CONFIG_AUTO_ZRELADDR=y
CONFIG_BCM2708_VCMEM=y
# CONFIG_BCM2711_THERMAL is not set
-CONFIG_BCM2835_DEVGPIOMEM=y
CONFIG_BCM2835_FAST_MEMCPY=y
CONFIG_BCM2835_MBOX=y
CONFIG_BCM2835_POWER=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
CONFIG_FB_SIMPLE=y
CONFIG_FIQ=y
CONFIG_FIXED_PHY=y
CONFIG_MEMFD_CREATE=y
CONFIG_MEMORY_ISOLATION=y
CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
CONFIG_MFD_SYSCON=y
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
CONFIG_MIGRATION=y
CONFIG_NO_HZ_COMMON=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_CONFIGFS=y
CONFIG_PM_GENERIC_DOMAINS_SLEEP=y
CONFIG_PM_OPP=y
CONFIG_PM_SLEEP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
CONFIG_POWER_SUPPLY=y
CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_PRINTK_TIME=y
CONFIG_PWM_SYSFS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
CONFIG_RASPBERRYPI_POWER=y
CONFIG_RATIONAL=y
# CONFIG_RAVE_SP_CORE is not set
CONFIG_AUTO_ZRELADDR=y
CONFIG_BCM2708_VCMEM=y
CONFIG_BCM2711_THERMAL=y
-CONFIG_BCM2835_DEVGPIOMEM=y
CONFIG_BCM2835_MBOX=y
CONFIG_BCM2835_POWER=y
# CONFIG_BCM2835_SMI is not set
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
CONFIG_FB_SIMPLE=y
CONFIG_FIQ=y
CONFIG_FIXED_PHY=y
CONFIG_MEMFD_CREATE=y
CONFIG_MEMORY_ISOLATION=y
CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
CONFIG_MFD_SYSCON=y
CONFIG_MICROCHIP_PHY=y
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_CONFIGFS=y
CONFIG_PM_OPP=y
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
CONFIG_POWER_SUPPLY=y
CONFIG_PPS=y
CONFIG_PREEMPT_NONE_BUILD=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RAS=y
CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
CONFIG_RASPBERRYPI_POWER=y
CONFIG_RATIONAL=y
# CONFIG_RAVE_SP_CORE is not set
CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
CONFIG_BCM2708_VCMEM=y
# CONFIG_BCM2711_THERMAL is not set
-CONFIG_BCM2835_DEVGPIOMEM=y
CONFIG_BCM2835_MBOX=y
CONFIG_BCM2835_POWER=y
# CONFIG_BCM2835_SMI is not set
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_SHA256=y
CONFIG_CRYPTO_LIB_UTILS=y
-# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_RNG_DEFAULT=y
CONFIG_CRYPTO_SHA256_ARM64=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_SHA512_ARM64=y
-# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set
-# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set
CONFIG_CRYPTO_XTS=y
CONFIG_DCACHE_WORD_ACCESS=y
CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
CONFIG_FB_SIMPLE=y
CONFIG_FIXED_PHY=y
CONFIG_FIX_EARLYCON_MEM=y
CONFIG_MEMFD_CREATE=y
CONFIG_MEMORY_ISOLATION=y
CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
CONFIG_MFD_SYSCON=y
CONFIG_MICROCHIP_PHY=y
CONFIG_MIGRATION=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_CONFIGFS=y
CONFIG_PM_OPP=y
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
CONFIG_POWER_RESET=y
CONFIG_POWER_SUPPLY=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
CONFIG_RASPBERRYPI_POWER=y
CONFIG_RATIONAL=y
# CONFIG_RAVE_SP_CORE is not set
CONFIG_AUDIT_ARCH_COMPAT_GENERIC=y
CONFIG_BCM2708_VCMEM=y
CONFIG_BCM2711_THERMAL=y
-CONFIG_BCM2835_DEVGPIOMEM=y
CONFIG_BCM2835_MBOX=y
CONFIG_BCM2835_POWER=y
# CONFIG_BCM2835_SMI is not set
CONFIG_CRYPTO_LIB_SHA1=y
CONFIG_CRYPTO_LIB_SHA256=y
CONFIG_CRYPTO_LIB_UTILS=y
-# CONFIG_CRYPTO_POLYVAL_ARM64_CE is not set
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
CONFIG_CRYPTO_RNG_DEFAULT=y
CONFIG_CRYPTO_SHA256_ARM64=y
CONFIG_CRYPTO_SHA512=y
CONFIG_CRYPTO_SHA512_ARM64=y
-# CONFIG_CRYPTO_SM4_ARM64_CE_BLK is not set
-# CONFIG_CRYPTO_SM4_ARM64_NEON_BLK is not set
CONFIG_CRYPTO_XTS=y
CONFIG_DCACHE_WORD_ACCESS=y
CONFIG_DEBUG_BUGVERBOSE=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FB_CMDLINE=y
-# CONFIG_FB_RPISENSE is not set
CONFIG_FB_SIMPLE=y
CONFIG_FIXED_PHY=y
CONFIG_FIX_EARLYCON_MEM=y
CONFIG_MEMFD_CREATE=y
CONFIG_MEMORY_ISOLATION=y
CONFIG_MFD_CORE=y
-# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
-# CONFIG_MFD_RPISENSE_CORE is not set
CONFIG_MFD_SYSCON=y
CONFIG_MIGRATION=y
CONFIG_MMC=y
CONFIG_NO_HZ_IDLE=y
CONFIG_NR_CPUS=4
CONFIG_NVMEM=y
+CONFIG_NVMEM_LAYOUTS=y
CONFIG_OF=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_CONFIGFS=y
CONFIG_PM_OPP=y
CONFIG_PM_SLEEP=y
CONFIG_PM_SLEEP_SMP=y
-# CONFIG_PM_USERSPACE_AUTOSLEEP is not set
CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
CONFIG_POWER_RESET=y
CONFIG_POWER_SUPPLY=y
CONFIG_RANDSTRUCT_NONE=y
CONFIG_RAS=y
CONFIG_RASPBERRYPI_FIRMWARE=y
+CONFIG_RASPBERRYPI_GPIOMEM=y
CONFIG_RASPBERRYPI_POWER=y
CONFIG_RATIONAL=y
# CONFIG_RAVE_SP_CORE is not set
+++ /dev/null
-From b642f64d629df5515f3a01fc5b2e17c3fa7b404c Mon Sep 17 00:00:00 2001
-From: Stefan Wahren <wahrenst@gmx.net>
-Date: Sat, 4 May 2019 17:06:15 +0200
-Subject: [PATCH] hwrng: iproc-rng200: Add BCM2838 support
-
-The HWRNG on the BCM2838 is compatible to iproc-rng200, so add the
-support to this driver instead of bcm2835-rng.
-
-Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
-
-hwrng: iproc-rng200: Correct SoC name
-
-The Pi 4 SoC is called BCM2711, not BCM2838.
-
-Fixes: "hwrng: iproc-rng200: Add BCM2838 support"
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- drivers/char/hw_random/Kconfig | 2 +-
- drivers/char/hw_random/iproc-rng200.c | 78 +++++++++++++++++++++++++--
- 2 files changed, 76 insertions(+), 4 deletions(-)
-
---- a/drivers/char/hw_random/Kconfig
-+++ b/drivers/char/hw_random/Kconfig
-@@ -104,7 +104,7 @@ config HW_RANDOM_IPROC_RNG200
- default HW_RANDOM
- help
- This driver provides kernel-side support for the RNG200
-- hardware found on the Broadcom iProc and STB SoCs.
-+ hardware found on the Broadcom iProc, BCM2711 and STB SoCs.
-
- To compile this driver as a module, choose M here: the
- module will be called iproc-rng200
---- a/drivers/char/hw_random/iproc-rng200.c
-+++ b/drivers/char/hw_random/iproc-rng200.c
-@@ -21,6 +21,7 @@
- #define RNG_CTRL_OFFSET 0x00
- #define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF
- #define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001
-+#define RNG_CTRL_RNG_DIV_CTRL_SHIFT 13
-
- #define RNG_SOFT_RESET_OFFSET 0x04
- #define RNG_SOFT_RESET 0x00000001
-@@ -28,16 +29,23 @@
- #define RBG_SOFT_RESET_OFFSET 0x08
- #define RBG_SOFT_RESET 0x00000001
-
-+#define RNG_TOTAL_BIT_COUNT_OFFSET 0x0C
-+
-+#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET 0x10
-+
- #define RNG_INT_STATUS_OFFSET 0x18
- #define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000
- #define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000
- #define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020
- #define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001
-
-+#define RNG_INT_ENABLE_OFFSET 0x1C
-+
- #define RNG_FIFO_DATA_OFFSET 0x20
-
- #define RNG_FIFO_COUNT_OFFSET 0x24
- #define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF
-+#define RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT 8
-
- struct iproc_rng200_dev {
- struct hwrng rng;
-@@ -158,6 +166,64 @@ static int iproc_rng200_init(struct hwrn
- return 0;
- }
-
-+static int bcm2711_rng200_read(struct hwrng *rng, void *buf, size_t max,
-+ bool wait)
-+{
-+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
-+ u32 max_words = max / sizeof(u32);
-+ u32 num_words, count, val;
-+
-+ /* ensure warm up period has elapsed */
-+ while (1) {
-+ val = ioread32(priv->base + RNG_TOTAL_BIT_COUNT_OFFSET);
-+ if (val > 16)
-+ break;
-+ cpu_relax();
-+ }
-+
-+ /* ensure fifo is not empty */
-+ while (1) {
-+ num_words = ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
-+ RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK;
-+ if (num_words)
-+ break;
-+ if (!wait)
-+ return 0;
-+ cpu_relax();
-+ }
-+
-+ if (num_words > max_words)
-+ num_words = max_words;
-+
-+ for (count = 0; count < num_words; count++) {
-+ ((u32 *)buf)[count] = ioread32(priv->base +
-+ RNG_FIFO_DATA_OFFSET);
-+ }
-+
-+ return num_words * sizeof(u32);
-+}
-+
-+static int bcm2711_rng200_init(struct hwrng *rng)
-+{
-+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
-+ uint32_t val;
-+
-+ if (ioread32(priv->base + RNG_CTRL_OFFSET) & RNG_CTRL_RNG_RBGEN_MASK)
-+ return 0;
-+
-+ /* initial numbers generated are "less random" so will be discarded */
-+ val = 0x40000;
-+ iowrite32(val, priv->base + RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET);
-+ /* min fifo count to generate full interrupt */
-+ val = 2 << RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT;
-+ iowrite32(val, priv->base + RNG_FIFO_COUNT_OFFSET);
-+ /* enable the rng - 1Mhz sample rate */
-+ val = (0x3 << RNG_CTRL_RNG_DIV_CTRL_SHIFT) | RNG_CTRL_RNG_RBGEN_MASK;
-+ iowrite32(val, priv->base + RNG_CTRL_OFFSET);
-+
-+ return 0;
-+}
-+
- static void iproc_rng200_cleanup(struct hwrng *rng)
- {
- struct iproc_rng200_dev *priv = to_rng_priv(rng);
-@@ -184,11 +250,17 @@ static int iproc_rng200_probe(struct pla
-
- dev_set_drvdata(dev, priv);
-
-- priv->rng.name = "iproc-rng200";
-- priv->rng.read = iproc_rng200_read;
-- priv->rng.init = iproc_rng200_init;
-+ priv->rng.name = pdev->name;
- priv->rng.cleanup = iproc_rng200_cleanup;
-
-+ if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-rng200")) {
-+ priv->rng.init = bcm2711_rng200_init;
-+ priv->rng.read = bcm2711_rng200_read;
-+ } else {
-+ priv->rng.init = iproc_rng200_init;
-+ priv->rng.read = iproc_rng200_read;
-+ }
-+
- /* Register driver */
- ret = devm_hwrng_register(dev, &priv->rng);
- if (ret) {
+++ /dev/null
-From dffb648dffeab7246040a30b7d1669387d1e767e Mon Sep 17 00:00:00 2001
-From: Phil Elwell <phil@raspberrypi.com>
-Date: Tue, 25 Apr 2023 11:49:41 +0100
-Subject: [PATCH] Bluetooth: hci_sync: Add fallback-bd-address prop
-
-The kernel Bluetooth framework understands that devices may not
-be programmed with valid Bluetooth addresses. It also has the ability
-to override a Bluetooth address with the value of the local-bd-address
-DT property, but it ignores the validity of the existing address when
-doing so.
-
-Add a new boolean property, fallback-bd-address, which indicates that
-the given local-bd-address property should only be used if the device
-does not already have a valid BDADDR.
-
-Signed-off-by: Phil Elwell <phil@raspberrypi.com>
----
- net/bluetooth/hci_sync.c | 5 ++++-
- 1 file changed, 4 insertions(+), 1 deletion(-)
-
---- a/net/bluetooth/hci_sync.c
-+++ b/net/bluetooth/hci_sync.c
-@@ -4630,6 +4630,7 @@ static const struct {
- */
- static int hci_dev_setup_sync(struct hci_dev *hdev)
- {
-+ struct fwnode_handle *fwnode = dev_fwnode(hdev->dev.parent);
- int ret = 0;
- bool invalid_bdaddr;
- size_t i;
-@@ -4658,7 +4659,9 @@ static int hci_dev_setup_sync(struct hci
-
- if (!ret) {
- if (test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks) &&
-- !bacmp(&hdev->public_addr, BDADDR_ANY))
-+ !bacmp(&hdev->public_addr, BDADDR_ANY) &&
-+ (invalid_bdaddr ||
-+ !fwnode_property_present(fwnode, "fallback-bd-address")))
- hci_dev_get_bd_addr_from_property(hdev);
-
- if ((invalid_bdaddr ||
--- /dev/null
+From 3ad8e28669e0058e3cec482a47215e50e33f2574 Mon Sep 17 00:00:00 2001
+From: Vinay Varma <varmavinaym@gmail.com>
+Date: Sun, 11 Jun 2023 23:45:03 +0800
+Subject: [PATCH] media: i2c: imx219: fix binning and rate_factor for 480p and
+ 1232p
+
+At a high FPS with RAW10, there is frame corruption for 480p because the
+rate_factor of 2 is used with the normal 2x2 bining [1]. This commit
+ties the rate_factor to the selected binning mode. For the 480p mode,
+analog 2x2 binning mode with a rate_factor of 2 is always used. For the
+1232p mode the normal 2x2 binning mode is used for RAW10 while analog
+2x2 binning mode is used for RAW8.
+
+[1] https://github.com/raspberrypi/linux/issues/5493
+
+Signed-off-by: Vinay Varma <varmavinaym@gmail.com>
+---
+ drivers/media/i2c/imx219.c | 143 ++++++++++++++++++++++++++-----------
+ 1 file changed, 100 insertions(+), 43 deletions(-)
+
+--- a/drivers/media/i2c/imx219.c
++++ b/drivers/media/i2c/imx219.c
+@@ -136,6 +136,18 @@ enum pad_types {
+ NUM_PADS
+ };
+
++enum binning_mode {
++ BINNING_NONE,
++ BINNING_DIGITAL_2x2,
++ BINNING_ANALOG_2x2,
++};
++
++enum binning_bit_depths {
++ BINNING_IDX_8_BIT,
++ BINNING_IDX_10_BIT,
++ BINNING_IDX_MAX
++};
++
+ struct imx219_reg {
+ u16 address;
+ u8 val;
+@@ -162,11 +174,8 @@ struct imx219_mode {
+ /* Default register values */
+ struct imx219_reg_list reg_list;
+
+- /* 2x2 binning is used */
+- bool binning;
+-
+- /* Relative pixel clock rate factor for the mode. */
+- unsigned int rate_factor;
++ /* binning mode based on format code */
++ enum binning_mode binning[BINNING_IDX_MAX];
+ };
+
+ static const struct imx219_reg imx219_common_regs[] = {
+@@ -404,8 +413,10 @@ static const struct imx219_mode supporte
+ .num_of_regs = ARRAY_SIZE(mode_3280x2464_regs),
+ .regs = mode_3280x2464_regs,
+ },
+- .binning = false,
+- .rate_factor = 1,
++ .binning = {
++ [BINNING_IDX_8_BIT] = BINNING_NONE,
++ [BINNING_IDX_10_BIT] = BINNING_NONE,
++ },
+ },
+ {
+ /* 1080P 30fps cropped */
+@@ -422,8 +433,10 @@ static const struct imx219_mode supporte
+ .num_of_regs = ARRAY_SIZE(mode_1920_1080_regs),
+ .regs = mode_1920_1080_regs,
+ },
+- .binning = false,
+- .rate_factor = 1,
++ .binning = {
++ [BINNING_IDX_8_BIT] = BINNING_NONE,
++ [BINNING_IDX_10_BIT] = BINNING_NONE,
++ },
+ },
+ {
+ /* 2x2 binned 30fps mode */
+@@ -440,8 +453,10 @@ static const struct imx219_mode supporte
+ .num_of_regs = ARRAY_SIZE(mode_1640_1232_regs),
+ .regs = mode_1640_1232_regs,
+ },
+- .binning = true,
+- .rate_factor = 1,
++ .binning = {
++ [BINNING_IDX_8_BIT] = BINNING_ANALOG_2x2,
++ [BINNING_IDX_10_BIT] = BINNING_DIGITAL_2x2,
++ },
+ },
+ {
+ /* 640x480 30fps mode */
+@@ -458,12 +473,10 @@ static const struct imx219_mode supporte
+ .num_of_regs = ARRAY_SIZE(mode_640_480_regs),
+ .regs = mode_640_480_regs,
+ },
+- .binning = true,
+- /*
+- * This mode uses a special 2x2 binning that doubles the
+- * internal pixel clock rate.
+- */
+- .rate_factor = 2,
++ .binning = {
++ [BINNING_IDX_8_BIT] = BINNING_ANALOG_2x2,
++ [BINNING_IDX_10_BIT] = BINNING_ANALOG_2x2,
++ },
+ },
+ };
+
+@@ -652,12 +665,51 @@ static int imx219_open(struct v4l2_subde
+ return 0;
+ }
+
++static int imx219_resolve_binning(struct imx219 *imx219,
++ enum binning_mode *binning)
++{
++ switch (imx219->fmt.code) {
++ case MEDIA_BUS_FMT_SRGGB8_1X8:
++ case MEDIA_BUS_FMT_SGRBG8_1X8:
++ case MEDIA_BUS_FMT_SGBRG8_1X8:
++ case MEDIA_BUS_FMT_SBGGR8_1X8:
++ *binning = imx219->mode->binning[BINNING_IDX_8_BIT];
++ return 0;
++
++ case MEDIA_BUS_FMT_SRGGB10_1X10:
++ case MEDIA_BUS_FMT_SGRBG10_1X10:
++ case MEDIA_BUS_FMT_SGBRG10_1X10:
++ case MEDIA_BUS_FMT_SBGGR10_1X10:
++ *binning = imx219->mode->binning[BINNING_IDX_10_BIT];
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static int imx219_get_rate_factor(struct imx219 *imx219)
++{
++ enum binning_mode binning = BINNING_NONE;
++ int ret = imx219_resolve_binning(imx219, &binning);
++
++ if (ret < 0)
++ return ret;
++ switch (binning) {
++ case BINNING_NONE:
++ case BINNING_DIGITAL_2x2:
++ return 1;
++ case BINNING_ANALOG_2x2:
++ return 2;
++ }
++ return -EINVAL;
++}
++
+ static int imx219_set_ctrl(struct v4l2_ctrl *ctrl)
+ {
+ struct imx219 *imx219 =
+ container_of(ctrl->handler, struct imx219, ctrl_handler);
+ struct i2c_client *client = v4l2_get_subdevdata(&imx219->sd);
+ int ret;
++ int rate_factor;
+
+ if (ctrl->id == V4L2_CID_VBLANK) {
+ int exposure_max, exposure_def;
+@@ -679,6 +731,10 @@ static int imx219_set_ctrl(struct v4l2_c
+ if (pm_runtime_get_if_in_use(&client->dev) == 0)
+ return 0;
+
++ rate_factor = imx219_get_rate_factor(imx219);
++ if (rate_factor < 0)
++ return rate_factor;
++
+ switch (ctrl->id) {
+ case V4L2_CID_ANALOGUE_GAIN:
+ ret = imx219_write_reg(imx219, IMX219_REG_ANALOG_GAIN,
+@@ -687,7 +743,7 @@ static int imx219_set_ctrl(struct v4l2_c
+ case V4L2_CID_EXPOSURE:
+ ret = imx219_write_reg(imx219, IMX219_REG_EXPOSURE,
+ IMX219_REG_VALUE_16BIT,
+- ctrl->val / imx219->mode->rate_factor);
++ ctrl->val / rate_factor);
+ break;
+ case V4L2_CID_DIGITAL_GAIN:
+ ret = imx219_write_reg(imx219, IMX219_REG_DIGITAL_GAIN,
+@@ -708,7 +764,7 @@ static int imx219_set_ctrl(struct v4l2_c
+ ret = imx219_write_reg(imx219, IMX219_REG_VTS,
+ IMX219_REG_VALUE_16BIT,
+ (imx219->mode->height + ctrl->val) /
+- imx219->mode->rate_factor);
++ rate_factor);
+ break;
+ case V4L2_CID_HBLANK:
+ ret = imx219_write_reg(imx219, IMX219_REG_HTS,
+@@ -890,7 +946,7 @@ static int imx219_set_pad_format(struct
+ struct imx219 *imx219 = to_imx219(sd);
+ const struct imx219_mode *mode;
+ struct v4l2_mbus_framefmt *framefmt;
+- int exposure_max, exposure_def, hblank, pixel_rate;
++ int exposure_max, exposure_def, hblank, pixel_rate, rate_factor;
+ unsigned int i;
+
+ if (fmt->pad >= NUM_PADS)
+@@ -924,6 +980,9 @@ static int imx219_set_pad_format(struct
+
+ imx219->fmt = fmt->format;
+ imx219->mode = mode;
++ rate_factor = imx219_get_rate_factor(imx219);
++ if (rate_factor < 0)
++ return rate_factor;
+ /* Update limits and set FPS to default */
+ __v4l2_ctrl_modify_range(imx219->vblank,
+ IMX219_VBLANK_MIN,
+@@ -957,8 +1016,7 @@ static int imx219_set_pad_format(struct
+ __v4l2_ctrl_s_ctrl(imx219->hblank, hblank);
+
+ /* Scale the pixel rate based on the mode specific factor */
+- pixel_rate =
+- IMX219_PIXEL_RATE * imx219->mode->rate_factor;
++ pixel_rate = IMX219_PIXEL_RATE * rate_factor;
+ __v4l2_ctrl_modify_range(imx219->pixel_rate, pixel_rate,
+ pixel_rate, 1, pixel_rate);
+ }
+@@ -1001,30 +1059,25 @@ static int imx219_set_framefmt(struct im
+
+ static int imx219_set_binning(struct imx219 *imx219)
+ {
+- if (!imx219->mode->binning) {
++ enum binning_mode binning = BINNING_NONE;
++ int ret = imx219_resolve_binning(imx219, &binning);
++
++ if (ret < 0)
++ return ret;
++ switch (binning) {
++ case BINNING_NONE:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+ IMX219_BINNING_NONE);
+- }
+-
+- switch (imx219->fmt.code) {
+- case MEDIA_BUS_FMT_SRGGB8_1X8:
+- case MEDIA_BUS_FMT_SGRBG8_1X8:
+- case MEDIA_BUS_FMT_SGBRG8_1X8:
+- case MEDIA_BUS_FMT_SBGGR8_1X8:
++ case BINNING_DIGITAL_2x2:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+- IMX219_BINNING_2X2_ANALOG);
+-
+- case MEDIA_BUS_FMT_SRGGB10_1X10:
+- case MEDIA_BUS_FMT_SGRBG10_1X10:
+- case MEDIA_BUS_FMT_SGBRG10_1X10:
+- case MEDIA_BUS_FMT_SBGGR10_1X10:
++ IMX219_BINNING_2X2);
++ case BINNING_ANALOG_2x2:
+ return imx219_write_reg(imx219, IMX219_REG_BINNING_MODE,
+ IMX219_REG_VALUE_16BIT,
+- IMX219_BINNING_2X2);
++ IMX219_BINNING_2X2_ANALOG);
+ }
+-
+ return -EINVAL;
+ }
+
+@@ -1342,7 +1395,7 @@ static int imx219_init_controls(struct i
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ unsigned int height = imx219->mode->height;
+ struct v4l2_fwnode_device_properties props;
+- int exposure_max, exposure_def, hblank, pixel_rate;
++ int exposure_max, exposure_def, hblank, pixel_rate, rate_factor;
+ int i, ret;
+
+ ctrl_hdlr = &imx219->ctrl_handler;
+@@ -1353,8 +1406,12 @@ static int imx219_init_controls(struct i
+ mutex_init(&imx219->mutex);
+ ctrl_hdlr->lock = &imx219->mutex;
+
++ rate_factor = imx219_get_rate_factor(imx219);
++ if (rate_factor < 0)
++ return rate_factor;
++
+ /* By default, PIXEL_RATE is read only */
+- pixel_rate = IMX219_PIXEL_RATE * imx219->mode->rate_factor;
++ pixel_rate = IMX219_PIXEL_RATE * rate_factor;
+ imx219->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx219_ctrl_ops,
+ V4L2_CID_PIXEL_RATE,
+ pixel_rate, pixel_rate,
+@@ -1576,6 +1633,9 @@ static int imx219_probe(struct i2c_clien
+ goto error_power_off;
+ usleep_range(100, 110);
+
++ /* Initialize default format */
++ imx219_set_default_format(imx219);
++
+ ret = imx219_init_controls(imx219);
+ if (ret)
+ goto error_power_off;
+@@ -1590,9 +1650,6 @@ static int imx219_probe(struct i2c_clien
+ imx219->pad[IMAGE_PAD].flags = MEDIA_PAD_FL_SOURCE;
+ imx219->pad[METADATA_PAD].flags = MEDIA_PAD_FL_SOURCE;
+
+- /* Initialize default format */
+- imx219_set_default_format(imx219);
+-
+ ret = media_entity_pads_init(&imx219->sd.entity, NUM_PADS, imx219->pad);
+ if (ret) {
+ dev_err(dev, "failed to init entity pads: %d\n", ret);
--- /dev/null
+From 52039b6ffb6e78c2f77319b167dceab9aa51d13f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 13 Jun 2023 16:12:54 +0100
+Subject: [PATCH] serial: sc16is7xx: Read modem line state at startup
+
+This patch sets the driver modem line state to the actual line state
+at driver startup.
+
+See: https://github.com/raspberrypi/linux/issues/5501
+
+Signed-off-by: Earl Schmidt <schmidt.earl.f@gmail.com>
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/sc16is7xx.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/tty/serial/sc16is7xx.c
++++ b/drivers/tty/serial/sc16is7xx.c
+@@ -1221,6 +1221,9 @@ static int sc16is7xx_startup(struct uart
+ SC16IS7XX_IER_MSI_BIT;
+ sc16is7xx_port_write(port, SC16IS7XX_IER_REG, val);
+
++ /* Initialize the Modem Control signals to current status */
++ one->old_mctrl = sc16is7xx_get_hwmctrl(port);
++
+ /* Enable modem status polling */
+ spin_lock_irqsave(&port->lock, flags);
+ sc16is7xx_enable_ms(port);
--- /dev/null
+From 6ef818eed60db70e9caf6bdf74cc1f9943994226 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 16 Jun 2023 16:24:19 +0100
+Subject: [PATCH] drivers: media: bcm2835_unicam: Improve frame sequence count
+ handling
+
+Ensure that the frame sequence counter is incremented only if a previous
+frame start interrupt has occurred, or a frame start + frame end has
+occurred simultaneously.
+
+This corresponds the sequence number with the actual number of frames
+produced by the sensor, not the number of frame buffers dequeued back
+to userland.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/bcm2835/bcm2835-unicam.c | 19 ++++++++++++++++++-
+ 1 file changed, 18 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/bcm2835/bcm2835-unicam.c
++++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c
+@@ -522,6 +522,7 @@ struct unicam_device {
+ /* subdevice async Notifier */
+ struct v4l2_async_notifier notifier;
+ unsigned int sequence;
++ bool frame_started;
+
+ /* ptr to sub device */
+ struct v4l2_subdev *sensor;
+@@ -914,6 +915,8 @@ static irqreturn_t unicam_isr(int irq, v
+ * buffer forever.
+ */
+ if (fe) {
++ bool inc_seq = unicam->frame_started;
++
+ /*
+ * Ensure we have swapped buffers already as we can't
+ * stop the peripheral. If no buffer is available, use a
+@@ -949,11 +952,23 @@ static irqreturn_t unicam_isr(int irq, v
+ unicam_process_buffer_complete(node, sequence);
+ node->cur_frm = node->next_frm;
+ node->next_frm = NULL;
++ inc_seq = true;
+ } else {
+ node->cur_frm = node->next_frm;
+ }
+ }
+- unicam->sequence++;
++
++ /*
++ * Increment the sequence number conditionally on either a FS
++ * having already occurred, or in the FE + FS condition as
++ * caught in the FE handler above. This ensures the sequence
++ * number corresponds to the frames generated by the sensor, not
++ * the frames dequeued to userland.
++ */
++ if (inc_seq) {
++ unicam->sequence++;
++ unicam->frame_started = false;
++ }
+ }
+
+ if (ista & UNICAM_FSI) {
+@@ -996,6 +1011,7 @@ static irqreturn_t unicam_isr(int irq, v
+ }
+
+ unicam_queue_event_sof(unicam);
++ unicam->frame_started = true;
+ }
+
+ /*
+@@ -2600,6 +2616,7 @@ static int unicam_start_streaming(struct
+ vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+ }
+
++ dev->frame_started = false;
+ unicam_start_rx(dev, buffer_addr);
+
+ ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1);
--- /dev/null
+From bd8e59b0456870997fb917bcd3b3b696e78d4ac2 Mon Sep 17 00:00:00 2001
+From: 6by9 <6by9@users.noreply.github.com>
+Date: Mon, 19 Jun 2023 16:02:36 +0100
+Subject: [PATCH] dtoverlays: Fix pitft[28|35] overlays for 6.1 driver change.
+ (#5508)
+
+The overlays configured both irq-gpio and an interrupts/
+interrupt-parent configuration for the stmpe MFD device.
+
+irq-gpio was reworked in 6.1 and has issues with the configuration
+as provided. Removing it and using the interrupts/interrupt-parent
+configuration works fine, so do that.
+
+See: https://forums.raspberrypi.com/viewtopic.php?t=351394
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts | 1 -
+ arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts | 1 -
+ 2 files changed, 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pitft28-resistive-overlay.dts
+@@ -68,7 +68,6 @@
+ reg = <1>;
+
+ spi-max-frequency = <500000>;
+- irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */
+ interrupts = <24 2>; /* high-to-low edge triggered */
+ interrupt-parent = <&gpio>;
+ interrupt-controller;
+--- a/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pitft35-resistive-overlay.dts
+@@ -68,7 +68,6 @@
+ reg = <1>;
+
+ spi-max-frequency = <500000>;
+- irq-gpio = <&gpio 24 0x2>; /* IRQF_TRIGGER_FALLING */
+ interrupts = <24 2>; /* high-to-low edge triggered */
+ interrupt-parent = <&gpio>;
+ interrupt-controller;
--- /dev/null
+From 713a7ef9d73fca0f7fed122cb854d930b7a6ba5a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 21 Jun 2023 08:45:02 +0100
+Subject: [PATCH] driver: media: i2c: imx477: Re-enable temperature sensor
+
+The temperature sensor enable register write got lost at some point.
+Re-enable it.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -167,6 +167,7 @@ struct imx477_mode {
+ static const struct imx477_reg mode_common_regs[] = {
+ {0x0136, 0x18},
+ {0x0137, 0x00},
++ {0x0138, 0x01},
+ {0xe000, 0x00},
+ {0xe07a, 0x01},
+ {0x0808, 0x02},
--- /dev/null
+From d4c3133378b377ee519ea50247339cd61221fc47 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 21 Jun 2023 09:20:36 +0100
+Subject: [PATCH] overlays: allo-katana-dac-audio: Reduce I2C clock
+
+Higher speeds have been shown to cause data corruption on a Pi 4,
+possibly due to clock-stretching.
+
+See: https://github.com/raspberrypi/linux/issues/5511
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
+@@ -30,6 +30,7 @@
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
++ clock-frequency = <50000>;
+
+ allo-katana-codec@30 {
+ #sound-dai-cells = <0>;
--- /dev/null
+From 76c457e7e2920342637b1955fbaadf2aae282f05 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 23 Jun 2023 09:48:59 +0100
+Subject: [PATCH] overlays: jedec-spi-nor: Add speed parameter
+
+Add a speed parameter to the jedec-spi-nor overlay to allow much
+faster accesses, taking the opportunity to simplify the internals.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 8 +-
+ .../dts/overlays/jedec-spi-nor-overlay.dts | 245 +++---------------
+ 2 files changed, 41 insertions(+), 212 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2557,9 +2557,11 @@ Name: jedec-spi-nor
+ Info: Adds support for JEDEC-compliant SPI NOR flash devices. (Note: The
+ "jedec,spi-nor" kernel driver was formerly known as "m25p80".)
+ Load: dtoverlay=jedec-spi-nor,<param>=<val>
+-Params: flash-spi<n>-<m> Enables flash device on SPI<n>, CS#<m>.
+- flash-fastr-spi<n>-<m> Enables flash device with fast read capability
+- on SPI<n>, CS#<m>.
++Params: spi<n>-<m> Enable flash device on SPI<n>, CS#<m>
++ fastr Add fast read capability to the flash device
++ speed Maximum SPI frequency (Hz)
++ flash-spi<n>-<m> Same as spi<n>-<m> (deprecated)
++ flash-fastr-spi<n>-<m> Same as spi<n>->m>,fastr (deprecated)
+
+
+ Name: justboom-both
+--- a/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
++++ b/arch/arm/boot/dts/overlays/jedec-spi-nor-overlay.dts
+@@ -3,6 +3,7 @@
+ // dtparams:
+ // flash-spi<n>-<m> - Enables flash device on SPI<n>, CS#<m>.
+ // flash-fastr-spi<n>-<m> - Enables flash device with fast read capability on SPI<n>, CS#<m>.
++// speed - Set the SPI clock speed in Hz
+ //
+ // If devices are present on SPI1 or SPI2, those interfaces must be enabled with one of the spi1-1/2/3cs and/or spi2-1/2/3cs overlays.
+ //
+@@ -79,50 +80,23 @@
+ };
+ };
+
+- // enable flash on spi0.0
++ // Enable fast read for device
++ // Use default active low interrupt signalling.
+ fragment@8 {
+- target = <&spi0>;
++ target = <&spi_nor>;
+ __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_00: spi_nor@0 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <0>;
+- spi-max-frequency = <500000>;
+- };
++ m25p,fast-read;
+ };
+ };
+
+- // enable flash on spi0.1
+- fragment@9 {
++ payload: fragment@100 {
+ target = <&spi0>;
+- __dormant__ {
++ __overlay__ {
+ status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_01: spi_nor@1 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <1>;
+- spi-max-frequency = <500000>;
+- };
+- };
+- };
++ #address-cells = <1>;
++ #size-cells = <0>;
+
+- // enable flash on spi1.0
+- fragment@10 {
+- target = <&spi1>;
+- __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_10: spi_nor@0 {
+- #address-cells = <1>;
+- #size-cells = <1>;
++ spi_nor: spi_nor@0 {
+ compatible = "jedec,spi-nor";
+ reg = <0>;
+ spi-max-frequency = <500000>;
+@@ -130,180 +104,33 @@
+ };
+ };
+
+- // enable flash on spi1.1
+- fragment@11 {
+- target = <&spi1>;
+- __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_11: spi_nor@1 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <1>;
+- spi-max-frequency = <500000>;
+- };
+- };
+- };
+-
+- // enable flash on spi1.2
+- fragment@12 {
+- target = <&spi1>;
+- __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_12: spi_nor@2 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <2>;
+- spi-max-frequency = <500000>;
+- };
+- };
+- };
+-
+- // enable flash on spi2.0
+- fragment@13 {
+- target = <&spi2>;
+- __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_20: spi_nor@0 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <0>;
+- spi-max-frequency = <500000>;
+- };
+- };
+- };
+-
+- // enable flash on spi2.1
+- fragment@14 {
+- target = <&spi2>;
+- __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_21: spi_nor@1 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <1>;
+- spi-max-frequency = <500000>;
+- };
+- };
+- };
+-
+- // enable flash on spi2.2
+- fragment@15 {
+- target = <&spi2>;
+- __dormant__ {
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- spi_nor_22: spi_nor@2 {
+- #address-cells = <1>;
+- #size-cells = <1>;
+- compatible = "jedec,spi-nor";
+- reg = <2>;
+- spi-max-frequency = <500000>;
+- };
+- };
+- };
+-
+- // Enable fast read for device on spi0.0.
+- // Use default active low interrupt signalling.
+- fragment@16 {
+- target = <&spi_nor_00>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi0.1.
+- // Use default active low interrupt signalling.
+- fragment@17 {
+- target = <&spi_nor_01>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi1.0.
+- // Use default active low interrupt signalling.
+- fragment@18 {
+- target = <&spi_nor_10>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi1.1.
+- // Use default active low interrupt signalling.
+- fragment@19 {
+- target = <&spi_nor_11>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi1.2.
+- // Use default active low interrupt signalling.
+- fragment@20 {
+- target = <&spi_nor_12>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi2.0.
+- // Use default active low interrupt signalling.
+- fragment@21 {
+- target = <&spi_nor_20>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi2.1.
+- // Use default active low interrupt signalling.
+- fragment@22 {
+- target = <&spi_nor_21>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+- // Enable fast read for device on spi2.2.
+- // Use default active low interrupt signalling.
+- fragment@23 {
+- target = <&spi_nor_22>;
+- __dormant__ {
+- m25p,fast-read;
+- };
+- };
+-
+ __overrides__ {
+- flash-spi0-0 = <0>,"+0+8";
+- flash-spi0-1 = <0>,"+1+9";
+- flash-spi1-0 = <0>,"+2+10";
+- flash-spi1-1 = <0>,"+3+11";
+- flash-spi1-2 = <0>,"+4+12";
+- flash-spi2-0 = <0>,"+5+13";
+- flash-spi2-1 = <0>,"+6+14";
+- flash-spi2-2 = <0>,"+7+15";
+- flash-fastr-spi0-0 = <0>,"+0+8+16";
+- flash-fastr-spi0-1 = <0>,"+1+9+17";
+- flash-fastr-spi1-0 = <0>,"+2+10+18";
+- flash-fastr-spi1-1 = <0>,"+3+11+19";
+- flash-fastr-spi1-2 = <0>,"+4+12+20";
+- flash-fastr-spi2-0 = <0>,"+5+13+21";
+- flash-fastr-spi2-1 = <0>,"+6+14+22";
+- flash-fastr-spi2-2 = <0>,"+7+15+23";
++ spi0-0 = <0>,"+0", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=0";
++ spi0-1 = <0>,"+1", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=1";
++ spi1-0 = <0>,"+2", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=0";
++ spi1-1 = <0>,"+3", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=1";
++ spi1-2 = <0>,"+4", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=2";
++ spi2-0 = <0>,"+5", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=0";
++ spi2-1 = <0>,"+6", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=1";
++ spi2-2 = <0>,"+7", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
++ flash-spi0-0 = <0>,"+0", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=0";
++ flash-spi0-1 = <0>,"+1", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=1";
++ flash-spi1-0 = <0>,"+2", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=0";
++ flash-spi1-1 = <0>,"+3", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=1";
++ flash-spi1-2 = <0>,"+4", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=2";
++ flash-spi2-0 = <0>,"+5", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=0";
++ flash-spi2-1 = <0>,"+6", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=1";
++ flash-spi2-2 = <0>,"+7", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
++ flash-fastr-spi0-0 = <0>,"+0+8", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=0";
++ flash-fastr-spi0-1 = <0>,"+1+8", <&payload>,"target:0=",<&spi0>, <&spi_nor>,"reg:0=1";
++ flash-fastr-spi1-0 = <0>,"+2+8", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=0";
++ flash-fastr-spi1-1 = <0>,"+3+8", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=1";
++ flash-fastr-spi1-2 = <0>,"+4+8", <&payload>,"target:0=",<&spi1>, <&spi_nor>,"reg:0=2";
++ flash-fastr-spi2-0 = <0>,"+5+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=0";
++ flash-fastr-spi2-1 = <0>,"+6+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=1";
++ flash-fastr-spi2-2 = <0>,"+7+8", <&payload>,"target:0=",<&spi2>, <&spi_nor>,"reg:0=2";
++ fastr = <0>,"+8";
++ speed = <&spi_nor>, "spi-max-frequency:0";
+ };
+ };
+
--- /dev/null
+From e866f9fc7c6dd6af1e74ce6fa50db9ba21acae5e Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Sat, 24 Jun 2023 18:52:16 +0200
+Subject: [PATCH] ALSA: pcm: fix ELD constraints for (E)AC3, DTS(-HD) and MLP
+ formats
+
+commit 04b49b90caeed0b5544ff616d654900d27d403b6 upstream.
+
+The SADs of compressed formats contain the channel and sample rate
+info of the audio data inside the compressed stream, but when
+building constraints we must use the rates and channels used to
+transport the compressed streams.
+
+eg 48kHz 6ch EAC3 needs to be transmitted as a 2ch 192kHz stream.
+
+This patch fixes the constraints for the common AC3 and DTS formats,
+the constraints for the less common MPEG, DSD etc formats are copied
+directly from the info in the SADs as before as I don't have the specs
+and equipment to test those.
+
+Signed-off-by: Matthias Reichl <hias@horus.com>
+Link: https://lore.kernel.org/r/20230624165216.5719-1-hias@horus.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+---
+ sound/core/pcm_drm_eld.c | 73 ++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 70 insertions(+), 3 deletions(-)
+
+--- a/sound/core/pcm_drm_eld.c
++++ b/sound/core/pcm_drm_eld.c
+@@ -2,11 +2,25 @@
+ /*
+ * PCM DRM helpers
+ */
++#include <linux/bitfield.h>
+ #include <linux/export.h>
++#include <linux/hdmi.h>
+ #include <drm/drm_edid.h>
+ #include <sound/pcm.h>
+ #include <sound/pcm_drm_eld.h>
+
++#define SAD0_CHANNELS_MASK GENMASK(2, 0) /* max number of channels - 1 */
++#define SAD0_FORMAT_MASK GENMASK(6, 3) /* audio format */
++
++#define SAD1_RATE_MASK GENMASK(6, 0) /* bitfield of supported rates */
++#define SAD1_RATE_32000_MASK BIT(0)
++#define SAD1_RATE_44100_MASK BIT(1)
++#define SAD1_RATE_48000_MASK BIT(2)
++#define SAD1_RATE_88200_MASK BIT(3)
++#define SAD1_RATE_96000_MASK BIT(4)
++#define SAD1_RATE_176400_MASK BIT(5)
++#define SAD1_RATE_192000_MASK BIT(6)
++
+ static const unsigned int eld_rates[] = {
+ 32000,
+ 44100,
+@@ -17,9 +31,62 @@ static const unsigned int eld_rates[] =
+ 192000,
+ };
+
++static unsigned int map_rate_families(const u8 *sad,
++ unsigned int mask_32000,
++ unsigned int mask_44100,
++ unsigned int mask_48000)
++{
++ unsigned int rate_mask = 0;
++
++ if (sad[1] & SAD1_RATE_32000_MASK)
++ rate_mask |= mask_32000;
++ if (sad[1] & (SAD1_RATE_44100_MASK | SAD1_RATE_88200_MASK | SAD1_RATE_176400_MASK))
++ rate_mask |= mask_44100;
++ if (sad[1] & (SAD1_RATE_48000_MASK | SAD1_RATE_96000_MASK | SAD1_RATE_192000_MASK))
++ rate_mask |= mask_48000;
++ return rate_mask;
++}
++
++static unsigned int sad_rate_mask(const u8 *sad)
++{
++ switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
++ case HDMI_AUDIO_CODING_TYPE_PCM:
++ return sad[1] & SAD1_RATE_MASK;
++ case HDMI_AUDIO_CODING_TYPE_AC3:
++ case HDMI_AUDIO_CODING_TYPE_DTS:
++ return map_rate_families(sad,
++ SAD1_RATE_32000_MASK,
++ SAD1_RATE_44100_MASK,
++ SAD1_RATE_48000_MASK);
++ case HDMI_AUDIO_CODING_TYPE_EAC3:
++ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
++ case HDMI_AUDIO_CODING_TYPE_MLP:
++ return map_rate_families(sad,
++ 0,
++ SAD1_RATE_176400_MASK,
++ SAD1_RATE_192000_MASK);
++ default:
++ /* TODO adjust for other compressed formats as well */
++ return sad[1] & SAD1_RATE_MASK;
++ }
++}
++
+ static unsigned int sad_max_channels(const u8 *sad)
+ {
+- return 1 + (sad[0] & 7);
++ switch (FIELD_GET(SAD0_FORMAT_MASK, sad[0])) {
++ case HDMI_AUDIO_CODING_TYPE_PCM:
++ return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
++ case HDMI_AUDIO_CODING_TYPE_AC3:
++ case HDMI_AUDIO_CODING_TYPE_DTS:
++ case HDMI_AUDIO_CODING_TYPE_EAC3:
++ return 2;
++ case HDMI_AUDIO_CODING_TYPE_DTS_HD:
++ case HDMI_AUDIO_CODING_TYPE_MLP:
++ return 8;
++ default:
++ /* TODO adjust for other compressed formats as well */
++ return 1 + FIELD_GET(SAD0_CHANNELS_MASK, sad[0]);
++ }
+ }
+
+ static int eld_limit_rates(struct snd_pcm_hw_params *params,
+@@ -42,7 +109,7 @@ static int eld_limit_rates(struct snd_pc
+ * requested number of channels.
+ */
+ if (c->min <= max_channels)
+- rate_mask |= sad[1];
++ rate_mask |= sad_rate_mask(sad);
+ }
+ }
+
+@@ -70,7 +137,7 @@ static int eld_limit_channels(struct snd
+ rate_mask |= BIT(i);
+
+ for (i = drm_eld_sad_count(eld); i > 0; i--, sad += 3)
+- if (rate_mask & sad[1])
++ if (rate_mask & sad_rate_mask(sad))
+ t.max = max(t.max, sad_max_channels(sad));
+ }
+
--- /dev/null
+From 3f388718331b5ce2acd34730448db001759868aa Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Sat, 24 Jun 2023 18:52:32 +0200
+Subject: [PATCH] ASoC: hdmi-codec: fix channel info for compressed formats
+
+commit 4e0871333661d2ec0ed3dc00a945c2160eccae77 upstream.
+
+According to CTA 861 the channel/speaker allocation info in the
+audio infoframe only applies to uncompressed (PCM) audio streams.
+
+The channel count info should indicate the number of channels
+in the transmitted audio, which usually won't match the number of
+channels used to transmit the compressed bitstream.
+
+Some devices (eg some Sony TVs) will refuse to decode compressed
+audio if these values are not set correctly.
+
+To fix this we can simply set the channel count to 0 (which means
+"refer to stream header") and set the channel/speaker allocation to 0
+as well (which would mean stereo FL/FR for PCM, a safe value all sinks
+will support) when transmitting compressed audio.
+
+Signed-off-by: Matthias Reichl <hias@horus.com>
+Link: https://lore.kernel.org/r/20230624165232.5751-1-hias@horus.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+---
+ sound/soc/codecs/hdmi-codec.c | 36 +++++++++++++++++++++++------------
+ 1 file changed, 24 insertions(+), 12 deletions(-)
+
+--- a/sound/soc/codecs/hdmi-codec.c
++++ b/sound/soc/codecs/hdmi-codec.c
+@@ -484,31 +484,43 @@ static int hdmi_codec_fill_codec_params(
+ struct hdmi_codec_params *hp)
+ {
+ struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
+- int idx;
++ int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
++ u8 ca_id = 0;
++ bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO);
++
++ if (pcm_audio) {
++ /* Select a channel allocation that matches with ELD and pcm channels */
++ idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
++
++ if (idx < 0) {
++ dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
++ idx);
++ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
++ return idx;
++ }
+
+- /* Select a channel allocation that matches with ELD and pcm channels */
+- idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels);
+- if (idx < 0) {
+- dev_err(dai->dev, "Not able to map channels to speakers (%d)\n",
+- idx);
+- hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+- return idx;
++ ca_id = hdmi_codec_channel_alloc[idx].ca_id;
+ }
+
+ memset(hp, 0, sizeof(*hp));
+
+ hdmi_audio_infoframe_init(&hp->cea);
+- hp->cea.channels = channels;
++
++ if (pcm_audio)
++ hp->cea.channels = channels;
++ else
++ hp->cea.channels = 0;
++
+ hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
+ hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
+ hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
+- hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id;
++ hp->cea.channel_allocation = ca_id;
+
+ hp->sample_width = sample_width;
+ hp->sample_rate = sample_rate;
+ hp->channels = channels;
+
+- hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id;
++ hcp->chmap_idx = idx;
+
+ return 0;
+ }
--- /dev/null
+From 9c5a7f04cab6b020389d7c5af155b1ee7f46537d Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Thu, 4 May 2023 11:14:04 +0800
+Subject: [PATCH] media: i2c: arducam_64mp: Modify the line length of 1280x720
+ resolution
+
+Arducam 64MP has specific requirements for the line length, and if these
+conditions are not met, the camera will not function properly. Under the
+previous configuration, once a stream off operation is performed, the
+camera will not output any data, even if a stream on operation is
+performed. This prevents us from switching from 1280x720 to another
+resolution.
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/arducam_64mp.c | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -1063,10 +1063,10 @@ static const struct arducam_64mp_reg mod
+
+ /* 720p 120fps mode */
+ static const struct arducam_64mp_reg mode_1280x720_regs[] = {
+- {0x0342, 0x1d},
+- {0x0343, 0xc4},
+- {0x0340, 0x03},
+- {0x0341, 0xd8},
++ {0x0342, 0x1b},
++ {0x0343, 0x08},
++ {0x0340, 0x04},
++ {0x0341, 0x3b},
+ {0x0344, 0x08},
+ {0x0345, 0x10},
+ {0x0346, 0x07},
+@@ -1209,7 +1209,7 @@ static const struct arducam_64mp_mode su
+ }, {
+ .width = 1280,
+ .height = 720,
+- .line_length_pix = 0x1dc4,
++ .line_length_pix = 0x1b08,
+ .crop = {
+ .left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT + 2064,
+ .top = ARDUCAM_64MP_PIXEL_ARRAY_TOP + 2032,
--- /dev/null
+From 7b3d0124c5cf462d5be0b0d4e558002b74750911 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Fri, 5 May 2023 14:36:15 +0800
+Subject: [PATCH] media: i2c: arducam_64mp: Add 8000x6000 resolution
+
+Added 8000x6000 10-bit (cropped) @ 3fps mode for Arducam 64MP
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/arducam_64mp.c | 77 ++++++++++++++++++++++++++++++++
+ 1 file changed, 77 insertions(+)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -849,6 +849,65 @@ static const struct arducam_64mp_reg mod
+ {0x020f, 0x00},
+ };
+
++/* 48 mpix 3.0fps */
++static const struct arducam_64mp_reg mode_8000x6000_regs[] = {
++ {0x0342, 0xb6},
++ {0x0343, 0xb2},
++ {0x0340, 0x19},
++ {0x0341, 0x0e},
++ {0x0344, 0x02},
++ {0x0345, 0x70},
++ {0x0346, 0x01},
++ {0x0347, 0xd8},
++ {0x0348, 0x21},
++ {0x0349, 0xaf},
++ {0x034a, 0x19},
++ {0x034b, 0x47},
++ {0x0900, 0x00},
++ {0x0901, 0x11},
++ {0x0902, 0x0a},
++ {0x30d8, 0x00},
++ {0x3200, 0x01},
++ {0x3201, 0x01},
++ {0x0408, 0x00},
++ {0x0409, 0x00},
++ {0x040a, 0x00},
++ {0x040b, 0x00},
++ {0x040c, 0x1f},
++ {0x040d, 0x40},
++ {0x040e, 0x17},
++ {0x040f, 0x70},
++ {0x034c, 0x1f},
++ {0x034d, 0x40},
++ {0x034e, 0x17},
++ {0x034f, 0x70},
++ {0x30d9, 0x01},
++ {0x32d5, 0x01},
++ {0x32d6, 0x00},
++ {0x401e, 0x00},
++ {0x40b8, 0x04},
++ {0x40b9, 0x20},
++ {0x40bc, 0x02},
++ {0x40bd, 0x58},
++ {0x40be, 0x02},
++ {0x40bf, 0x58},
++ {0x41a4, 0x00},
++ {0x5a09, 0x01},
++ {0x5a17, 0x01},
++ {0x5a25, 0x01},
++ {0x5a33, 0x01},
++ {0x98d7, 0x14},
++ {0x98d8, 0x14},
++ {0x98d9, 0x00},
++ {0x99c4, 0x00},
++ {0x0202, 0x03},
++ {0x0203, 0xe8},
++ {0x0204, 0x00},
++ {0x0205, 0x00},
++ {0x020e, 0x01},
++ {0x020f, 0x00},
++};
++
+ /* 16 mpix 10fps */
+ static const struct arducam_64mp_reg mode_4624x3472_regs[] = {
+ {0x0342, 0x63},
+@@ -1135,6 +1194,24 @@ static const struct arducam_64mp_mode su
+ .regs = mode_9152x6944_regs,
+ }
+ }, {
++ .width = 8000,
++ .height = 6000,
++ .line_length_pix = 0xb6b2,
++ .crop = {
++ .left = ARDUCAM_64MP_PIXEL_ARRAY_LEFT + 624,
++ .top = ARDUCAM_64MP_PIXEL_ARRAY_TOP + 472,
++ .width = 9248,
++ .height = 6944,
++ },
++ .timeperframe_default = {
++ .numerator = 100,
++ .denominator = 300
++ },
++ .reg_list = {
++ .num_of_regs = ARRAY_SIZE(mode_8000x6000_regs),
++ .regs = mode_8000x6000_regs,
++ }
++ }, {
+ .width = 4624,
+ .height = 3472,
+ .line_length_pix = 0x6397,
--- /dev/null
+From b9d2d1862aa5b798cecb87a95d970ad34a4aebc0 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Tue, 30 May 2023 15:50:05 +0800
+Subject: [PATCH] media: i2c: arducam_64mp: Add PDAF support
+
+Enable PDAF output for all modes, and also need to modify Embedded Line
+Width to 11560 * 3 (two lines of Embedded Data + one line of PDAF).
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/arducam_64mp.c | 64 ++++++++++++++++++++++++++++++--
+ 1 file changed, 61 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -95,7 +95,7 @@
+ #define ARDUCAM_64MP_TEST_PATTERN_GB_DEFAULT 0
+
+ /* Embedded metadata stream structure */
+-#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH 16384
++#define ARDUCAM_64MP_EMBEDDED_LINE_WIDTH (11560 * 3)
+ #define ARDUCAM_64MP_NUM_EMBEDDED_LINES 1
+
+ enum pad_types {
+@@ -144,6 +144,7 @@ struct arducam_64mp_mode {
+ };
+
+ static const struct arducam_64mp_reg mode_common_regs[] = {
++ {0x0100, 0x00},
+ {0x0136, 0x18},
+ {0x0137, 0x00},
+ {0x33F0, 0x01},
+@@ -788,6 +789,7 @@ static const struct arducam_64mp_reg mod
+ {0x3092, 0x01},
+ {0x3093, 0x00},
+ {0x0350, 0x00},
++ {0x3419, 0x00},
+ };
+
+ /* 64 mpix 2.7fps */
+@@ -847,6 +849,14 @@ static const struct arducam_64mp_reg mod
+ {0x0205, 0x00},
+ {0x020e, 0x01},
+ {0x020f, 0x00},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x02},
++ {0x341f, 0x3c},
++ {0x3420, 0x02},
++ {0x3421, 0x42},
+ };
+
+ /* 48 mpix 3.0fps */
+@@ -906,6 +916,14 @@ static const struct arducam_64mp_reg mod
+ {0x0205, 0x00},
+ {0x020e, 0x01},
+ {0x020f, 0x00},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x01},
++ {0x341f, 0xf4},
++ {0x3420, 0x01},
++ {0x3421, 0xf4},
+ };
+
+ /* 16 mpix 10fps */
+@@ -959,6 +977,14 @@ static const struct arducam_64mp_reg mod
+ {0x98d8, 0x8c},
+ {0x98d9, 0x0a},
+ {0x99c4, 0x16},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x01},
++ {0x341f, 0x21},
++ {0x3420, 0x01},
++ {0x3421, 0x21},
+ };
+
+ /* 4k 20fps mode */
+@@ -1012,6 +1038,14 @@ static const struct arducam_64mp_reg mod
+ {0x98d8, 0x8c},
+ {0x98d9, 0x0a},
+ {0x99c4, 0x16},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0xf0},
++ {0x3420, 0x00},
++ {0x3421, 0xb4},
+ };
+
+ /* 4x4 binned 30fps mode */
+@@ -1031,7 +1065,7 @@ static const struct arducam_64mp_reg mod
+ {0x0900, 0x01},
+ {0x0901, 0x44},
+ {0x0902, 0x08},
+- {0x30d8, 0x00},
++ {0x30d8, 0x04},
+ {0x3200, 0x43},
+ {0x3201, 0x43},
+ {0x0408, 0x00},
+@@ -1046,7 +1080,7 @@ static const struct arducam_64mp_reg mod
+ {0x034d, 0x08},
+ {0x034e, 0x06},
+ {0x034f, 0xc8},
+- {0x30d9, 0x01},
++ {0x30d9, 0x00},
+ {0x32d5, 0x00},
+ {0x32d6, 0x00},
+ {0x401e, 0x00},
+@@ -1065,6 +1099,14 @@ static const struct arducam_64mp_reg mod
+ {0x98d8, 0x8c},
+ {0x98d9, 0x0a},
+ {0x99c4, 0x16},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0x90},
++ {0x3420, 0x00},
++ {0x3421, 0x90},
+ };
+
+ /* 1080p 60fps mode */
+@@ -1118,6 +1160,14 @@ static const struct arducam_64mp_reg mod
+ {0x98d8, 0x8c},
+ {0x98d9, 0x0a},
+ {0x99c4, 0x16},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0x78},
++ {0x3420, 0x00},
++ {0x3421, 0x5a},
+ };
+
+ /* 720p 120fps mode */
+@@ -1171,6 +1221,14 @@ static const struct arducam_64mp_reg mod
+ {0x98d8, 0x8c},
+ {0x98d9, 0x0a},
+ {0x99c4, 0x16},
++ {0x341a, 0x00},
++ {0x341b, 0x00},
++ {0x341c, 0x00},
++ {0x341d, 0x00},
++ {0x341e, 0x00},
++ {0x341f, 0x50},
++ {0x3420, 0x00},
++ {0x3421, 0x3c},
+ };
+
+ /* Mode configs */
--- /dev/null
+From 6f4106f7a7fdcbc03290008713915b4122988c90 Mon Sep 17 00:00:00 2001
+From: James Hughes <JamesH65@users.noreply.github.com>
+Date: Wed, 5 Jul 2023 15:43:30 +0100
+Subject: [PATCH] overlays: audremap: Document CM4 40&41 restriction
+
+Update audremap information to state pins 40,41 are not available on the CM4.
+
+Signed-off-by: James Hughes (james.hughes@raspberrypi.com)
+---
+ arch/arm/boot/dts/overlays/README | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -703,7 +703,8 @@ Params: swap_lr Reverse
+ nothing on BCM2711 (default off)
+ pins_12_13 Select GPIOs 12 & 13 (default)
+ pins_18_19 Select GPIOs 18 & 19
+- pins_40_41 Select GPIOs 40 & 41
++ pins_40_41 Select GPIOs 40 & 41 (not available on CM4, used
++ for other purposes)
+ pins_40_45 Select GPIOs 40 & 45 (don't use on BCM2711 - the
+ pins are on different controllers)
+
--- /dev/null
+From 1d15e6a34222cc8d8eb1050e7a3e276b0348be41 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 3 Jul 2023 11:04:56 +0100
+Subject: [PATCH] fixup! Allow mac address to be set in smsc95xx
+
+usbnet: smsc95xx: Fix indentation of smsc95xx_is_macaddr_param()
+
+smsc95xx_is_macaddr_param() is incorrectly indented, it uses 7 spaces
+instead of tabs. Fix it.
+
+Fixes: aac7b105788e ("Allow mac address to be set in smsc95xx")
+Signed-off-by: Philipp Rosenberger <p.rosenberger@kunbus.com>
+[lukas: fix netif_dbg() indentation as well, wordsmith commit message]
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+
+usbnet: smsc95xx: Simplify MAC address parsing
+
+Parsing the MAC address provided on the kernel command line can be
+simplified quite a bit by taking advantage of the kernel's built-in
+mac_pton() helper.
+
+Likewise emitting the MAC address can be simplified with the %pM
+format string conversion.
+
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+
+usbnet: smsc95xx: Fix style issues in smsc95xx_is_macaddr_param()
+
+It is bad practice to have a function named ..._is_...() which has side
+effects. So drop the 'is' from the name.
+
+Per kernel convention return 0 on success and a negative errno on
+failure.
+
+Validate the MAC address retrieved from the command line.
+
+Signed-off-by: Philipp Rosenberger <p.rosenberger@kunbus.com>
+[lukas: leave 2nd function parameter unchanged, wordsmith commit message]
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+---
+ drivers/net/usb/smsc95xx.c | 61 +++++++++++---------------------------
+ 1 file changed, 17 insertions(+), 44 deletions(-)
+
+--- a/drivers/net/usb/smsc95xx.c
++++ b/drivers/net/usb/smsc95xx.c
+@@ -814,49 +814,18 @@ static int smsc95xx_ioctl(struct net_dev
+ }
+
+ /* Check the macaddr module parameter for a MAC address */
+-static int smsc95xx_is_macaddr_param(struct usbnet *dev, struct net_device *nd)
++static int smsc95xx_macaddr_param(struct usbnet *dev, struct net_device *nd)
+ {
+- int i, j, got_num, num;
+- u8 mtbl[ETH_ALEN];
++ u8 mtbl[ETH_ALEN];
+
+- if (macaddr[0] == ':')
+- return 0;
+-
+- i = 0;
+- j = 0;
+- num = 0;
+- got_num = 0;
+- while (j < ETH_ALEN) {
+- if (macaddr[i] && macaddr[i] != ':') {
+- got_num++;
+- if ('0' <= macaddr[i] && macaddr[i] <= '9')
+- num = num * 16 + macaddr[i] - '0';
+- else if ('A' <= macaddr[i] && macaddr[i] <= 'F')
+- num = num * 16 + 10 + macaddr[i] - 'A';
+- else if ('a' <= macaddr[i] && macaddr[i] <= 'f')
+- num = num * 16 + 10 + macaddr[i] - 'a';
+- else
+- break;
+- i++;
+- } else if (got_num == 2) {
+- mtbl[j++] = (u8) num;
+- num = 0;
+- got_num = 0;
+- i++;
+- } else {
+- break;
+- }
+- }
+-
+- if (j == ETH_ALEN) {
+- netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: "
+- "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2],
+- mtbl[3], mtbl[4], mtbl[5]);
+- dev_addr_mod(nd, 0, mtbl, ETH_ALEN);
+- return 1;
+- } else {
+- return 0;
+- }
++ if (mac_pton(macaddr, mtbl)) {
++ netif_dbg(dev, ifup, dev->net,
++ "Overriding MAC address with: %pM\n", mtbl);
++ dev_addr_mod(nd, 0, mtbl, ETH_ALEN);
++ return 0;
++ } else {
++ return -EINVAL;
++ }
+ }
+
+ static void smsc95xx_init_mac_address(struct usbnet *dev)
+@@ -883,8 +852,12 @@ static void smsc95xx_init_mac_address(st
+ }
+
+ /* Check module parameters */
+- if (smsc95xx_is_macaddr_param(dev, dev->net))
+- return;
++ if (smsc95xx_macaddr_param(dev, dev->net) == 0) {
++ if (is_valid_ether_addr(dev->net->dev_addr)) {
++ netif_dbg(dev, ifup, dev->net, "MAC address read from module parameter\n");
++ return;
++ }
++ }
+
+ /* no useful static MAC address found. generate a random one */
+ eth_hw_addr_random(dev->net);
--- /dev/null
+From a2d2745c311baa588fb0fffbe38076294f06b7c0 Mon Sep 17 00:00:00 2001
+From: Nicolai Buchwitz <n.buchwitz@kunbus.com>
+Date: Wed, 12 Jul 2023 11:30:42 +0200
+Subject: [PATCH] cfg80211: ship debian certificates as hex files
+
+Loading the regulatory database from the debian files fails with
+
+"loaded regulatory.db is malformed or signature is missing/invalid"
+
+due to missing certificates. Add these debian-specific certificates
+from upstream to fix this error. See #5536 for details.
+
+The certificates have been imported as following:
+
+patch -p1 <<<$(
+curl https://salsa.debian.org/kernel-team/linux/-/raw/\
+master/debian/patches/debian/\
+wireless-add-debian-wireless-regdb-certificates.patch
+)
+
+Signed-off-by: Nicolai Buchwitz <n.buchwitz@kunbus.com>
+---
+ net/wireless/certs/debian.hex | 1426 +++++++++++++++++++++++++++++++++
+ 1 file changed, 1426 insertions(+)
+ create mode 100644 net/wireless/certs/debian.hex
+
+--- /dev/null
++++ b/net/wireless/certs/debian.hex
+@@ -0,0 +1,1426 @@
++0x30,
++0x82,
++0x02,
++0xbd,
++0x30,
++0x82,
++0x01,
++0xa5,
++0x02,
++0x14,
++0x57,
++0x7e,
++0x02,
++0x1c,
++0xb9,
++0x80,
++0xe0,
++0xe8,
++0x20,
++0x82,
++0x1b,
++0xa7,
++0xb5,
++0x4b,
++0x49,
++0x61,
++0xb8,
++0xb4,
++0xfa,
++0xdf,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x30,
++0x1a,
++0x31,
++0x18,
++0x30,
++0x16,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x0f,
++0x62,
++0x65,
++0x6e,
++0x68,
++0x40,
++0x64,
++0x65,
++0x62,
++0x69,
++0x61,
++0x6e,
++0x2e,
++0x6f,
++0x72,
++0x67,
++0x30,
++0x20,
++0x17,
++0x0d,
++0x32,
++0x30,
++0x30,
++0x31,
++0x33,
++0x30,
++0x31,
++0x33,
++0x32,
++0x36,
++0x31,
++0x33,
++0x5a,
++0x18,
++0x0f,
++0x32,
++0x31,
++0x32,
++0x30,
++0x30,
++0x31,
++0x30,
++0x36,
++0x31,
++0x33,
++0x32,
++0x36,
++0x31,
++0x33,
++0x5a,
++0x30,
++0x1a,
++0x31,
++0x18,
++0x30,
++0x16,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x0f,
++0x62,
++0x65,
++0x6e,
++0x68,
++0x40,
++0x64,
++0x65,
++0x62,
++0x69,
++0x61,
++0x6e,
++0x2e,
++0x6f,
++0x72,
++0x67,
++0x30,
++0x82,
++0x01,
++0x22,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x01,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x0f,
++0x00,
++0x30,
++0x82,
++0x01,
++0x0a,
++0x02,
++0x82,
++0x01,
++0x01,
++0x00,
++0x9d,
++0xe1,
++0x77,
++0xa0,
++0x24,
++0xa0,
++0xd5,
++0x79,
++0x65,
++0x3a,
++0x07,
++0x90,
++0xc9,
++0xf6,
++0xa5,
++0xa6,
++0x1f,
++0x84,
++0x1c,
++0x23,
++0x07,
++0x4b,
++0x4f,
++0xa5,
++0x03,
++0xc6,
++0x0f,
++0xf7,
++0x54,
++0xd5,
++0x8b,
++0x7e,
++0x79,
++0x81,
++0x00,
++0xd2,
++0xe9,
++0x3d,
++0xf4,
++0x97,
++0xfe,
++0x84,
++0xcd,
++0x55,
++0xbd,
++0xc9,
++0x8f,
++0x21,
++0x57,
++0x88,
++0x06,
++0x39,
++0x90,
++0x66,
++0x41,
++0x26,
++0x79,
++0x2c,
++0xca,
++0x3f,
++0x95,
++0x87,
++0x01,
++0x11,
++0x2f,
++0x2f,
++0xb0,
++0xe1,
++0x0b,
++0x43,
++0xfc,
++0x5f,
++0x2f,
++0x4f,
++0x67,
++0x04,
++0xdb,
++0x4d,
++0xb7,
++0x72,
++0x4d,
++0xd1,
++0xc5,
++0x76,
++0x73,
++0x4d,
++0x91,
++0x69,
++0xb0,
++0x71,
++0x17,
++0x36,
++0xea,
++0xab,
++0x0a,
++0x3a,
++0xcd,
++0x95,
++0x9b,
++0x76,
++0x1b,
++0x8e,
++0x21,
++0x17,
++0x8f,
++0xc5,
++0x02,
++0xbf,
++0x24,
++0xc7,
++0xc0,
++0x40,
++0xb1,
++0x3b,
++0xc4,
++0x80,
++0x7c,
++0x71,
++0xa5,
++0x51,
++0xdc,
++0xf7,
++0x3a,
++0x58,
++0x7f,
++0xb1,
++0x07,
++0x81,
++0x8a,
++0x10,
++0xd1,
++0xf6,
++0x93,
++0x17,
++0x71,
++0xe0,
++0xfa,
++0x51,
++0x79,
++0x15,
++0xd4,
++0xd7,
++0x8f,
++0xad,
++0xbd,
++0x6f,
++0x38,
++0xe1,
++0x26,
++0x7d,
++0xbc,
++0xf0,
++0x3e,
++0x80,
++0x89,
++0xb4,
++0xec,
++0x8e,
++0x69,
++0x90,
++0xdb,
++0x97,
++0x8a,
++0xf0,
++0x23,
++0x23,
++0x83,
++0x82,
++0x3b,
++0x6a,
++0xb1,
++0xac,
++0xeb,
++0xe7,
++0x99,
++0x74,
++0x2a,
++0x35,
++0x8e,
++0xa9,
++0x64,
++0xfd,
++0x46,
++0x9e,
++0xe8,
++0xe5,
++0x48,
++0x61,
++0x31,
++0x6e,
++0xe6,
++0xfc,
++0x19,
++0x18,
++0x54,
++0xc3,
++0x1b,
++0x4f,
++0xd6,
++0x00,
++0x44,
++0x87,
++0x1c,
++0x37,
++0x45,
++0xea,
++0xf5,
++0xc9,
++0xcb,
++0x0f,
++0x0c,
++0x55,
++0xec,
++0xcf,
++0x6a,
++0xc2,
++0x45,
++0x26,
++0x23,
++0xa2,
++0x31,
++0x52,
++0x4d,
++0xee,
++0x21,
++0x7d,
++0xfd,
++0x58,
++0x72,
++0xc2,
++0x28,
++0xc5,
++0x8e,
++0xa9,
++0xd0,
++0xee,
++0x01,
++0x77,
++0x08,
++0xa5,
++0xf0,
++0x22,
++0x2b,
++0x47,
++0x79,
++0x2b,
++0xcf,
++0x9a,
++0x46,
++0xb5,
++0x8f,
++0xfd,
++0x64,
++0xa2,
++0xb5,
++0xed,
++0x02,
++0x03,
++0x01,
++0x00,
++0x01,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x01,
++0x00,
++0x20,
++0x44,
++0xfe,
++0xa9,
++0x9e,
++0xdd,
++0x9b,
++0xea,
++0xce,
++0x25,
++0x75,
++0x08,
++0xf0,
++0x2b,
++0x53,
++0xf7,
++0x5a,
++0x36,
++0x1c,
++0x4a,
++0x23,
++0x7f,
++0xd0,
++0x41,
++0x3c,
++0x12,
++0x2b,
++0xb9,
++0x80,
++0x4e,
++0x8a,
++0x15,
++0x5d,
++0x1f,
++0x40,
++0xa7,
++0x26,
++0x28,
++0x32,
++0xc3,
++0x5b,
++0x06,
++0x28,
++0x2d,
++0x3d,
++0x08,
++0x09,
++0x1e,
++0x01,
++0xe9,
++0x67,
++0xe3,
++0x33,
++0xe6,
++0x15,
++0x45,
++0x39,
++0xee,
++0x17,
++0x83,
++0xdb,
++0x42,
++0xff,
++0x7f,
++0x35,
++0xf4,
++0xac,
++0x16,
++0xdb,
++0xba,
++0xb8,
++0x1a,
++0x20,
++0x21,
++0x41,
++0xff,
++0xf3,
++0x92,
++0xff,
++0x65,
++0x6e,
++0x29,
++0x16,
++0xd0,
++0xbf,
++0x8d,
++0xdf,
++0x48,
++0x2c,
++0x73,
++0x36,
++0x7f,
++0x22,
++0xe6,
++0xee,
++0x78,
++0xb4,
++0x63,
++0x83,
++0x0e,
++0x39,
++0xeb,
++0xaf,
++0x10,
++0x2a,
++0x90,
++0xd3,
++0xfc,
++0xe6,
++0xc3,
++0x8f,
++0x97,
++0x5b,
++0x76,
++0xbf,
++0x9b,
++0xf5,
++0x98,
++0xd2,
++0x53,
++0x06,
++0x8b,
++0xf8,
++0xa4,
++0x04,
++0x9b,
++0x1b,
++0x62,
++0x6a,
++0x9d,
++0xac,
++0xe6,
++0x4b,
++0x0d,
++0xc9,
++0xd7,
++0x56,
++0x63,
++0x15,
++0x01,
++0x38,
++0x8c,
++0xbe,
++0xf1,
++0x44,
++0xc4,
++0x38,
++0x27,
++0xe0,
++0xcf,
++0x72,
++0xd6,
++0x3d,
++0xe4,
++0xf7,
++0x4b,
++0x3b,
++0xd2,
++0xb1,
++0x0c,
++0xd5,
++0x83,
++0x6d,
++0x1e,
++0x10,
++0x04,
++0x69,
++0x29,
++0x88,
++0x69,
++0xe0,
++0x7d,
++0xd7,
++0xdb,
++0xb4,
++0x59,
++0x72,
++0x8d,
++0x9d,
++0x3c,
++0x43,
++0xaf,
++0xc6,
++0x7d,
++0xb7,
++0x21,
++0x15,
++0x52,
++0x8a,
++0xe9,
++0x9b,
++0x6b,
++0x2e,
++0xe8,
++0x27,
++0x3c,
++0x3f,
++0x2d,
++0x84,
++0xfb,
++0x9a,
++0x22,
++0x0a,
++0x9f,
++0x6a,
++0x25,
++0xe6,
++0x39,
++0xe4,
++0x74,
++0x73,
++0xb6,
++0x2a,
++0x70,
++0xaa,
++0x1d,
++0xcb,
++0xcc,
++0xd4,
++0xa0,
++0x1b,
++0x26,
++0x71,
++0x63,
++0x04,
++0xc5,
++0x12,
++0x21,
++0x48,
++0xba,
++0x92,
++0x27,
++0x06,
++0xa8,
++0x3e,
++0x6d,
++0xa1,
++0x43,
++0xa5,
++0xd2,
++0x2a,
++0xf7,
++0xca,
++0xc4,
++0x26,
++0xe8,
++0x5b,
++0x1f,
++0xe4,
++0xdc,
++0x89,
++0xdc,
++0x1f,
++0x04,
++0x79,
++0x3f,
++0x30,
++0x82,
++0x02,
++0xcd,
++0x30,
++0x82,
++0x01,
++0xb5,
++0x02,
++0x14,
++0x3a,
++0xbb,
++0xc6,
++0xec,
++0x14,
++0x6e,
++0x09,
++0xd1,
++0xb6,
++0x01,
++0x6a,
++0xb9,
++0xd6,
++0xcf,
++0x71,
++0xdd,
++0x23,
++0x3f,
++0x03,
++0x28,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x30,
++0x22,
++0x31,
++0x20,
++0x30,
++0x1e,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x17,
++0x72,
++0x6f,
++0x6d,
++0x61,
++0x69,
++0x6e,
++0x2e,
++0x70,
++0x65,
++0x72,
++0x69,
++0x65,
++0x72,
++0x40,
++0x67,
++0x6d,
++0x61,
++0x69,
++0x6c,
++0x2e,
++0x63,
++0x6f,
++0x6d,
++0x30,
++0x20,
++0x17,
++0x0d,
++0x32,
++0x30,
++0x30,
++0x32,
++0x32,
++0x34,
++0x31,
++0x39,
++0x30,
++0x31,
++0x34,
++0x34,
++0x5a,
++0x18,
++0x0f,
++0x32,
++0x31,
++0x32,
++0x30,
++0x30,
++0x31,
++0x33,
++0x31,
++0x31,
++0x39,
++0x30,
++0x31,
++0x34,
++0x34,
++0x5a,
++0x30,
++0x22,
++0x31,
++0x20,
++0x30,
++0x1e,
++0x06,
++0x03,
++0x55,
++0x04,
++0x03,
++0x0c,
++0x17,
++0x72,
++0x6f,
++0x6d,
++0x61,
++0x69,
++0x6e,
++0x2e,
++0x70,
++0x65,
++0x72,
++0x69,
++0x65,
++0x72,
++0x40,
++0x67,
++0x6d,
++0x61,
++0x69,
++0x6c,
++0x2e,
++0x63,
++0x6f,
++0x6d,
++0x30,
++0x82,
++0x01,
++0x22,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x01,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x0f,
++0x00,
++0x30,
++0x82,
++0x01,
++0x0a,
++0x02,
++0x82,
++0x01,
++0x01,
++0x00,
++0xf0,
++0xb8,
++0x4f,
++0x3f,
++0x70,
++0x78,
++0xf8,
++0x74,
++0x45,
++0xa2,
++0x28,
++0xaf,
++0x04,
++0x75,
++0x04,
++0xa3,
++0xf3,
++0xa7,
++0xc7,
++0x04,
++0xac,
++0xb6,
++0xe1,
++0xfc,
++0xe1,
++0xc0,
++0x3d,
++0xe0,
++0x26,
++0x90,
++0x8a,
++0x45,
++0x60,
++0xc4,
++0x75,
++0xf3,
++0x1a,
++0x33,
++0x37,
++0x56,
++0x7d,
++0x30,
++0x07,
++0x75,
++0x0e,
++0xa6,
++0x79,
++0x06,
++0x95,
++0x9d,
++0x17,
++0x3c,
++0x09,
++0xa9,
++0x7f,
++0xab,
++0x95,
++0x5d,
++0xed,
++0xe0,
++0x75,
++0x26,
++0x2f,
++0x65,
++0x65,
++0xcd,
++0x61,
++0xb1,
++0x33,
++0x27,
++0x67,
++0x41,
++0xa1,
++0x01,
++0x13,
++0xe9,
++0x13,
++0x6a,
++0x6d,
++0x4e,
++0x98,
++0xe1,
++0x9e,
++0x7b,
++0x0b,
++0x5b,
++0x44,
++0xef,
++0x68,
++0x5a,
++0x6f,
++0x7d,
++0x97,
++0xa1,
++0x33,
++0x22,
++0x97,
++0x12,
++0x21,
++0x09,
++0x8f,
++0x90,
++0xe0,
++0x25,
++0x94,
++0xdd,
++0x8a,
++0x3a,
++0xf7,
++0x4a,
++0x60,
++0x04,
++0x26,
++0x6d,
++0x00,
++0x82,
++0xe4,
++0xcf,
++0x64,
++0x1c,
++0x79,
++0x15,
++0x24,
++0xf2,
++0x42,
++0x86,
++0xf5,
++0x10,
++0x86,
++0xac,
++0x20,
++0x88,
++0x90,
++0x87,
++0xdf,
++0x8c,
++0x37,
++0x7c,
++0xbf,
++0x35,
++0xd5,
++0x6f,
++0x9f,
++0x77,
++0xc3,
++0xcd,
++0x69,
++0x25,
++0x06,
++0xc2,
++0x65,
++0x51,
++0x71,
++0x89,
++0x7f,
++0x6e,
++0x4d,
++0xe5,
++0xd5,
++0x8a,
++0x36,
++0x1a,
++0xad,
++0xc1,
++0x18,
++0xd6,
++0x14,
++0x42,
++0x87,
++0xf0,
++0x93,
++0x83,
++0xf1,
++0x99,
++0x74,
++0xc4,
++0x13,
++0xaa,
++0x3b,
++0x66,
++0x85,
++0x6f,
++0xe0,
++0xbc,
++0x5f,
++0xb6,
++0x40,
++0xa6,
++0x41,
++0x06,
++0x0a,
++0xba,
++0x0e,
++0xe9,
++0x32,
++0x44,
++0x10,
++0x39,
++0x53,
++0xcd,
++0xbf,
++0xf3,
++0xd3,
++0x26,
++0xf6,
++0xb6,
++0x2b,
++0x40,
++0x2e,
++0xb9,
++0x88,
++0xc1,
++0xf4,
++0xe3,
++0xa0,
++0x28,
++0x77,
++0x4f,
++0xba,
++0xa8,
++0xca,
++0x9c,
++0x05,
++0xba,
++0x88,
++0x96,
++0x99,
++0x54,
++0x89,
++0xa2,
++0x8d,
++0xf3,
++0x73,
++0xa1,
++0x8c,
++0x4a,
++0xa8,
++0x71,
++0xee,
++0x2e,
++0xd2,
++0x83,
++0x14,
++0x48,
++0xbd,
++0x98,
++0xc6,
++0xce,
++0xdc,
++0xa8,
++0xa3,
++0x97,
++0x2e,
++0x40,
++0x16,
++0x2f,
++0x02,
++0x03,
++0x01,
++0x00,
++0x01,
++0x30,
++0x0d,
++0x06,
++0x09,
++0x2a,
++0x86,
++0x48,
++0x86,
++0xf7,
++0x0d,
++0x01,
++0x01,
++0x0b,
++0x05,
++0x00,
++0x03,
++0x82,
++0x01,
++0x01,
++0x00,
++0x76,
++0x5d,
++0x03,
++0x3d,
++0xb6,
++0x96,
++0x00,
++0x1b,
++0x6e,
++0x0c,
++0xdd,
++0xbb,
++0xc8,
++0xdf,
++0xbc,
++0xeb,
++0x6c,
++0x01,
++0x40,
++0x1a,
++0x2b,
++0x07,
++0x60,
++0xa1,
++0x1a,
++0xe1,
++0x43,
++0x57,
++0xfa,
++0xbe,
++0xde,
++0xbb,
++0x8f,
++0x73,
++0xf3,
++0x92,
++0xa2,
++0xaa,
++0x83,
++0x01,
++0xc1,
++0x17,
++0xe4,
++0x9d,
++0x09,
++0x41,
++0xe0,
++0x32,
++0x33,
++0x97,
++0x4b,
++0xf2,
++0xdc,
++0x0f,
++0x8b,
++0xa8,
++0xb8,
++0x5a,
++0x04,
++0x86,
++0xf6,
++0x71,
++0xa1,
++0x97,
++0xd0,
++0x54,
++0x56,
++0x10,
++0x8e,
++0x54,
++0x99,
++0x0d,
++0x2a,
++0xa9,
++0xaf,
++0x1b,
++0x55,
++0x59,
++0x06,
++0x2b,
++0xa4,
++0x5f,
++0xb1,
++0x54,
++0xa6,
++0xec,
++0xc7,
++0xd6,
++0x43,
++0xee,
++0x86,
++0x2c,
++0x9b,
++0x18,
++0x9d,
++0x8f,
++0x00,
++0x82,
++0xc1,
++0x88,
++0x61,
++0x16,
++0x85,
++0x3c,
++0x17,
++0x56,
++0xfe,
++0x6a,
++0xa0,
++0x7a,
++0x68,
++0xc5,
++0x7b,
++0x3d,
++0x3c,
++0xb6,
++0x13,
++0x18,
++0x99,
++0x6d,
++0x74,
++0x65,
++0x13,
++0x67,
++0xb7,
++0xfc,
++0x5a,
++0x44,
++0x48,
++0x72,
++0xa0,
++0x73,
++0xb8,
++0xff,
++0x02,
++0x9d,
++0x7c,
++0x5b,
++0xf9,
++0x7c,
++0x75,
++0x0a,
++0x3c,
++0x81,
++0x80,
++0x3c,
++0x41,
++0xf2,
++0xd5,
++0xfa,
++0x3d,
++0x1f,
++0xe3,
++0xda,
++0x8c,
++0xa5,
++0x17,
++0x1f,
++0x53,
++0x1a,
++0x75,
++0xad,
++0x4e,
++0x11,
++0x1c,
++0x07,
++0xec,
++0x0a,
++0x69,
++0xfd,
++0x33,
++0xfa,
++0x32,
++0x7e,
++0x66,
++0xf5,
++0x29,
++0xe8,
++0x4d,
++0x8a,
++0xfa,
++0x0d,
++0x4b,
++0x68,
++0xc3,
++0x95,
++0x11,
++0xba,
++0x6f,
++0x1e,
++0x07,
++0x8c,
++0x85,
++0xc7,
++0xc7,
++0xc9,
++0xc1,
++0x30,
++0xa3,
++0x70,
++0xb0,
++0xa1,
++0xe0,
++0xd5,
++0x85,
++0x15,
++0x94,
++0x77,
++0xc1,
++0x1c,
++0x91,
++0xf1,
++0x5f,
++0x50,
++0xcd,
++0x2c,
++0x57,
++0x4b,
++0x22,
++0x4f,
++0xee,
++0x95,
++0xd7,
++0xa7,
++0xa4,
++0x59,
++0x62,
++0xae,
++0xb9,
++0xbf,
++0xd7,
++0x63,
++0x5a,
++0x04,
++0xfc,
++0x24,
++0x11,
++0xae,
++0x34,
++0x4b,
++0xf4,
++0x0c,
++0x9f,
++0x0b,
++0x59,
++0x7d,
++0x27,
++0x39,
++0x54,
++0x69,
++0x4f,
++0xfd,
++0x6e,
++0x44,
++0x9f,
++0x21,
--- /dev/null
+From 3ece03b1575b0c3a0989e372aaaa4557ae74dc89 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 20 Jul 2023 11:28:20 +0100
+Subject: [PATCH] fixup! Add support for all the downstream rpi sound card
+ drivers
+
+Replace the Allo Dac clock driver with an extension of the
+HiFiBerry clock driver that it cloned.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/clk/Makefile | 1 -
+ drivers/clk/clk-allo-dac.c | 161 -----------------------------
+ drivers/clk/clk-hifiberry-dacpro.c | 57 ++++++----
+ sound/soc/bcm/Kconfig | 1 +
+ 4 files changed, 40 insertions(+), 180 deletions(-)
+ delete mode 100644 drivers/clk/clk-allo-dac.c
+
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -19,7 +19,6 @@ endif
+
+ # hardware specific clock types
+ # please keep this section sorted lexicographically by file path name
+-obj-$(CONFIG_SND_BCM2708_SOC_ALLO_BOSS_DAC) += clk-allo-dac.o
+ obj-$(CONFIG_COMMON_CLK_APPLE_NCO) += clk-apple-nco.o
+ obj-$(CONFIG_MACH_ASM9260) += clk-asm9260.o
+ obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
+--- a/drivers/clk/clk-allo-dac.c
++++ /dev/null
+@@ -1,161 +0,0 @@
+-/*
+- * Clock Driver for Allo DAC
+- *
+- * Author: Baswaraj K <jaikumar@cem-solutions.net>
+- * Copyright 2016
+- * based on code by Stuart MacLean
+- *
+- * This program is free software; you can redistribute it and/or
+- * modify it under the terms of the GNU General Public License
+- * version 2 as published by the Free Software Foundation.
+- *
+- * This program is distributed in the hope that it will be useful, but
+- * WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+- * General Public License for more details.
+- */
+-
+-#include <linux/clk-provider.h>
+-#include <linux/clkdev.h>
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/slab.h>
+-#include <linux/platform_device.h>
+-
+-/* Clock rate of CLK44EN attached to GPIO6 pin */
+-#define CLK_44EN_RATE 45158400UL
+-/* Clock rate of CLK48EN attached to GPIO3 pin */
+-#define CLK_48EN_RATE 49152000UL
+-
+-/**
+- * struct allo_dac_clk - Common struct to the Allo DAC
+- * @hw: clk_hw for the common clk framework
+- * @mode: 0 => CLK44EN, 1 => CLK48EN
+- */
+-struct clk_allo_hw {
+- struct clk_hw hw;
+- uint8_t mode;
+-};
+-
+-#define to_allo_clk(_hw) container_of(_hw, struct clk_allo_hw, hw)
+-
+-static const struct of_device_id clk_allo_dac_dt_ids[] = {
+- { .compatible = "allo,dac-clk",},
+- { }
+-};
+-MODULE_DEVICE_TABLE(of, clk_allo_dac_dt_ids);
+-
+-static unsigned long clk_allo_dac_recalc_rate(struct clk_hw *hw,
+- unsigned long parent_rate)
+-{
+- return (to_allo_clk(hw)->mode == 0) ? CLK_44EN_RATE :
+- CLK_48EN_RATE;
+-}
+-
+-static long clk_allo_dac_round_rate(struct clk_hw *hw,
+- unsigned long rate, unsigned long *parent_rate)
+-{
+- long actual_rate;
+-
+- if (rate <= CLK_44EN_RATE) {
+- actual_rate = (long)CLK_44EN_RATE;
+- } else if (rate >= CLK_48EN_RATE) {
+- actual_rate = (long)CLK_48EN_RATE;
+- } else {
+- long diff44Rate = (long)(rate - CLK_44EN_RATE);
+- long diff48Rate = (long)(CLK_48EN_RATE - rate);
+-
+- if (diff44Rate < diff48Rate)
+- actual_rate = (long)CLK_44EN_RATE;
+- else
+- actual_rate = (long)CLK_48EN_RATE;
+- }
+- return actual_rate;
+-}
+-
+-
+-static int clk_allo_dac_set_rate(struct clk_hw *hw,
+- unsigned long rate, unsigned long parent_rate)
+-{
+- unsigned long actual_rate;
+- struct clk_allo_hw *clk = to_allo_clk(hw);
+-
+- actual_rate = (unsigned long)clk_allo_dac_round_rate(hw, rate,
+- &parent_rate);
+- clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
+- return 0;
+-}
+-
+-
+-const struct clk_ops clk_allo_dac_rate_ops = {
+- .recalc_rate = clk_allo_dac_recalc_rate,
+- .round_rate = clk_allo_dac_round_rate,
+- .set_rate = clk_allo_dac_set_rate,
+-};
+-
+-static int clk_allo_dac_probe(struct platform_device *pdev)
+-{
+- int ret;
+- struct clk_allo_hw *proclk;
+- struct clk *clk;
+- struct device *dev;
+- struct clk_init_data init;
+-
+- dev = &pdev->dev;
+-
+- proclk = kzalloc(sizeof(struct clk_allo_hw), GFP_KERNEL);
+- if (!proclk)
+- return -ENOMEM;
+-
+- init.name = "clk-allo-dac";
+- init.ops = &clk_allo_dac_rate_ops;
+- init.flags = 0;
+- init.parent_names = NULL;
+- init.num_parents = 0;
+-
+- proclk->mode = 0;
+- proclk->hw.init = &init;
+-
+- clk = devm_clk_register(dev, &proclk->hw);
+- if (!IS_ERR(clk)) {
+- ret = of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
+- clk);
+- } else {
+- dev_err(dev, "Fail to register clock driver\n");
+- kfree(proclk);
+- ret = PTR_ERR(clk);
+- }
+- return ret;
+-}
+-
+-static int clk_allo_dac_remove(struct platform_device *pdev)
+-{
+- of_clk_del_provider(pdev->dev.of_node);
+- return 0;
+-}
+-
+-static struct platform_driver clk_allo_dac_driver = {
+- .probe = clk_allo_dac_probe,
+- .remove = clk_allo_dac_remove,
+- .driver = {
+- .name = "clk-allo-dac",
+- .of_match_table = clk_allo_dac_dt_ids,
+- },
+-};
+-
+-static int __init clk_allo_dac_init(void)
+-{
+- return platform_driver_register(&clk_allo_dac_driver);
+-}
+-core_initcall(clk_allo_dac_init);
+-
+-static void __exit clk_allo_dac_exit(void)
+-{
+- platform_driver_unregister(&clk_allo_dac_driver);
+-}
+-module_exit(clk_allo_dac_exit);
+-
+-MODULE_DESCRIPTION("Allo DAC clock driver");
+-MODULE_LICENSE("GPL v2");
+-MODULE_ALIAS("platform:clk-allo-dac");
+--- a/drivers/clk/clk-hifiberry-dacpro.c
++++ b/drivers/clk/clk-hifiberry-dacpro.c
+@@ -22,10 +22,12 @@
+ #include <linux/slab.h>
+ #include <linux/platform_device.h>
+
+-/* Clock rate of CLK44EN attached to GPIO6 pin */
+-#define CLK_44EN_RATE 22579200UL
+-/* Clock rate of CLK48EN attached to GPIO3 pin */
+-#define CLK_48EN_RATE 24576000UL
++struct ext_clk_rates {
++ /* Clock rate of CLK44EN attached to GPIO6 pin */
++ unsigned long clk_44en;
++ /* Clock rate of CLK48EN attached to GPIO3 pin */
++ unsigned long clk_48en;
++};
+
+ /**
+ * struct hifiberry_dacpro_clk - Common struct to the HiFiBerry DAC Pro
+@@ -35,12 +37,24 @@
+ struct clk_hifiberry_hw {
+ struct clk_hw hw;
+ uint8_t mode;
++ struct ext_clk_rates clk_rates;
+ };
+
+ #define to_hifiberry_clk(_hw) container_of(_hw, struct clk_hifiberry_hw, hw)
+
++static const struct ext_clk_rates hifiberry_dacpro_clks = {
++ .clk_44en = 22579200UL,
++ .clk_48en = 24576000UL,
++};
++
++static const struct ext_clk_rates allo_dac_clks = {
++ .clk_44en = 45158400UL,
++ .clk_48en = 49152000UL,
++};
++
+ static const struct of_device_id clk_hifiberry_dacpro_dt_ids[] = {
+- { .compatible = "hifiberry,dacpro-clk",},
++ { .compatible = "hifiberry,dacpro-clk", &hifiberry_dacpro_clks },
++ { .compatible = "allo,dac-clk", &allo_dac_clks },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, clk_hifiberry_dacpro_dt_ids);
+@@ -48,27 +62,29 @@ MODULE_DEVICE_TABLE(of, clk_hifiberry_da
+ static unsigned long clk_hifiberry_dacpro_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+ {
+- return (to_hifiberry_clk(hw)->mode == 0) ? CLK_44EN_RATE :
+- CLK_48EN_RATE;
++ struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
++ return (clk->mode == 0) ? clk->clk_rates.clk_44en :
++ clk->clk_rates.clk_48en;
+ }
+
+ static long clk_hifiberry_dacpro_round_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long *parent_rate)
+ {
++ struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
+ long actual_rate;
+
+- if (rate <= CLK_44EN_RATE) {
+- actual_rate = (long)CLK_44EN_RATE;
+- } else if (rate >= CLK_48EN_RATE) {
+- actual_rate = (long)CLK_48EN_RATE;
++ if (rate <= clk->clk_rates.clk_44en) {
++ actual_rate = (long)clk->clk_rates.clk_44en;
++ } else if (rate >= clk->clk_rates.clk_48en) {
++ actual_rate = (long)clk->clk_rates.clk_48en;
+ } else {
+- long diff44Rate = (long)(rate - CLK_44EN_RATE);
+- long diff48Rate = (long)(CLK_48EN_RATE - rate);
++ long diff44Rate = (long)(rate - clk->clk_rates.clk_44en);
++ long diff48Rate = (long)(clk->clk_rates.clk_48en - rate);
+
+ if (diff44Rate < diff48Rate)
+- actual_rate = (long)CLK_44EN_RATE;
++ actual_rate = (long)clk->clk_rates.clk_44en;
+ else
+- actual_rate = (long)CLK_48EN_RATE;
++ actual_rate = (long)clk->clk_rates.clk_48en;
+ }
+ return actual_rate;
+ }
+@@ -77,12 +93,12 @@ static long clk_hifiberry_dacpro_round_r
+ static int clk_hifiberry_dacpro_set_rate(struct clk_hw *hw,
+ unsigned long rate, unsigned long parent_rate)
+ {
+- unsigned long actual_rate;
+ struct clk_hifiberry_hw *clk = to_hifiberry_clk(hw);
++ unsigned long actual_rate;
+
+ actual_rate = (unsigned long)clk_hifiberry_dacpro_round_rate(hw, rate,
+ &parent_rate);
+- clk->mode = (actual_rate == CLK_44EN_RATE) ? 0 : 1;
++ clk->mode = (actual_rate == clk->clk_rates.clk_44en) ? 0 : 1;
+ return 0;
+ }
+
+@@ -95,13 +111,17 @@ const struct clk_ops clk_hifiberry_dacpr
+
+ static int clk_hifiberry_dacpro_probe(struct platform_device *pdev)
+ {
+- int ret;
++ const struct of_device_id *of_id;
+ struct clk_hifiberry_hw *proclk;
+ struct clk *clk;
+ struct device *dev;
+ struct clk_init_data init;
++ int ret;
+
+ dev = &pdev->dev;
++ of_id = of_match_node(clk_hifiberry_dacpro_dt_ids, dev->of_node);
++ if (!of_id)
++ return -EINVAL;
+
+ proclk = kzalloc(sizeof(struct clk_hifiberry_hw), GFP_KERNEL);
+ if (!proclk)
+@@ -115,6 +135,7 @@ static int clk_hifiberry_dacpro_probe(st
+
+ proclk->mode = 0;
+ proclk->hw.init = &init;
++ memcpy(&proclk->clk_rates, of_id->data, sizeof(proclk->clk_rates));
+
+ clk = devm_clk_register(dev, &proclk->hw);
+ if (!IS_ERR(clk)) {
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -271,6 +271,7 @@ config SND_BCM2708_SOC_ALLO_BOSS_DAC
+ tristate "Support for Allo Boss DAC"
+ depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
++ select COMMON_CLK_HIFIBERRY_DACPRO
+ help
+ Say Y or M if you want to add support for Allo Boss DAC.
+
--- /dev/null
+From 2addf7045f2b4866ab819f48e4d32f5734a32134 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 20 Jul 2023 15:15:27 +0100
+Subject: [PATCH] fixup! drm/tc358762: Set the pre_enable_upstream_first flag
+ to configure DSI host
+
+---
+ drivers/gpu/drm/bridge/tc358762.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/bridge/tc358762.c
++++ b/drivers/gpu/drm/bridge/tc358762.c
+@@ -229,7 +229,7 @@ static int tc358762_probe(struct mipi_ds
+ ctx->bridge.funcs = &tc358762_bridge_funcs;
+ ctx->bridge.type = DRM_MODE_CONNECTOR_DPI;
+ ctx->bridge.of_node = dev->of_node;
+- ctx->bridge.pre_enable_upstream_first = true;
++ ctx->bridge.pre_enable_prev_first = true;
+
+ drm_bridge_add(&ctx->bridge);
+
--- /dev/null
+From b84b8a9ad2046a855a7044b6368def01ddd5de6e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 21 Jul 2023 16:50:56 +0100
+Subject: [PATCH] rpi sound cards: Fix Codec Zero rate switching
+
+The Raspberry Pi Codec Zero (and IQaudIO Codec) don't notify the DA7213
+codec when it needs to change PLL frequencies. As a result, audio can
+be played at the wrong rate - play a 48kHz sound immediately after a
+44.1kHz sound to see the effect, but in some configurations the codec
+can lock into the wrong state and always get some rates wrong.
+
+Add the necessary notification to fix the issue.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/iqaudio-codec.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/sound/soc/bcm/iqaudio-codec.c
++++ b/sound/soc/bcm/iqaudio-codec.c
+@@ -143,6 +143,7 @@ static int snd_rpi_iqaudio_codec_hw_para
+ struct snd_pcm_hw_params *params)
+ {
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
++ struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
+ unsigned int samplerate = params_rate(params);
+
+ switch (samplerate) {
+@@ -152,15 +153,17 @@ static int snd_rpi_iqaudio_codec_hw_para
+ case 48000:
+ case 96000:
+ pll_out = DA7213_PLL_FREQ_OUT_98304000;
+- return 0;
++ break;
+ case 44100:
+ case 88200:
+ pll_out = DA7213_PLL_FREQ_OUT_90316800;
+- return 0;
++ break;
+ default:
+ dev_err(rtd->dev,"Unsupported samplerate %d\n", samplerate);
+ return -EINVAL;
+ }
++
++ return snd_soc_dai_set_pll(codec_dai, 0, DA7213_SYSCLK_PLL, 0, pll_out);
+ }
+
+ static const struct snd_soc_ops snd_rpi_iqaudio_codec_ops = {
--- /dev/null
+From 31822340129e3c4030500d7f30ce4d19bbf9dd40 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 24 Jul 2023 17:34:47 +0100
+Subject: [PATCH] overlays: Add trickle-voltage-mv parameter to RTCs
+
+The RV3032 RTC requires an additional DT property to enable trickle
+charging. Add a parameter - trickle-voltage-mv - to the i2c-rtc
+and i2c-rtc-gpio overlays to set it.
+
+See: https://github.com/raspberrypi/linux/issues/5547
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 12 ++++++++----
+ arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi | 2 ++
+ 2 files changed, 10 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1957,13 +1957,15 @@ Params: abx80x Select o
+ "schottky" (ABx80x and RV1805 only)
+
+ trickle-resistor-ohms Resistor value for trickle charge (DS1339,
+- ABx80x, RV1805, RV3028)
++ ABx80x, BQ32000, RV1805, RV3028, RV3032)
++
++ trickle-voltage-mv Charge pump voltage for trickle charge (RV3032)
+
+ wakeup-source Specify that the RTC can be used as a wakeup
+ source
+
+ backup-switchover-mode Backup power supply switch mode. Must be 0 for
+- off or 1 for Vdd < VBackup (RV3028 only)
++ off or 1 for Vdd < VBackup (RV3028, RV3032)
+
+
+ Name: i2c-rtc-gpio
+@@ -2027,13 +2029,15 @@ Params: abx80x Select o
+ "schottky" (ABx80x and RV1805 only)
+
+ trickle-resistor-ohms Resistor value for trickle charge (DS1339,
+- ABx80x, RV1805, RV3028)
++ ABx80x, BQ32000, RV1805, RV3028, RV3032)
++
++ trickle-voltage-mv Charge pump voltage for trickle charge (RV3032)
+
+ wakeup-source Specify that the RTC can be used as a wakeup
+ source
+
+ backup-switchover-mode Backup power supply switch mode. Must be 0 for
+- off or 1 for Vdd < VBackup (RV3028 only)
++ off or 1 for Vdd < VBackup (RV3028, RV3032)
+
+ i2c_gpio_sda GPIO used for I2C data (default "23")
+
+--- a/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-rtc-common.dtsi
+@@ -339,8 +339,10 @@
+ <&ds1340>,"trickle-resistor-ohms:0",
+ <&abx80x>,"abracon,tc-resistor:0",
+ <&rv3028>,"trickle-resistor-ohms:0",
++ <&rv3032>,"trickle-resistor-ohms:0",
+ <&rv1805>,"abracon,tc-resistor:0",
+ <&bq32000>,"abracon,tc-resistor:0";
++ trickle-voltage-mv = <&rv3032>,"trickle-voltage-millivolts:0";
+ backup-switchover-mode = <&rv3028>,"backup-switchover-mode:0";
+ wakeup-source = <&ds1339>,"wakeup-source?",
+ <&ds3231>,"wakeup-source?",
--- /dev/null
+From 5fb3b300557d6a6902e7321f42fdabb8c09eef54 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Fri, 28 Jul 2023 12:00:40 +0100
+Subject: [PATCH] drivers: media: imx296: Add standby delay during probe
+
+Add a 2-5ms delay when coming out of standby and before reading the
+sensor info register durning probe, as instructed by the datasheet. This
+standby delay is already present when the sensor starts streaming.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx296.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/i2c/imx296.c
++++ b/drivers/media/i2c/imx296.c
+@@ -1022,6 +1022,8 @@ static int imx296_identify_model(struct
+ return ret;
+ }
+
++ usleep_range(2000, 5000);
++
+ ret = imx296_read(sensor, IMX296_SENSOR_INFO);
+ if (ret < 0) {
+ dev_err(sensor->dev, "failed to read sensor information (%d)\n",
--- /dev/null
+From e1016d61e3dcb058932e8ec5072f2c4bbb05fcb7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Sun, 30 Jul 2023 18:27:03 +0100
+Subject: [PATCH] overlays: Add bmp380 to i2c-sensor overlay
+
+Add support for the BMP380 pressor sensor to the i2c-sensor overlay.
+
+See: https://github.com/raspberrypi/linux/issues/5558
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 7 +++++--
+ .../boot/dts/overlays/i2c-sensor-common.dtsi | 19 ++++++++++++++++++-
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2052,8 +2052,8 @@ Info: Adds support for a number of I2C
+ light level and chemical sensors on i2c_arm
+ Load: dtoverlay=i2c-sensor,<param>=<val>
+ Params: addr Set the address for the BH1750, BME280, BME680,
+- BMP280, CCS811, DS1621, HDC100X, JC42, LM75,
+- MCP980x, MPU6050, MPU9250, MS5637, MS5803,
++ BMP280, BMP380, CCS811, DS1621, HDC100X, JC42,
++ LM75, MCP980x, MPU6050, MPU9250, MS5637, MS5803,
+ MS5805, MS5837, MS8607, SHT3x or TMP102
+
+ aht10 Select the Aosong AHT10 temperature and humidity
+@@ -2075,6 +2075,9 @@ Params: addr Set the
+ bmp280 Select the Bosch Sensortronic BMP280
+ Valid addresses 0x76-0x77, default 0x76
+
++ bmp380 Select the Bosch Sensortronic BMP380
++ Valid addresses 0x76-0x77, default 0x76
++
+ bno055 Select the Bosch Sensortronic BNO055 IMU
+ Valid address 0x28-0x29, default 0x29
+
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
+@@ -493,11 +493,27 @@
+ };
+ };
+
++ fragment@33 {
++ target = <&i2cbus>;
++ __dormant__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ bmp380: bmp380@76 {
++ compatible = "bosch,bmp380";
++ reg = <0x76>;
++ status = "okay";
++ };
++ };
++ };
++
+ __overrides__ {
+ bme280 = <0>,"+0";
+ bmp085 = <0>,"+1";
+ bmp180 = <0>,"+2";
+ bmp280 = <0>,"+3";
++ bmp380 = <0>,"+33";
+ htu21 = <0>,"+4";
+ lm75 = <0>,"+5";
+ lm75addr = <&lm75>,"reg:0";
+@@ -535,7 +551,8 @@
+ <&ms5637>,"reg:0", <&ms5803>,"reg:0", <&ms5805>,"reg:0",
+ <&ms5837>,"reg:0", <&ms8607>,"reg:0",
+ <&mpu6050>,"reg:0", <&mpu9250>,"reg:0",
+- <&bno055>,"reg:0", <&sht4x>,"reg:0";
++ <&bno055>,"reg:0", <&sht4x>,"reg:0",
++ <&bmp380>,"reg:0";
+ int_pin = <&max30102>, "interrupts:0",
+ <&mpu6050>, "interrupts:0",
+ <&mpu9250>, "interrupts:0";
--- /dev/null
+From 4b729a06b15fc5ee3694dcc62346dcb718ae4290 Mon Sep 17 00:00:00 2001
+From: Oliver Hartkopp <socketcan@hartkopp.net>
+Date: Sun, 26 Mar 2023 13:59:11 +0200
+Subject: [PATCH] can: isotp: add module parameter for maximum pdu size
+
+commit 96d1c81e6a0478535342dff6c730adb076cd84e8 upstream.
+
+With ISO 15765-2:2016 the PDU size is not limited to 2^12 - 1 (4095)
+bytes but can be represented as a 32 bit unsigned integer value which
+allows 2^32 - 1 bytes (~4GB). The use-cases like automotive unified
+diagnostic services (UDS) and flashing of ECUs still use the small
+static buffers which are provided at socket creation time.
+
+When a use-case requires to transfer PDUs up to 1025 kByte the maximum
+PDU size can now be extended by setting the module parameter
+max_pdu_size. The extended size buffers are only allocated on a
+per-socket/connection base when needed at run-time.
+
+changes since v2: https://lore.kernel.org/all/20230313172510.3851-1-socketcan@hartkopp.net
+- use ARRAY_SIZE() to reference DEFAULT_MAX_PDU_SIZE only at one place
+
+changes since v1: https://lore.kernel.org/all/20230311143446.3183-1-socketcan@hartkopp.net
+- limit the minimum 'max_pdu_size' to 4095 to maintain the classic
+ behavior before ISO 15765-2:2016
+
+Link: https://github.com/raspberrypi/linux/issues/5371
+Signed-off-by: Oliver Hartkopp <socketcan@hartkopp.net>
+Link: https://lore.kernel.org/all/20230326115911.15094-1-socketcan@hartkopp.net
+Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
+---
+ net/can/isotp.c | 65 ++++++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 56 insertions(+), 9 deletions(-)
+
+--- a/net/can/isotp.c
++++ b/net/can/isotp.c
+@@ -85,10 +85,21 @@ MODULE_ALIAS("can-proto-6");
+
+ /* ISO 15765-2:2016 supports more than 4095 byte per ISO PDU as the FF_DL can
+ * take full 32 bit values (4 Gbyte). We would need some good concept to handle
+- * this between user space and kernel space. For now increase the static buffer
+- * to something about 64 kbyte to be able to test this new functionality.
++ * this between user space and kernel space. For now set the static buffer to
++ * something about 8 kbyte to be able to test this new functionality.
+ */
+-#define MAX_MSG_LENGTH 66000
++#define DEFAULT_MAX_PDU_SIZE 8300
++
++/* maximum PDU size before ISO 15765-2:2016 extension was 4095 */
++#define MAX_12BIT_PDU_SIZE 4095
++
++/* limit the isotp pdu size from the optional module parameter to 1MByte */
++#define MAX_PDU_SIZE (1025 * 1024U)
++
++static unsigned int max_pdu_size __read_mostly = DEFAULT_MAX_PDU_SIZE;
++module_param(max_pdu_size, uint, 0444);
++MODULE_PARM_DESC(max_pdu_size, "maximum isotp pdu size (default "
++ __stringify(DEFAULT_MAX_PDU_SIZE) ")");
+
+ /* N_PCI type values in bits 7-4 of N_PCI bytes */
+ #define N_PCI_SF 0x00 /* single frame */
+@@ -124,13 +135,15 @@ enum {
+ };
+
+ struct tpcon {
+- unsigned int idx;
++ u8 *buf;
++ unsigned int buflen;
+ unsigned int len;
++ unsigned int idx;
+ u32 state;
+ u8 bs;
+ u8 sn;
+ u8 ll_dl;
+- u8 buf[MAX_MSG_LENGTH + 1];
++ u8 sbuf[DEFAULT_MAX_PDU_SIZE];
+ };
+
+ struct isotp_sock {
+@@ -498,7 +511,17 @@ static int isotp_rcv_ff(struct sock *sk,
+ if (so->rx.len + ae + off + ff_pci_sz < so->rx.ll_dl)
+ return 1;
+
+- if (so->rx.len > MAX_MSG_LENGTH) {
++ /* PDU size > default => try max_pdu_size */
++ if (so->rx.len > so->rx.buflen && so->rx.buflen < max_pdu_size) {
++ u8 *newbuf = kmalloc(max_pdu_size, GFP_ATOMIC);
++
++ if (newbuf) {
++ so->rx.buf = newbuf;
++ so->rx.buflen = max_pdu_size;
++ }
++ }
++
++ if (so->rx.len > so->rx.buflen) {
+ /* send FC frame with overflow status */
+ isotp_send_fc(sk, ae, ISOTP_FC_OVFLW);
+ return 1;
+@@ -802,7 +825,7 @@ static void isotp_create_fframe(struct c
+ cf->data[0] = so->opt.ext_address;
+
+ /* create N_PCI bytes with 12/32 bit FF_DL data length */
+- if (so->tx.len > 4095) {
++ if (so->tx.len > MAX_12BIT_PDU_SIZE) {
+ /* use 32 bit FF_DL notation */
+ cf->data[ae] = N_PCI_FF;
+ cf->data[ae + 1] = 0;
+@@ -939,7 +962,17 @@ static int isotp_sendmsg(struct socket *
+ goto err_event_drop;
+ }
+
+- if (!size || size > MAX_MSG_LENGTH) {
++ /* PDU size > default => try max_pdu_size */
++ if (size > so->tx.buflen && so->tx.buflen < max_pdu_size) {
++ u8 *newbuf = kmalloc(max_pdu_size, GFP_KERNEL);
++
++ if (newbuf) {
++ so->tx.buf = newbuf;
++ so->tx.buflen = max_pdu_size;
++ }
++ }
++
++ if (!size || size > so->tx.buflen) {
+ err = -EINVAL;
+ goto err_out_drop;
+ }
+@@ -1194,6 +1227,12 @@ static int isotp_release(struct socket *
+ so->ifindex = 0;
+ so->bound = 0;
+
++ if (so->rx.buf != so->rx.sbuf)
++ kfree(so->rx.buf);
++
++ if (so->tx.buf != so->tx.sbuf)
++ kfree(so->tx.buf);
++
+ sock_orphan(sk);
+ sock->sk = NULL;
+
+@@ -1588,6 +1627,11 @@ static int isotp_init(struct sock *sk)
+ so->rx.state = ISOTP_IDLE;
+ so->tx.state = ISOTP_IDLE;
+
++ so->rx.buf = so->rx.sbuf;
++ so->tx.buf = so->tx.sbuf;
++ so->rx.buflen = ARRAY_SIZE(so->rx.sbuf);
++ so->tx.buflen = ARRAY_SIZE(so->tx.sbuf);
++
+ hrtimer_init(&so->rxtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+ so->rxtimer.function = isotp_rx_timer_handler;
+ hrtimer_init(&so->txtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_SOFT);
+@@ -1670,7 +1714,10 @@ static __init int isotp_module_init(void
+ {
+ int err;
+
+- pr_info("can: isotp protocol\n");
++ max_pdu_size = max_t(unsigned int, max_pdu_size, MAX_12BIT_PDU_SIZE);
++ max_pdu_size = min_t(unsigned int, max_pdu_size, MAX_PDU_SIZE);
++
++ pr_info("can: isotp protocol (max_pdu_size %d)\n", max_pdu_size);
+
+ err = can_proto_register(&isotp_can_proto);
+ if (err < 0)
--- /dev/null
+From e1b03ea9e84320e6bf36a1486abaebbceadd7fc7 Mon Sep 17 00:00:00 2001
+From: Ben Benson <ben.benson@raspberrypi.com>
+Date: Fri, 21 Jul 2023 15:59:51 +0100
+Subject: [PATCH] drivers: media: imx296: Updated imx296 driver for external
+ trigger
+
+Updated imx296 driver to support external trigger mode via XTR pin.
+Added module parameter to control this mode.
+
+Signed-off-by: Ben Benson <ben.benson@raspberrypi.com>
+---
+ drivers/media/i2c/imx296.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/media/i2c/imx296.c
++++ b/drivers/media/i2c/imx296.c
+@@ -20,6 +20,10 @@
+ #include <media/v4l2-fwnode.h>
+ #include <media/v4l2-subdev.h>
+
++static int trigger_mode;
++module_param(trigger_mode, int, 0644);
++MODULE_PARM_DESC(trigger_mode, "Set trigger mode: 0=default, 1=XTRIG");
++
+ #define IMX296_PIXEL_ARRAY_WIDTH 1456
+ #define IMX296_PIXEL_ARRAY_HEIGHT 1088
+
+@@ -645,6 +649,12 @@ static int imx296_stream_on(struct imx29
+
+ imx296_write(sensor, IMX296_CTRL00, 0, &ret);
+ usleep_range(2000, 5000);
++
++ if (trigger_mode == 1) {
++ imx296_write(sensor, IMX296_CTRL0B, IMX296_CTRL0B_TRIGEN, &ret);
++ imx296_write(sensor, IMX296_LOWLAGTRG, IMX296_LOWLAGTRG_FAST, &ret);
++ }
++
+ imx296_write(sensor, IMX296_CTRL0A, 0, &ret);
+
+ /* vflip and hflip cannot change during streaming */
--- /dev/null
+From 74bc238e86e62109c74d8f229dc105bf3818b4a7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 14:35:32 +0100
+Subject: [PATCH] media: dt-bindings: imx258: Fix alternate compatible strings
+
+Multiple compatible strings must appear as an enum.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/media/i2c/imx258.yaml | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/Documentation/devicetree/bindings/media/i2c/imx258.yaml
++++ b/Documentation/devicetree/bindings/media/i2c/imx258.yaml
+@@ -19,8 +19,9 @@ description: |-
+
+ properties:
+ compatible:
+- const: sony,imx258
+- const: sony,imx258-pdaf
++ enum:
++ - sony,imx258
++ - sony,imx258-pdaf
+
+ assigned-clocks: true
+ assigned-clock-parents: true
--- /dev/null
+From 282819aead0166af415b780241dc2def4caee7f4 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:12:01 +0000
+Subject: [PATCH] char: broadcom: vc_mem: Fix preprocessor conditional
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ drivers/char/broadcom/vc_mem.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/char/broadcom/vc_mem.c
++++ b/drivers/char/broadcom/vc_mem.c
+@@ -353,7 +353,7 @@ vc_mem_exit(void)
+ pr_debug("%s: called\n", __func__);
+
+ if (vc_mem_inited) {
+-#if CONFIG_DEBUG_FS
++#ifdef CONFIG_DEBUG_FS
+ vc_mem_debugfs_deinit();
+ #endif
+ device_destroy(vc_mem_class, vc_mem_devnum);
--- /dev/null
+From ec61075a786c455444a1d5df338a41bacfce0bb1 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:23:02 +0000
+Subject: [PATCH] drivers: dwc_otg: Fix fallthrough warnings
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 1 +
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2 +-
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+@@ -2049,6 +2049,7 @@ int fiq_fsm_queue_split_transaction(dwc_
+ } else {
+ st->fsm = FIQ_PER_SSPLIT_QUEUED;
+ }
++ break;
+ default:
+ break;
+ }
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+@@ -402,7 +402,7 @@ int32_t dwc_otg_hcd_handle_rx_status_q_l
+ hc->xfer_count += grxsts.b.bcnt;
+ hc->xfer_buff += grxsts.b.bcnt;
+ }
+-
++ break;
+ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP:
+ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR:
+ case DWC_GRXSTS_PKTSTS_CH_HALTED:
--- /dev/null
+From 2dd2f36d10961e3819ff0525ae2567e601973826 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:29:37 +0000
+Subject: [PATCH] vc04_services/vc-sm-cma: Switch one-bit bitfields to bool
+
+Clang 16 warns:
+
+../drivers/staging/vc04_services/vc-sm-cma/vc_sm.c:816:19: warning: implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1 [-Wsingle-bit-bitfield-constant-conversion]
+ buffer->imported = 1;
+ ^ ~
+../drivers/staging/vc04_services/vc-sm-cma/vc_sm.c:822:17: warning: implicit truncation from 'int' to a one-bit wide bit-field changes value from 1 to -1 [-Wsingle-bit-bitfield-constant-conversion]
+ buffer->in_use = 1;
+ ^ ~
+2 warnings generated.
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ drivers/staging/vc04_services/vc-sm-cma/vc_sm.c | 6 +++---
+ drivers/staging/vc04_services/vc-sm-cma/vc_sm.h | 4 ++--
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c
++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c
+@@ -533,7 +533,7 @@ static void vc_sm_dma_buf_release(struct
+
+ pr_debug("%s dmabuf %p, buffer %p\n", __func__, dmabuf, buffer);
+
+- buffer->in_use = 0;
++ buffer->in_use = false;
+
+ /* Unmap on the VPU */
+ vc_sm_vpu_free(buffer);
+@@ -813,13 +813,13 @@ vc_sm_cma_import_dmabuf_internal(struct
+ buffer->size = import.size;
+ buffer->vpu_state = VPU_MAPPED;
+
+- buffer->imported = 1;
++ buffer->imported = true;
+ buffer->import.dma_buf = dma_buf;
+
+ buffer->import.attach = attach;
+ buffer->import.sgt = sgt;
+ buffer->dma_addr = dma_addr;
+- buffer->in_use = 1;
++ buffer->in_use = true;
+ buffer->kernel_id = import.kernel_id;
+
+ /*
+--- a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h
++++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h
+@@ -57,8 +57,8 @@ struct vc_sm_buffer {
+
+ char name[VC_SM_MAX_NAME_LEN];
+
+- int in_use:1; /* Kernel is still using this resource */
+- int imported:1; /* Imported dmabuf */
++ bool in_use:1; /* Kernel is still using this resource */
++ bool imported:1; /* Imported dmabuf */
+
+ enum vc_sm_vpu_mapping_state vpu_state;
+ u32 vc_handle; /* VideoCore handle for this buffer */
--- /dev/null
+From 3333d45347d313ea589b8b8da1193d342060a946 Mon Sep 17 00:00:00 2001
+From: Alexander Winkowski <dereference23@outlook.com>
+Date: Mon, 3 Jul 2023 18:36:45 +0000
+Subject: [PATCH] media: i2c: ov2311: Fix uninitialized variable usage
+
+Signed-off-by: Alexander Winkowski <dereference23@outlook.com>
+---
+ drivers/media/i2c/ov2311.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/i2c/ov2311.c
++++ b/drivers/media/i2c/ov2311.c
+@@ -1018,7 +1018,7 @@ static int ov2311_check_sensor_id(struct
+ struct i2c_client *client)
+ {
+ struct device *dev = &ov2311->client->dev;
+- u32 id = 0, id_msb;
++ u32 id = 0, id_msb = 0;
+ int ret;
+
+ ret = ov2311_read_reg(client, OV2311_REG_CHIP_ID + 1,
--- /dev/null
+From e89e7655a197d28df49da2be7e2003436cf52197 Mon Sep 17 00:00:00 2001
+From: Ignacio Larrain <ilarrain@gmail.com>
+Date: Tue, 22 Aug 2023 11:11:56 -0400
+Subject: [PATCH] drm/panel: Fix default values for Waveshare 7.9 inch DSI
+ touchscreen (#5565)
+
+This fixes touchscreen calibration, axis swapping and inversion.
+
+As referenced in https://github.com/raspberrypi/linux/issues/5550
+---
+ .../dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-waveshare-panel-overlay.dts
+@@ -93,10 +93,10 @@
+ <&touch>, "touchscreen-size-x:0=800",
+ <&touch>, "touchscreen-size-y:0=480";
+ 7_9_inch = <&panel>, "compatible=waveshare,7.9inch-panel",
+- <&touch>, "touchscreen-size-x:0=400",
+- <&touch>, "touchscreen-size-y:0=1280",
++ <&touch>, "touchscreen-size-x:0=4096",
++ <&touch>, "touchscreen-size-y:0=4096",
+ <&touch>, "touchscreen-inverted-x?",
+- <&touch>, "touchscreen-inverted-y?";
++ <&touch>, "touchscreen-swapped-x-y?";
+ 8_0_inch = <&panel>, "compatible=waveshare,8.0inch-panel",
+ <&touch>, "touchscreen-size-x:0=800",
+ <&touch>, "touchscreen-size-y:0=1280",
--- /dev/null
+From 3fa2fbb7f6e60b85086e454403c5eab1af63b1aa Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 14 Jun 2023 13:43:58 +0100
+Subject: [PATCH] dtoverlays: Add i2c bus overrides to edt-ft5406 overlay
+
+Adds the option for the touch controller to be connected to any
+of the I2C ports.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 17 +++++++++++++++-
+ .../boot/dts/overlays/edt-ft5406-overlay.dts | 20 +++++++++++++++++++
+ arch/arm/boot/dts/overlays/edt-ft5406.dtsi | 9 ++++++++-
+ 3 files changed, 44 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1040,9 +1040,11 @@ Params: dr_mode Dual rol
+
+
+ Name: edt-ft5406
+-Info: Overlay for the EDT FT5406 touchscreen on the CSI/DSI I2C interface.
++Info: Overlay for the EDT FT5406 touchscreen.
+ This works with the Raspberry Pi 7" touchscreen when not being polled
+ by the firmware.
++ By default the overlay uses the i2c_csi_dsi I2C interface, but this
++ can be overridden
+ You MUST use either "disable_touchscreen=1" or "ignore_lcd=1" in
+ config.txt to stop the firmware polling the touchscreen.
+ Load: dtoverlay=edt-ft5406,<param>=<val>
+@@ -1051,6 +1053,19 @@ Params: sizex Touchscr
+ invx Touchscreen inverted x axis
+ invy Touchscreen inverted y axis
+ swapxy Touchscreen swapped x y axis
++ i2c0 Choose the I2C0 bus on GPIOs 0&1
++ i2c1 Choose the I2C1 bus on GPIOs 2&3
++ i2c3 Choose the I2C3 bus (configure with the i2c3
++ overlay - BCM2711 only)
++ i2c4 Choose the I2C4 bus (configure with the i2c4
++ overlay - BCM2711 only)
++ i2c5 Choose the I2C5 bus (configure with the i2c5
++ overlay - BCM2711 only)
++ i2c6 Choose the I2C6 bus (configure with the i2c6
++ overlay - BCM2711 only)
++ addr Sets the address for the touch controller. Note
++ that the device must be configured to use the
++ specified address.
+
+
+ Name: enc28j60
+--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
+@@ -23,4 +23,24 @@
+ status = "okay";
+ };
+ };
++
++ __overrides__ {
++ i2c0 = <&frag13>,"target:0=",<&i2c0>;
++ i2c1 = <&frag13>, "target?=0",
++ <&frag13>, "target-path=i2c1",
++ <0>,"-0-1";
++ i2c3 = <&frag13>, "target?=0",
++ <&frag13>, "target-path=i2c3",
++ <0>,"-0-1";
++ i2c4 = <&frag13>, "target?=0",
++ <&frag13>, "target-path=i2c4",
++ <0>,"-0-1";
++ i2c5 = <&frag13>, "target?=0",
++ <&frag13>, "target-path=i2c5",
++ <0>,"-0-1";
++ i2c6 = <&frag13>, "target?=0",
++ <&frag13>, "target-path=i2c6",
++ <0>,"-0-1";
++ addr = <&ft5406>,"reg:0";
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
+@@ -23,7 +23,7 @@
+ };
+
+ fragment@12 {
+- target = <&i2c_csi_dsi>;
++ target = <&i2cbus>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -37,6 +37,13 @@
+ };
+ };
+
++ frag13: fragment@13 {
++ target = <&i2c_csi_dsi>;
++ i2cbus: __overlay__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ sizex = <&ft5406>,"touchscreen-size-x:0";
+ sizey = <&ft5406>,"touchscreen-size-y:0";
--- /dev/null
+From 9d9586dc0c0deecb90675bd70862fe262f7376ab Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 14 Jun 2023 14:25:21 +0100
+Subject: [PATCH] dtoverlays: Fix README text for i2c-fan
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1800,10 +1800,10 @@ Params: addr Sets the
+ i2c3 Choose the I2C3 bus (configure with the i2c3
+ overlay - BCM2711 only)
+
+- i2c4 Choose the I2C3 bus (configure with the i2c3
++ i2c4 Choose the I2C4 bus (configure with the i2c4
+ overlay - BCM2711 only)
+
+- i2c5 Choose the I2C5 bus (configure with the i2c4
++ i2c5 Choose the I2C5 bus (configure with the i2c5
+ overlay - BCM2711 only)
+
+ i2c6 Choose the I2C6 bus (configure with the i2c6
--- /dev/null
+From e804bd1843236a63815e9acfb1a38ebf9a28ef5b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 31 Aug 2023 16:45:44 +0100
+Subject: [PATCH] drivers: irqchip: irq-bcm2835: Concurrency fix
+
+The commit shown in Fixes: aims to improve interrupt throughput by
+getting the handlers invoked on different CPU cores. It does so (*) by
+using an irq_ack hook to change the interrupt routing.
+
+Unfortunately, the IRQ status bits must be cleared at source, which only
+happens once the interrupt handler has run - there is no easy way for
+one core to claim one of the IRQs before sending the remainder to the
+next core on the list, so waking another core immediately results in a
+race with a chance of both cores handling the same IRQ. It is probably
+for this reason that the routing change is deferred to irq_ack, but that
+doesn't guarantee no clashes - after irq_ack is called, control returns
+to bcm2836_chained_handler_irq which proceeds to check for other pending
+IRQs at a time when the next core is probably doing the same thing.
+
+Since the whole point of the original commit is to distribute the IRQ
+handling, there is no reason to attempt to handle multiple IRQs in one
+interrupt callback, so the problem can be solved (or at least made much
+harder to reproduce) by changing a "while" into an "if", so that each
+invocation only handles one IRQ.
+
+(*) I'm not convinced it's as effective as claimed since irq_ack is
+called _after_ the interrupt handler, but the author thought it made a
+difference.
+
+See: https://github.com/raspberrypi/linux/issues/5214
+ https://github.com/raspberrypi/linux/pull/1794
+
+Fixes: fd4c9785bde8 ("ARM64: Round-Robin dispatch IRQs between CPUs.")
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/irqchip/irq-bcm2835.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/irqchip/irq-bcm2835.c
++++ b/drivers/irqchip/irq-bcm2835.c
+@@ -343,7 +343,8 @@ static void bcm2836_chained_handle_irq(s
+ {
+ u32 hwirq;
+
+- while ((hwirq = get_next_armctrl_hwirq()) != ~0)
++ hwirq = get_next_armctrl_hwirq();
++ if (hwirq != ~0)
+ generic_handle_domain_irq(intc.domain, hwirq);
+ }
+
--- /dev/null
+From 5e54398e1b61335883dff1be46a6c8b3ca973926 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 30 Aug 2023 18:03:37 +0100
+Subject: [PATCH] dtoverlays: Add drm option to piscreen overlay
+
+Adds the option of selecting the DRM/KMS TinyDRM driver for
+this panel, rather than the deprecated FBTFT one.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 3 +++
+ arch/arm/boot/dts/overlays/piscreen-overlay.dts | 10 +++++++---
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3245,6 +3245,9 @@ Params: speed Display
+
+ xohms Touchpanel sensitivity (X-plate resistance)
+
++ drm Select the DRM/KMS driver instead of the FBTFT
++ one
++
+
+ Name: piscreen2r
+ Info: PiScreen 2 with resistive TP display by OzzMaker.com
+--- a/arch/arm/boot/dts/overlays/piscreen-overlay.dts
++++ b/arch/arm/boot/dts/overlays/piscreen-overlay.dts
+@@ -6,6 +6,8 @@
+ /dts-v1/;
+ /plugin/;
+
++#include <dt-bindings/gpio/gpio.h>
++
+ / {
+ compatible = "brcm,bcm2835";
+
+@@ -59,9 +61,9 @@
+ fps = <30>;
+ buswidth = <8>;
+ regwidth = <16>;
+- reset-gpios = <&gpio 25 1>;
+- dc-gpios = <&gpio 24 0>;
+- led-gpios = <&gpio 22 0>;
++ reset-gpios = <&gpio 25 GPIO_ACTIVE_LOW>;
++ dc-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>;
++ led-gpios = <&gpio 22 GPIO_ACTIVE_HIGH>;
+ debug = <0>;
+
+ init = <0x10000b0 0x00
+@@ -98,5 +100,7 @@
+ fps = <&piscreen>,"fps:0";
+ debug = <&piscreen>,"debug:0";
+ xohms = <&piscreen_ts>,"ti,x-plate-ohms;0";
++ drm = <&piscreen>,"compatible=waveshare,rpi-lcd-35",
++ <&piscreen>,"reset-gpios:8=",<GPIO_ACTIVE_HIGH>;
+ };
+ };
--- /dev/null
+From f59fe2d1bd056af117eb512bb0e9210a943c6d47 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 1 Sep 2023 12:17:38 +0100
+Subject: [PATCH] drm/ili9486: Resolve clash in spi_device_id names
+
+For "Really Good Reasons" [1] the SPI core requires a match
+between compatible device strings and the name in spi_device_id.
+
+The ili9486 driver uses compatible strings "waveshare,rpi-lcd-35"
+and "ozzmaker,piscreen", but "rpi-lcd-35" and "piscreen" are missing,
+so add them.
+
+Compatible string "ilitek,ili9486" is already used by
+staging/fbtft/fb_ili9486, therefore leaving it present in ili9486 as an
+spi_device_id causes the incorrect module to be loaded, therefore remove
+this id.
+
+[1] https://elixir.bootlin.com/linux/latest/source/drivers/spi/spi.c#L487
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/tiny/ili9486.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/tiny/ili9486.c
++++ b/drivers/gpu/drm/tiny/ili9486.c
+@@ -187,7 +187,8 @@ static const struct of_device_id ili9486
+ MODULE_DEVICE_TABLE(of, ili9486_of_match);
+
+ static const struct spi_device_id ili9486_id[] = {
+- { "ili9486", 0 },
++ { "rpi-lcd-35", 0 },
++ { "piscreen", 0 },
+ { }
+ };
+ MODULE_DEVICE_TABLE(spi, ili9486_id);
--- /dev/null
+From 50c5a8558f4aaa54a3c4f5a8c2b6053f641d94eb Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 1 Sep 2023 12:23:30 +0100
+Subject: [PATCH] input: ads7846: Add missing spi_device_id strings
+
+The SPI core logs error messages if a compatible string device
+name is not also present as an spi_device_id.
+
+No spi_device_id values are specified by the driver, therefore
+we get 4 log lines every time it is loaded:
+SPI driver ads7846 has no spi_device_id for ti,tsc2046
+SPI driver ads7846 has no spi_device_id for ti,ads7843
+SPI driver ads7846 has no spi_device_id for ti,ads7845
+SPI driver ads7846 has no spi_device_id for ti,ads7873
+
+Add the spi_device_id values for these devices.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/ads7846.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/input/touchscreen/ads7846.c
++++ b/drivers/input/touchscreen/ads7846.c
+@@ -1127,6 +1127,17 @@ static const struct of_device_id ads7846
+ };
+ MODULE_DEVICE_TABLE(of, ads7846_dt_ids);
+
++static const struct spi_device_id ads7846_spi_ids[] = {
++ { "tsc2046", 0 },
++ { "ads7843", 0 },
++ { "ads7843", 0 },
++ { "ads7845", 0 },
++ { "ads7846", 0 },
++ { "ads7873", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(spi, ads7846_spi_ids);
++
+ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
+ {
+ struct ads7846_platform_data *pdata;
+@@ -1424,6 +1435,7 @@ static struct spi_driver ads7846_driver
+ .pm = &ads7846_pm,
+ .of_match_table = of_match_ptr(ads7846_dt_ids),
+ },
++ .id_table = ads7846_spi_ids,
+ .probe = ads7846_probe,
+ .remove = ads7846_remove,
+ };
--- /dev/null
+From 65742d7116e89b08858fcd7d67bd521ee19ee837 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 30 Aug 2023 18:05:43 +0100
+Subject: [PATCH] staging: bcm2835-codec: Downgrade the level for a debug
+ message
+
+The debug message from bcm2835_codec_buf_prepare when the buffer
+size is incorrect can be a little spammy if the application isn't
+careful on how it drives it, therefore drop the priority of the
+message.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -2883,7 +2883,7 @@ static int bcm2835_codec_buf_prepare(str
+ }
+
+ if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
+- v4l2_err(&ctx->dev->v4l2_dev, "%s data will not fit into plane (%lu < %lu)\n",
++ v4l2_dbg(1, debug, &ctx->dev->v4l2_dev, "%s data will not fit into plane (%lu < %lu)\n",
+ __func__, vb2_plane_size(vb, 0),
+ (long)q_data->sizeimage);
+ return -EINVAL;
--- /dev/null
+From cee471c3ada3215d6dfc53fb0f1b97548444dea7 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 5 Sep 2023 11:56:19 +0100
+Subject: [PATCH] gpio-fsm: Sort functions into a more logical order
+
+Move some functions into a more logical ordering. This change causes
+no functional change and is essentially cosmetic.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/gpio/gpio-fsm.c | 245 ++++++++++++++++++++--------------------
+ 1 file changed, 125 insertions(+), 120 deletions(-)
+
+--- a/drivers/gpio/gpio-fsm.c
++++ b/drivers/gpio/gpio-fsm.c
+@@ -193,131 +193,14 @@ static void free_symbols(struct symtab_e
+ }
+ }
+
+-static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off)
+-{
+- struct gpio_fsm *gf = gpiochip_get_data(gc);
+- struct soft_gpio *sg;
+-
+- if (off >= gf->num_soft_gpios)
+- return -EINVAL;
+- sg = &gf->soft_gpios[off];
+-
+- return sg->dir;
+-}
+-
+-static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off)
+-{
+- struct gpio_fsm *gf = gpiochip_get_data(gc);
+- struct soft_gpio *sg;
+-
+- if (off >= gf->num_soft_gpios)
+- return -EINVAL;
+- sg = &gf->soft_gpios[off];
+-
+- return sg->value;
+-}
+-
+ static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
+- struct fsm_state *new_state)
+-{
+- struct input_gpio_state *inp_state;
+- struct gpio_event *gp_ev;
+- struct fsm_state *state;
+- int i;
+-
+- dev_dbg(gf->dev, "go_to_state(%s)\n",
+- new_state ? new_state->name : "<unset>");
+-
+- spin_lock(&gf->spinlock);
+-
+- if (gf->next_state) {
+- /* Something else has already requested a transition */
+- spin_unlock(&gf->spinlock);
+- return;
+- }
+-
+- gf->next_state = new_state;
+- state = gf->current_state;
+- gf->delay_target_state = NULL;
+-
+- if (state) {
+- /* Disarm any GPIO IRQs */
+- for (i = 0; i < state->num_gpio_events; i++) {
+- gp_ev = &state->gpio_events[i];
+- inp_state = &gf->input_gpio_states[gp_ev->index];
+- inp_state->target = NULL;
+- }
+- }
+-
+- spin_unlock(&gf->spinlock);
+-
+- if (new_state)
+- schedule_work(&gf->work);
+-}
++ struct fsm_state *new_state);
+
+ static void gpio_fsm_set_soft(struct gpio_fsm *gf,
+- unsigned int off, int val)
+-{
+- struct soft_gpio *sg = &gf->soft_gpios[off];
+- struct gpio_event *gp_ev;
+- struct fsm_state *state;
+- int i;
+-
+- dev_dbg(gf->dev, "set(%d,%d)\n", off, val);
+- state = gf->current_state;
+- sg->value = val;
+- for (i = 0; i < state->num_soft_events; i++) {
+- gp_ev = &state->soft_events[i];
+- if (gp_ev->index == off && gp_ev->value == val) {
+- if (gf->debug)
+- dev_info(gf->dev,
+- "GF_SOFT %d->%d -> %s\n", gp_ev->index,
+- gp_ev->value, gp_ev->target->name);
+- gpio_fsm_go_to_state(gf, gp_ev->target);
+- break;
+- }
+- }
+-}
+-
+-static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off)
+-{
+- struct gpio_fsm *gf = gpiochip_get_data(gc);
+- struct soft_gpio *sg;
+-
+- if (off >= gf->num_soft_gpios)
+- return -EINVAL;
+- sg = &gf->soft_gpios[off];
+- sg->dir = GPIOF_DIR_IN;
+-
+- return 0;
+-}
+-
+-static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off,
+- int value)
+-{
+- struct gpio_fsm *gf = gpiochip_get_data(gc);
+- struct soft_gpio *sg;
+-
+- if (off >= gf->num_soft_gpios)
+- return -EINVAL;
+- sg = &gf->soft_gpios[off];
+- sg->dir = GPIOF_DIR_OUT;
+- gpio_fsm_set_soft(gf, off, value);
+-
+- return 0;
+-}
+-
+-static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val)
+-{
+- struct gpio_fsm *gf;
+-
+- gf = gpiochip_get_data(gc);
+- if (off < gf->num_soft_gpios)
+- gpio_fsm_set_soft(gf, off, val);
+-}
++ unsigned int off, int val);
+
+ static void gpio_fsm_enter_state(struct gpio_fsm *gf,
+- struct fsm_state *state)
++ struct fsm_state *state)
+ {
+ struct input_gpio_state *inp_state;
+ struct output_signal *signal;
+@@ -431,6 +314,44 @@ static void gpio_fsm_enter_state(struct
+ }
+ }
+
++static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
++ struct fsm_state *new_state)
++{
++ struct input_gpio_state *inp_state;
++ struct gpio_event *gp_ev;
++ struct fsm_state *state;
++ int i;
++
++ dev_dbg(gf->dev, "go_to_state(%s)\n",
++ new_state ? new_state->name : "<unset>");
++
++ spin_lock(&gf->spinlock);
++
++ if (gf->next_state) {
++ /* Something else has already requested a transition */
++ spin_unlock(&gf->spinlock);
++ return;
++ }
++
++ gf->next_state = new_state;
++ state = gf->current_state;
++ gf->delay_target_state = NULL;
++
++ if (state) {
++ /* Disarm any GPIO IRQs */
++ for (i = 0; i < state->num_gpio_events; i++) {
++ gp_ev = &state->gpio_events[i];
++ inp_state = &gf->input_gpio_states[gp_ev->index];
++ inp_state->target = NULL;
++ }
++ }
++
++ spin_unlock(&gf->spinlock);
++
++ if (new_state)
++ schedule_work(&gf->work);
++}
++
+ static void gpio_fsm_work(struct work_struct *work)
+ {
+ struct input_gpio_state *inp_state;
+@@ -851,6 +772,90 @@ static int resolve_sym_to_state(struct g
+ return 0;
+ }
+
++static void gpio_fsm_set_soft(struct gpio_fsm *gf,
++ unsigned int off, int val)
++{
++ struct soft_gpio *sg = &gf->soft_gpios[off];
++ struct gpio_event *gp_ev;
++ struct fsm_state *state;
++ int i;
++
++ dev_dbg(gf->dev, "set(%d,%d)\n", off, val);
++ state = gf->current_state;
++ sg->value = val;
++ for (i = 0; i < state->num_soft_events; i++) {
++ gp_ev = &state->soft_events[i];
++ if (gp_ev->index == off && gp_ev->value == val) {
++ if (gf->debug)
++ dev_info(gf->dev,
++ "GF_SOFT %d->%d -> %s\n", gp_ev->index,
++ gp_ev->value, gp_ev->target->name);
++ gpio_fsm_go_to_state(gf, gp_ev->target);
++ break;
++ }
++ }
++}
++
++static int gpio_fsm_get(struct gpio_chip *gc, unsigned int off)
++{
++ struct gpio_fsm *gf = gpiochip_get_data(gc);
++ struct soft_gpio *sg;
++
++ if (off >= gf->num_soft_gpios)
++ return -EINVAL;
++ sg = &gf->soft_gpios[off];
++
++ return sg->value;
++}
++
++static void gpio_fsm_set(struct gpio_chip *gc, unsigned int off, int val)
++{
++ struct gpio_fsm *gf;
++
++ gf = gpiochip_get_data(gc);
++ if (off < gf->num_soft_gpios)
++ gpio_fsm_set_soft(gf, off, val);
++}
++
++static int gpio_fsm_get_direction(struct gpio_chip *gc, unsigned int off)
++{
++ struct gpio_fsm *gf = gpiochip_get_data(gc);
++ struct soft_gpio *sg;
++
++ if (off >= gf->num_soft_gpios)
++ return -EINVAL;
++ sg = &gf->soft_gpios[off];
++
++ return sg->dir;
++}
++
++static int gpio_fsm_direction_input(struct gpio_chip *gc, unsigned int off)
++{
++ struct gpio_fsm *gf = gpiochip_get_data(gc);
++ struct soft_gpio *sg;
++
++ if (off >= gf->num_soft_gpios)
++ return -EINVAL;
++ sg = &gf->soft_gpios[off];
++ sg->dir = GPIOF_DIR_IN;
++
++ return 0;
++}
++
++static int gpio_fsm_direction_output(struct gpio_chip *gc, unsigned int off,
++ int value)
++{
++ struct gpio_fsm *gf = gpiochip_get_data(gc);
++ struct soft_gpio *sg;
++
++ if (off >= gf->num_soft_gpios)
++ return -EINVAL;
++ sg = &gf->soft_gpios[off];
++ sg->dir = GPIOF_DIR_OUT;
++ gpio_fsm_set_soft(gf, off, value);
++
++ return 0;
++}
+
+ /*
+ * /sys/class/gpio-fsm/<fsm-name>/
--- /dev/null
+From f0061ffc98c6e027c5774e2a24ceadcfee4167ea Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 5 Sep 2023 12:01:13 +0100
+Subject: [PATCH] gpio_fsm: Rework the atomic-vs-non-atomic split
+
+Partition the code to separate atomic and non-atomic methods so that
+none of them have to handle both cases. The result avoids using deferred
+work unless necessary, and should be easier to understand.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/gpio/gpio-fsm.c | 84 ++++++++++++++++++++---------------------
+ 1 file changed, 41 insertions(+), 43 deletions(-)
+
+--- a/drivers/gpio/gpio-fsm.c
++++ b/drivers/gpio/gpio-fsm.c
+@@ -193,9 +193,6 @@ static void free_symbols(struct symtab_e
+ }
+ }
+
+-static void gpio_fsm_go_to_state(struct gpio_fsm *gf,
+- struct fsm_state *new_state);
+-
+ static void gpio_fsm_set_soft(struct gpio_fsm *gf,
+ unsigned int off, int val);
+
+@@ -213,6 +210,7 @@ static void gpio_fsm_enter_state(struct
+ dev_dbg(gf->dev, "enter_state(%s)\n", state->name);
+
+ gf->current_state = state;
++ gf->delay_target_state = NULL;
+
+ // 1. Apply any listed signals
+ for (i = 0; i < state->num_signals; i++) {
+@@ -271,7 +269,7 @@ static void gpio_fsm_enter_state(struct
+ dev_info(gf->dev,
+ "GF_SOFT %d=%d -> %s\n", event->index,
+ event->value, event->target->name);
+- gpio_fsm_go_to_state(gf, event->target);
++ gpio_fsm_enter_state(gf, event->target);
+ return;
+ }
+ }
+@@ -284,7 +282,7 @@ static void gpio_fsm_enter_state(struct
+ inp_state->value = event->value;
+ inp_state->enabled = true;
+
+- value = gpiod_get_value(gf->input_gpios->desc[event->index]);
++ value = gpiod_get_value_cansleep(gf->input_gpios->desc[event->index]);
+
+ // Clear stale event state
+ disable_irq(inp_state->irq);
+@@ -299,7 +297,7 @@ static void gpio_fsm_enter_state(struct
+ dev_info(gf->dev,
+ "GF_IN %d=%d -> %s\n", event->index,
+ event->value, event->target->name);
+- gpio_fsm_go_to_state(gf, event->target);
++ gpio_fsm_enter_state(gf, event->target);
+ return;
+ }
+ }
+@@ -325,6 +323,33 @@ static void gpio_fsm_go_to_state(struct
+ dev_dbg(gf->dev, "go_to_state(%s)\n",
+ new_state ? new_state->name : "<unset>");
+
++ state = gf->current_state;
++
++ /* Disable any enabled GPIO IRQs */
++ for (i = 0; i < state->num_gpio_events; i++) {
++ gp_ev = &state->gpio_events[i];
++ inp_state = &gf->input_gpio_states[gp_ev->index];
++ if (inp_state->enabled) {
++ inp_state->enabled = false;
++ irq_set_irq_type(inp_state->irq,
++ IRQF_TRIGGER_NONE);
++ }
++ }
++
++ gpio_fsm_enter_state(gf, new_state);
++}
++
++static void gpio_fsm_go_to_state_deferred(struct gpio_fsm *gf,
++ struct fsm_state *new_state)
++{
++ struct input_gpio_state *inp_state;
++ struct gpio_event *gp_ev;
++ struct fsm_state *state;
++ int i;
++
++ dev_dbg(gf->dev, "go_to_state_deferred(%s)\n",
++ new_state ? new_state->name : "<unset>");
++
+ spin_lock(&gf->spinlock);
+
+ if (gf->next_state) {
+@@ -335,57 +360,31 @@ static void gpio_fsm_go_to_state(struct
+
+ gf->next_state = new_state;
+ state = gf->current_state;
+- gf->delay_target_state = NULL;
+
+- if (state) {
+- /* Disarm any GPIO IRQs */
+- for (i = 0; i < state->num_gpio_events; i++) {
+- gp_ev = &state->gpio_events[i];
+- inp_state = &gf->input_gpio_states[gp_ev->index];
+- inp_state->target = NULL;
+- }
++ /* Disarm any GPIO IRQs */
++ for (i = 0; i < state->num_gpio_events; i++) {
++ gp_ev = &state->gpio_events[i];
++ inp_state = &gf->input_gpio_states[gp_ev->index];
++ inp_state->target = NULL;
+ }
+
+ spin_unlock(&gf->spinlock);
+
+- if (new_state)
+- schedule_work(&gf->work);
++ schedule_work(&gf->work);
+ }
+
+ static void gpio_fsm_work(struct work_struct *work)
+ {
+- struct input_gpio_state *inp_state;
+ struct fsm_state *new_state;
+- struct fsm_state *state;
+- struct gpio_event *gp_ev;
+ struct gpio_fsm *gf;
+- int i;
+
+ gf = container_of(work, struct gpio_fsm, work);
+ spin_lock(&gf->spinlock);
+- state = gf->current_state;
+ new_state = gf->next_state;
+- if (!new_state)
+- new_state = gf->delay_target_state;
+ gf->next_state = NULL;
+- gf->delay_target_state = NULL;
+ spin_unlock(&gf->spinlock);
+
+- if (state) {
+- /* Disable any enabled GPIO IRQs */
+- for (i = 0; i < state->num_gpio_events; i++) {
+- gp_ev = &state->gpio_events[i];
+- inp_state = &gf->input_gpio_states[gp_ev->index];
+- if (inp_state->enabled) {
+- inp_state->enabled = false;
+- irq_set_irq_type(inp_state->irq,
+- IRQF_TRIGGER_NONE);
+- }
+- }
+- }
+-
+- if (new_state)
+- gpio_fsm_enter_state(gf, new_state);
++ gpio_fsm_go_to_state(gf, new_state);
+ }
+
+ static irqreturn_t gpio_fsm_gpio_irq_handler(int irq, void *dev_id)
+@@ -404,7 +403,7 @@ static irqreturn_t gpio_fsm_gpio_irq_han
+ if (gf->debug)
+ dev_info(gf->dev, "GF_IN %d->%d -> %s\n",
+ inp_state->index, inp_state->value, target->name);
+- gpio_fsm_go_to_state(gf, target);
++ gpio_fsm_go_to_state_deferred(gf, target);
+ return IRQ_HANDLED;
+ }
+
+@@ -416,12 +415,11 @@ static void gpio_fsm_timer(struct timer_
+ target = gf->delay_target_state;
+ if (!target)
+ return;
+-
+ if (gf->debug)
+ dev_info(gf->dev, "GF_DELAY %d -> %s\n", gf->delay_ms,
+ target->name);
+
+- gpio_fsm_go_to_state(gf, target);
++ gpio_fsm_go_to_state_deferred(gf, target);
+ }
+
+ int gpio_fsm_parse_signals(struct gpio_fsm *gf, struct fsm_state *state,
+@@ -1119,7 +1117,7 @@ static int gpio_fsm_probe(struct platfor
+ if (gf->debug)
+ dev_info(gf->dev, "Start -> %s\n", gf->start_state->name);
+
+- gpio_fsm_go_to_state(gf, gf->start_state);
++ gpio_fsm_enter_state(gf, gf->start_state);
+
+ return devm_gpiochip_add_data(dev, &gf->gc, gf);
+ }
--- /dev/null
+From bf9fb25f3265605572f04e5c7836bb83ee345236 Mon Sep 17 00:00:00 2001
+From: Chao Yu <chao@kernel.org>
+Date: Fri, 30 Dec 2022 23:43:32 +0800
+Subject: [PATCH] f2fs: fix to avoid NULL pointer dereference in
+ f2fs_issue_flush()
+
+commit b3d83066cbebc76dbac8a5fca931f64b4c6fff34 upstream.
+
+With below two cases, it will cause NULL pointer dereference when
+accessing SM_I(sbi)->fcc_info in f2fs_issue_flush().
+
+a) If kthread_run() fails in f2fs_create_flush_cmd_control(), it will
+release SM_I(sbi)->fcc_info,
+
+- mount -o noflush_merge /dev/vda /mnt/f2fs
+- mount -o remount,flush_merge /dev/vda /mnt/f2fs -- kthread_run() fails
+- dd if=/dev/zero of=/mnt/f2fs/file bs=4k count=1 conv=fsync
+
+b) we will never allocate memory for SM_I(sbi)->fcc_info w/ below
+testcase,
+
+- mount -o ro /dev/vda /mnt/f2fs
+- mount -o rw,remount /dev/vda /mnt/f2fs
+- dd if=/dev/zero of=/mnt/f2fs/file bs=4k count=1 conv=fsync
+
+In order to fix this issue, let change as below:
+- fix error path handling in f2fs_create_flush_cmd_control().
+- allocate SM_I(sbi)->fcc_info even if readonly is on.
+
+Signed-off-by: Chao Yu <chao@kernel.org>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+---
+ fs/f2fs/segment.c | 12 ++++--------
+ 1 file changed, 4 insertions(+), 8 deletions(-)
+
+--- a/fs/f2fs/segment.c
++++ b/fs/f2fs/segment.c
+@@ -663,9 +663,7 @@ init_thread:
+ "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev));
+ if (IS_ERR(fcc->f2fs_issue_flush)) {
+ err = PTR_ERR(fcc->f2fs_issue_flush);
+- kfree(fcc);
+- SM_I(sbi)->fcc_info = NULL;
+- return err;
++ fcc->f2fs_issue_flush = NULL;
+ }
+
+ return err;
+@@ -5062,11 +5060,9 @@ int f2fs_build_segment_manager(struct f2
+
+ init_f2fs_rwsem(&sm_info->curseg_lock);
+
+- if (!f2fs_readonly(sbi->sb)) {
+- err = f2fs_create_flush_cmd_control(sbi);
+- if (err)
+- return err;
+- }
++ err = f2fs_create_flush_cmd_control(sbi);
++ if (err)
++ return err;
+
+ err = create_discard_cmd_control(sbi);
+ if (err)
--- /dev/null
+From e079555a4c68356e58249cfc041b28f6eb455bd5 Mon Sep 17 00:00:00 2001
+From: Stefan Wahren <wahrenst@gmx.net>
+Date: Sat, 4 May 2019 17:06:15 +0200
+Subject: [PATCH] hwrng: iproc-rng200: Add BCM2838 support
+
+The HWRNG on the BCM2838 is compatible to iproc-rng200, so add the
+support to this driver instead of bcm2835-rng.
+
+Signed-off-by: Stefan Wahren <wahrenst@gmx.net>
+
+hwrng: iproc-rng200: Correct SoC name
+
+The Pi 4 SoC is called BCM2711, not BCM2838.
+
+Fixes: "hwrng: iproc-rng200: Add BCM2838 support"
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/char/hw_random/Kconfig | 2 +-
+ drivers/char/hw_random/iproc-rng200.c | 79 ++++++++++++++++++++++++++-
+ 2 files changed, 77 insertions(+), 4 deletions(-)
+
+--- a/drivers/char/hw_random/Kconfig
++++ b/drivers/char/hw_random/Kconfig
+@@ -104,7 +104,7 @@ config HW_RANDOM_IPROC_RNG200
+ default HW_RANDOM
+ help
+ This driver provides kernel-side support for the RNG200
+- hardware found on the Broadcom iProc and STB SoCs.
++ hardware found on the Broadcom iProc, BCM2711 and STB SoCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called iproc-rng200
+--- a/drivers/char/hw_random/iproc-rng200.c
++++ b/drivers/char/hw_random/iproc-rng200.c
+@@ -14,6 +14,7 @@
+ #include <linux/module.h>
+ #include <linux/of_address.h>
+ #include <linux/of_platform.h>
++#include <linux/of.h>
+ #include <linux/platform_device.h>
+ #include <linux/delay.h>
+
+@@ -21,6 +22,7 @@
+ #define RNG_CTRL_OFFSET 0x00
+ #define RNG_CTRL_RNG_RBGEN_MASK 0x00001FFF
+ #define RNG_CTRL_RNG_RBGEN_ENABLE 0x00000001
++#define RNG_CTRL_RNG_DIV_CTRL_SHIFT 13
+
+ #define RNG_SOFT_RESET_OFFSET 0x04
+ #define RNG_SOFT_RESET 0x00000001
+@@ -28,16 +30,23 @@
+ #define RBG_SOFT_RESET_OFFSET 0x08
+ #define RBG_SOFT_RESET 0x00000001
+
++#define RNG_TOTAL_BIT_COUNT_OFFSET 0x0C
++
++#define RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET 0x10
++
+ #define RNG_INT_STATUS_OFFSET 0x18
+ #define RNG_INT_STATUS_MASTER_FAIL_LOCKOUT_IRQ_MASK 0x80000000
+ #define RNG_INT_STATUS_STARTUP_TRANSITIONS_MET_IRQ_MASK 0x00020000
+ #define RNG_INT_STATUS_NIST_FAIL_IRQ_MASK 0x00000020
+ #define RNG_INT_STATUS_TOTAL_BITS_COUNT_IRQ_MASK 0x00000001
+
++#define RNG_INT_ENABLE_OFFSET 0x1C
++
+ #define RNG_FIFO_DATA_OFFSET 0x20
+
+ #define RNG_FIFO_COUNT_OFFSET 0x24
+ #define RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK 0x000000FF
++#define RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT 8
+
+ struct iproc_rng200_dev {
+ struct hwrng rng;
+@@ -158,6 +167,64 @@ static int iproc_rng200_init(struct hwrn
+ return 0;
+ }
+
++static int bcm2711_rng200_read(struct hwrng *rng, void *buf, size_t max,
++ bool wait)
++{
++ struct iproc_rng200_dev *priv = to_rng_priv(rng);
++ u32 max_words = max / sizeof(u32);
++ u32 num_words, count, val;
++
++ /* ensure warm up period has elapsed */
++ while (1) {
++ val = ioread32(priv->base + RNG_TOTAL_BIT_COUNT_OFFSET);
++ if (val > 16)
++ break;
++ cpu_relax();
++ }
++
++ /* ensure fifo is not empty */
++ while (1) {
++ num_words = ioread32(priv->base + RNG_FIFO_COUNT_OFFSET) &
++ RNG_FIFO_COUNT_RNG_FIFO_COUNT_MASK;
++ if (num_words)
++ break;
++ if (!wait)
++ return 0;
++ cpu_relax();
++ }
++
++ if (num_words > max_words)
++ num_words = max_words;
++
++ for (count = 0; count < num_words; count++) {
++ ((u32 *)buf)[count] = ioread32(priv->base +
++ RNG_FIFO_DATA_OFFSET);
++ }
++
++ return num_words * sizeof(u32);
++}
++
++static int bcm2711_rng200_init(struct hwrng *rng)
++{
++ struct iproc_rng200_dev *priv = to_rng_priv(rng);
++ uint32_t val;
++
++ if (ioread32(priv->base + RNG_CTRL_OFFSET) & RNG_CTRL_RNG_RBGEN_MASK)
++ return 0;
++
++ /* initial numbers generated are "less random" so will be discarded */
++ val = 0x40000;
++ iowrite32(val, priv->base + RNG_TOTAL_BIT_COUNT_THRESHOLD_OFFSET);
++ /* min fifo count to generate full interrupt */
++ val = 2 << RNG_FIFO_COUNT_RNG_FIFO_THRESHOLD_SHIFT;
++ iowrite32(val, priv->base + RNG_FIFO_COUNT_OFFSET);
++ /* enable the rng - 1Mhz sample rate */
++ val = (0x3 << RNG_CTRL_RNG_DIV_CTRL_SHIFT) | RNG_CTRL_RNG_RBGEN_MASK;
++ iowrite32(val, priv->base + RNG_CTRL_OFFSET);
++
++ return 0;
++}
++
+ static void iproc_rng200_cleanup(struct hwrng *rng)
+ {
+ struct iproc_rng200_dev *priv = to_rng_priv(rng);
+@@ -184,11 +251,17 @@ static int iproc_rng200_probe(struct pla
+
+ dev_set_drvdata(dev, priv);
+
+- priv->rng.name = "iproc-rng200";
+- priv->rng.read = iproc_rng200_read;
+- priv->rng.init = iproc_rng200_init;
++ priv->rng.name = pdev->name;
+ priv->rng.cleanup = iproc_rng200_cleanup;
+
++ if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-rng200")) {
++ priv->rng.init = bcm2711_rng200_init;
++ priv->rng.read = bcm2711_rng200_read;
++ } else {
++ priv->rng.init = iproc_rng200_init;
++ priv->rng.read = iproc_rng200_read;
++ }
++
+ /* Register driver */
+ ret = devm_hwrng_register(dev, &priv->rng);
+ if (ret) {
--- /dev/null
+From 6f634d7efb8876e5953c30c0a613aaa5f575fe05 Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <jim2101024@gmail.com>
+Date: Tue, 11 Oct 2022 14:42:07 -0400
+Subject: [PATCH] PCI: brcmstb: Wait for 100ms following PERST# deassert
+
+commit 3ae140ad827b359bc4fa7c7985691c4c1e3ca8f4 upstream.
+
+Be prudent and give some time for power and clocks to become stable. As
+described in the PCIe CEM specification sections 2.2 and 2.2.1; as well as
+PCIe r5.0, 6.6.1.
+
+Link: https://lore.kernel.org/r/20221011184211.18128-3-jim2101024@gmail.com
+Signed-off-by: Jim Quinlan <jim2101024@gmail.com>
+Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
+Acked-by: Florian Fainelli <f.fainelli@gmail.com>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -1038,8 +1038,15 @@ static int brcm_pcie_start_link(struct b
+ pcie->perst_set(pcie, 0);
+
+ /*
+- * Give the RC/EP time to wake up, before trying to configure RC.
+- * Intermittently check status for link-up, up to a total of 100ms.
++ * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
++ * sections 2.2, PCIe r5.0, 6.6.1.
++ */
++ msleep(100);
++
++ /*
++ * Give the RC/EP even more time to wake up, before trying to
++ * configure RC. Intermittently check status for link-up, up to a
++ * total of 100ms.
+ */
+ for (i = 0; i < 100 && !brcm_pcie_link_up(pcie); i += 5)
+ msleep(5);
--- /dev/null
+From cc08810f89e52337a99cc6ae5f53f08588357c5f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Sep 2023 20:31:34 +0100
+Subject: [PATCH] overlays: Add a sample hat_map
+
+The HAT map is way of associating named overlays with HATs whose
+EEPROMs were programmed with the contents of the overlay.
+Unfortunately, change in the DT and kernel drivers has meant that some
+of these embedded overlays no longer function, or even don't apply.
+
+The HAT map is a mapping from HAT UUIDs to overlay names. If a HAT with
+a listed UUID is detected, the embedded overlay is ignored and the
+overlay named in the mapping is loaded in its place.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 2 +-
+ arch/arm/boot/dts/overlays/hat_map.dts | 13 +++++++++++++
+ 2 files changed, 14 insertions(+), 1 deletion(-)
+ create mode 100644 arch/arm/boot/dts/overlays/hat_map.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -1,6 +1,6 @@
+ # Overlays for the Raspberry Pi platform
+
+-dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb
++dtb-$(CONFIG_ARCH_BCM2835) += overlay_map.dtb hat_map.dtb
+
+ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ act-led.dtbo \
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/hat_map.dts
+@@ -0,0 +1,13 @@
++/dts-v1/;
++
++/ {
++ iqaudio-pi-codecplus {
++ uuid = [ dc1c9594 c1ab 4c6c acda a88dc59a3c5b ];
++ overlay = "iqaudio-codec";
++ };
++
++ recalbox-rgbdual {
++ uuid = [ 1c955808 681f 4bbc a2ef b7ea47cd388e ];
++ overlay = "recalboxrgbdual";
++ };
++};
--- /dev/null
+From 406e7dc82be6ce1b81c88b418640daeef6c2be42 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 23 May 2022 16:56:44 +0100
+Subject: [PATCH] Revert "usb: phy: generic: Get the vbus supply"
+
+This reverts commit c0ea202fbc855d60bc4a0603ca52a9e80654b327.
+---
+ drivers/usb/phy/phy-generic.c | 7 -------
+ 1 file changed, 7 deletions(-)
+
+--- a/drivers/usb/phy/phy-generic.c
++++ b/drivers/usb/phy/phy-generic.c
+@@ -265,13 +265,6 @@ int usb_phy_gen_create_phy(struct device
+ return -EPROBE_DEFER;
+ }
+
+- nop->vbus_draw = devm_regulator_get_exclusive(dev, "vbus");
+- if (PTR_ERR(nop->vbus_draw) == -ENODEV)
+- nop->vbus_draw = NULL;
+- if (IS_ERR(nop->vbus_draw))
+- return dev_err_probe(dev, PTR_ERR(nop->vbus_draw),
+- "could not get vbus regulator\n");
+-
+ nop->dev = dev;
+ nop->phy.dev = nop->dev;
+ nop->phy.label = "nop-xceiv";
--- /dev/null
+From 1196bf1a7736ff0ab79f5012fa84082e298031a7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 19 Sep 2023 15:55:00 +0100
+Subject: [PATCH] dts: 2712: Update for device tree
+
+dtoverlays: Fix up edt5406 entries to match with vc4-kms-dsi-7inch
+
+vc4-kms-dsi-7inch expects the touch fragment to be named ts_i2c_frag,
+but edt5406 didn't do this.
+
+Fixes: 736d601fb38c ("dts: 2712: Update for device tree")
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/Makefile | 3 +-
+ arch/arm/boot/dts/bcm2708-rpi-b-plus.dts | 3 +
+ arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts | 3 +
+ arch/arm/boot/dts/bcm2708-rpi-b.dts | 3 +
+ arch/arm/boot/dts/bcm2708-rpi-cm.dts | 3 +
+ arch/arm/boot/dts/bcm2708-rpi-zero-w.dts | 1 +
+ arch/arm/boot/dts/bcm2708-rpi-zero.dts | 1 +
+ arch/arm/boot/dts/bcm2709-rpi-2-b.dts | 3 +
+ arch/arm/boot/dts/bcm2709-rpi-cm2.dts | 3 +
+ arch/arm/boot/dts/bcm270x-rpi.dtsi | 3 +
+ arch/arm/boot/dts/bcm2710-rpi-2-b.dts | 3 +
+ arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts | 3 +
+ arch/arm/boot/dts/bcm2710-rpi-3-b.dts | 3 +
+ arch/arm/boot/dts/bcm2710-rpi-cm3.dts | 3 +
+ arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts | 3 +
+ arch/arm/boot/dts/bcm2711-rpi-4-b.dts | 3 +
+ arch/arm/boot/dts/bcm2711-rpi-cm4.dts | 3 +
+ arch/arm/boot/dts/bcm2711-rpi-cm4s.dts | 3 +
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 824 +++++++++++
+ arch/arm/boot/dts/bcm2712-rpi.dtsi | 281 ++++
+ arch/arm/boot/dts/bcm2712.dtsi | 1287 +++++++++++++++++
+ arch/arm/boot/dts/overlays/Makefile | 23 +
+ arch/arm/boot/dts/overlays/README | 360 ++++-
+ .../dts/overlays/adau1977-adc-overlay.dts | 4 +-
+ .../dts/overlays/adau7002-simple-overlay.dts | 4 +-
+ .../overlays/akkordion-iqdacplus-overlay.dts | 4 +-
+ .../allo-boss-dac-pcm512x-audio-overlay.dts | 10 +-
+ .../overlays/allo-boss2-dac-audio-overlay.dts | 2 +-
+ .../dts/overlays/allo-digione-overlay.dts | 4 +-
+ .../allo-katana-dac-audio-overlay.dts | 2 +-
+ .../allo-piano-dac-pcm512x-audio-overlay.dts | 4 +-
+ ...o-piano-dac-plus-pcm512x-audio-overlay.dts | 4 +-
+ .../boot/dts/overlays/applepi-dac-overlay.dts | 4 +-
+ .../dts/overlays/arducam-64mp-overlay.dts | 2 +-
+ .../overlays/arducam-pivariety-overlay.dts | 2 +-
+ .../overlays/audioinjector-addons-overlay.dts | 4 +-
+ .../audioinjector-bare-i2s-overlay.dts | 6 +-
+ ...dioinjector-isolated-soundcard-overlay.dts | 4 +-
+ .../overlays/audioinjector-ultra-overlay.dts | 6 +-
+ .../audioinjector-wm8731-audio-overlay.dts | 4 +-
+ .../dts/overlays/audiosense-pi-overlay.dts | 4 +-
+ .../boot/dts/overlays/chipdip-dac-overlay.dts | 4 +-
+ .../dts/overlays/cirrus-wm5102-overlay.dts | 4 +-
+ .../boot/dts/overlays/dacberry400-overlay.dts | 4 +-
+ .../dts/overlays/dionaudio-kiwi-overlay.dts | 4 +-
+ .../dts/overlays/dionaudio-loco-overlay.dts | 4 +-
+ .../overlays/dionaudio-loco-v2-overlay.dts | 4 +-
+ .../dts/overlays/disable-bt-pi5-overlay.dts | 17 +
+ .../dts/overlays/disable-wifi-pi5-overlay.dts | 13 +
+ arch/arm/boot/dts/overlays/draws-overlay.dts | 6 +-
+ .../boot/dts/overlays/edt-ft5406-overlay.dts | 22 +-
+ arch/arm/boot/dts/overlays/edt-ft5406.dtsi | 2 +-
+ .../boot/dts/overlays/fe-pi-audio-overlay.dts | 4 +-
+ .../boot/dts/overlays/ghost-amp-overlay.dts | 4 +-
+ .../googlevoicehat-soundcard-overlay.dts | 4 +-
+ .../dts/overlays/hifiberry-amp-overlay.dts | 4 +-
+ .../dts/overlays/hifiberry-amp100-overlay.dts | 11 +-
+ .../dts/overlays/hifiberry-amp3-overlay.dts | 4 +-
+ .../dts/overlays/hifiberry-dac-overlay.dts | 4 +-
+ .../overlays/hifiberry-dacplus-overlay.dts | 11 +-
+ .../overlays/hifiberry-dacplusadc-overlay.dts | 10 +-
+ .../hifiberry-dacplusadcpro-overlay.dts | 10 +-
+ .../overlays/hifiberry-dacplusdsp-overlay.dts | 4 +-
+ .../overlays/hifiberry-dacplushd-overlay.dts | 4 +-
+ .../dts/overlays/hifiberry-digi-overlay.dts | 4 +-
+ .../overlays/hifiberry-digi-pro-overlay.dts | 4 +-
+ .../boot/dts/overlays/i-sabre-q2m-overlay.dts | 4 +-
+ .../boot/dts/overlays/i2c0-pi5-overlay.dts | 34 +
+ .../boot/dts/overlays/i2c1-pi5-overlay.dts | 34 +
+ .../boot/dts/overlays/i2c2-pi5-overlay.dts | 21 +
+ .../boot/dts/overlays/i2c3-pi5-overlay.dts | 22 +
+ .../arm/boot/dts/overlays/i2s-dac-overlay.dts | 4 +-
+ arch/arm/boot/dts/overlays/imx219-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/imx258-overlay.dts | 2 +-
+ .../boot/dts/overlays/imx290_327-overlay.dtsi | 2 +-
+ arch/arm/boot/dts/overlays/imx296-overlay.dts | 2 +-
+ .../boot/dts/overlays/imx477_378-overlay.dtsi | 2 +-
+ arch/arm/boot/dts/overlays/imx519-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/imx708-overlay.dts | 4 +-
+ .../dts/overlays/iqaudio-codec-overlay.dts | 4 +-
+ .../boot/dts/overlays/iqaudio-dac-overlay.dts | 4 +-
+ .../dts/overlays/iqaudio-dacplus-overlay.dts | 4 +-
+ .../iqaudio-digi-wm8804-audio-overlay.dts | 4 +-
+ .../arm/boot/dts/overlays/irs1125-overlay.dts | 2 +-
+ .../dts/overlays/justboom-both-overlay.dts | 4 +-
+ .../dts/overlays/justboom-dac-overlay.dts | 4 +-
+ .../dts/overlays/justboom-digi-overlay.dts | 4 +-
+ .../boot/dts/overlays/max98357a-overlay.dts | 6 +-
+ .../boot/dts/overlays/mbed-dac-overlay.dts | 6 +-
+ .../boot/dts/overlays/merus-amp-overlay.dts | 4 +-
+ .../dts/overlays/midi-uart0-pi5-overlay.dts | 35 +
+ .../dts/overlays/midi-uart1-pi5-overlay.dts | 35 +
+ .../dts/overlays/midi-uart2-pi5-overlay.dts | 35 +
+ .../dts/overlays/midi-uart3-pi5-overlay.dts | 35 +
+ .../dts/overlays/midi-uart4-pi5-overlay.dts | 35 +
+ arch/arm/boot/dts/overlays/ov2311-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/ov7251-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/ov9281-overlay.dts | 2 +-
+ arch/arm/boot/dts/overlays/overlay_map.dts | 226 +++
+ arch/arm/boot/dts/overlays/pibell-overlay.dts | 6 +-
+ .../arm/boot/dts/overlays/pifi-40-overlay.dts | 4 +-
+ .../boot/dts/overlays/pifi-dac-hd-overlay.dts | 4 +-
+ .../dts/overlays/pifi-dac-zero-overlay.dts | 4 +-
+ .../dts/overlays/pifi-mini-210-overlay.dts | 4 +-
+ .../arm/boot/dts/overlays/pisound-overlay.dts | 4 +-
+ .../boot/dts/overlays/proto-codec-overlay.dts | 4 +-
+ .../rra-digidac1-wm8741-audio-overlay.dts | 4 +-
+ .../dts/overlays/spi2-1cs-pi5-overlay.dts | 33 +
+ .../dts/overlays/spi2-2cs-pi5-overlay.dts | 44 +
+ .../dts/overlays/spi3-1cs-pi5-overlay.dts | 33 +
+ .../dts/overlays/spi3-2cs-pi5-overlay.dts | 44 +
+ .../dts/overlays/spi5-1cs-pi5-overlay.dts | 33 +
+ .../dts/overlays/spi5-2cs-pi5-overlay.dts | 44 +
+ .../dts/overlays/superaudioboard-overlay.dts | 6 +-
+ .../dts/overlays/tc358743-audio-overlay.dts | 10 +-
+ .../boot/dts/overlays/tc358743-overlay.dts | 2 +-
+ .../boot/dts/overlays/uart0-pi5-overlay.dts | 17 +
+ .../boot/dts/overlays/uart1-pi5-overlay.dts | 17 +
+ .../boot/dts/overlays/uart2-pi5-overlay.dts | 17 +
+ .../boot/dts/overlays/uart3-pi5-overlay.dts | 17 +
+ .../boot/dts/overlays/uart4-pi5-overlay.dts | 17 +
+ arch/arm/boot/dts/overlays/udrc-overlay.dts | 6 +-
+ .../dts/overlays/ugreen-dabboard-overlay.dts | 10 +-
+ .../dts/overlays/vc4-fkms-v3d-overlay.dts | 6 +
+ .../dts/overlays/vc4-fkms-v3d-pi4-overlay.dts | 6 +
+ .../overlays/vc4-kms-dsi-7inch-overlay.dts | 18 +-
+ .../vc4-kms-dsi-waveshare-panel-overlay.dts | 8 +-
+ .../dts/overlays/vc4-kms-v3d-pi5-overlay.dts | 147 ++
+ .../dts/overlays/vc4-kms-vga666-overlay.dts | 9 +-
+ .../dts/overlays/wm8960-soundcard-overlay.dts | 4 +-
+ arch/arm/boot/dts/rp1.dtsi | 1168 +++++++++++++++
+ arch/arm64/boot/dts/broadcom/Makefile | 1 +
+ .../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 1 +
+ 134 files changed, 5143 insertions(+), 264 deletions(-)
+ create mode 100644 arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+ create mode 100644 arch/arm/boot/dts/bcm2712-rpi.dtsi
+ create mode 100644 arch/arm/boot/dts/bcm2712.dtsi
+ create mode 100644 arch/arm/boot/dts/overlays/disable-bt-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/disable-wifi-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c0-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c1-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c2-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/i2c3-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart0-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart1-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart2-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart3-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/midi-uart4-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi2-1cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi2-2cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi3-1cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi3-2cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi5-1cs-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/spi5-2cs-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
+ create mode 100755 arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts
+ create mode 100644 arch/arm/boot/dts/rp1.dtsi
+ create mode 100644 arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -18,7 +18,8 @@ dtb-$(CONFIG_ARCH_BCM2835) += \
+ bcm2709-rpi-cm2.dtb \
+ bcm2710-rpi-cm3.dtb \
+ bcm2711-rpi-cm4.dtb \
+- bcm2711-rpi-cm4s.dtb
++ bcm2711-rpi-cm4s.dtb \
++ bcm2712-rpi-5-b.dtb
+
+ dtb-$(CONFIG_ARCH_ALPINE) += \
+ alpine-db.dtb
+--- a/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b-plus.dts
+@@ -192,6 +192,9 @@ i2c_arm: &i2c1 {
+ i2c_vc: &i2c0 {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b-rev1.dts
+@@ -203,6 +203,9 @@ i2c_arm: &i2c0 {
+ i2c_vc: &i2c1 {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2708-rpi-b.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-b.dts
+@@ -185,6 +185,9 @@ i2c_arm: &i2c1 {
+ i2c_vc: &i2c0 {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2708-rpi-cm.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-cm.dts
+@@ -19,6 +19,9 @@ cam0_reg: &cam0_regulator {
+ gpio = <&gpio 31 GPIO_ACTIVE_HIGH>;
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ &uart0 {
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero-w.dts
+@@ -243,6 +243,7 @@ cam0_reg: &cam_dummy_reg {
+
+ i2c_arm: &i2c1 {};
+ i2c_vc: &i2c0 {};
++i2c_csi_dsi0: &i2c0 {};
+
+ / {
+ __overrides__ {
+--- a/arch/arm/boot/dts/bcm2708-rpi-zero.dts
++++ b/arch/arm/boot/dts/bcm2708-rpi-zero.dts
+@@ -178,6 +178,7 @@ cam0_reg: &cam_dummy_reg {
+
+ i2c_arm: &i2c1 {};
+ i2c_vc: &i2c0 {};
++i2c_csi_dsi0: &i2c0 {};
+
+ / {
+ __overrides__ {
+--- a/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2709-rpi-2-b.dts
+@@ -186,6 +186,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2709-rpi-cm2.dts
++++ b/arch/arm/boot/dts/bcm2709-rpi-cm2.dts
+@@ -20,6 +20,9 @@ cam0_reg: &cam0_regulator {
+ gpio = <&gpio 30 GPIO_ACTIVE_HIGH>;
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ &uart0 {
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi
+@@ -127,6 +127,9 @@
+ status = "disabled";
+ };
+
++i2s_clk_producer: &i2s {};
++i2s_clk_consumer: &i2s {};
++
+ &clocks {
+ firmware = <&firmware>;
+ };
+--- a/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-2-b.dts
+@@ -186,6 +186,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts
+@@ -274,6 +274,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-3-b.dts
+@@ -283,6 +283,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-cm3.dts
+@@ -19,6 +19,9 @@ cam0_reg: &cam0_regulator {
+ gpio = <&gpio 31 GPIO_ACTIVE_HIGH>;
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ &uart0 {
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
+@@ -262,6 +262,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-4-b.dts
+@@ -400,6 +400,9 @@
+ cam0_reg: &cam_dummy_reg {
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_headphones=1 snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_headphones=0 snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
+@@ -409,6 +409,9 @@ cam0_reg: &cam1_reg {
+ gpio = <&expgpio 5 GPIO_ACTIVE_HIGH>;
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_hdmi=0'}";
+--- a/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-cm4s.dts
+@@ -282,6 +282,9 @@ cam0_reg: &cam0_regulator {
+ status = "disabled";
+ };
+
++i2c_csi_dsi0: &i2c0 {
++};
++
+ / {
+ __overrides__ {
+ audio = <&chosen>,"bootargs{on='snd_bcm2835.enable_hdmi=1',off='snd_bcm2835.enable_hdmi=0'}";
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -0,0 +1,824 @@
++// SPDX-License-Identifier: GPL-2.0
++/dts-v1/;
++
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/clock/rp1.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/mfd/rp1.h>
++#include <dt-bindings/pwm/pwm.h>
++#include <dt-bindings/reset/raspberrypi,firmware-reset.h>
++
++#define i2c0 _i2c0
++#define i2c3 _i2c3
++#define i2c4 _i2c4
++#define i2c5 _i2c5
++#define i2c6 _i2c6
++#define i2c8 _i2c8
++#define i2s _i2s
++#define pwm0 _pwm0
++#define pwm1 _pwm1
++#define spi0 _spi0
++#define spi3 _spi3
++#define spi4 _spi4
++#define spi5 _spi5
++#define spi6 _spi6
++#define uart0 _uart0
++#define uart2 _uart2
++#define uart3 _uart3
++#define uart4 _uart4
++#define uart5 _uart5
++
++#include "bcm2712.dtsi"
++
++#undef i2c0
++#undef i2c3
++#undef i2c4
++#undef i2c5
++#undef i2c6
++#undef i2c8
++#undef i2s
++#undef pwm0
++#undef pwm1
++#undef spi0
++#undef spi3
++#undef spi4
++#undef spi5
++#undef spi6
++#undef uart0
++#undef uart2
++#undef uart3
++#undef uart4
++#undef uart5
++
++/ {
++ compatible = "raspberrypi,5-model-b", "brcm,bcm2712";
++ model = "Raspberry Pi 5 Model B";
++
++ /* Will be filled by the bootloader */
++ memory@0 {
++ device_type = "memory";
++ reg = <0 0 0x28000000>;
++ };
++
++ leds: leds {
++ compatible = "gpio-leds";
++
++ pwr_led: led-pwr {
++ label = "PWR";
++ gpios = <&rp1_gpio 44 GPIO_ACTIVE_LOW>;
++ default-state = "off";
++ linux,default-trigger = "none";
++ };
++
++ act_led: led-act {
++ label = "ACT";
++ gpios = <&gio_aon 9 GPIO_ACTIVE_LOW>;
++ default-state = "off";
++ linux,default-trigger = "mmc0";
++ };
++ };
++
++ sd_io_1v8_reg: sd_io_1v8_reg {
++ compatible = "regulator-gpio";
++ regulator-name = "vdd-sd-io";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-boot-on;
++ regulator-always-on;
++ regulator-settling-time-us = <5000>;
++ gpios = <&gio_aon 3 GPIO_ACTIVE_HIGH>;
++ states = <1800000 0x1
++ 3300000 0x0>;
++ status = "okay";
++ };
++
++ sd_vcc_reg: sd_vcc_reg {
++ compatible = "regulator-fixed";
++ regulator-name = "vcc-sd";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-boot-on;
++ enable-active-high;
++ gpios = <&gio_aon 4 GPIO_ACTIVE_HIGH>;
++ status = "okay";
++ };
++
++ wl_on_reg: wl_on_reg {
++ compatible = "regulator-fixed";
++ regulator-name = "wl-on-regulator";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ pinctrl-0 = <&wl_on_pins>;
++ pinctrl-names = "default";
++
++ gpio = <&gio 28 GPIO_ACTIVE_HIGH>;
++
++ startup-delay-us = <150000>;
++ enable-active-high;
++ };
++
++ clocks: clocks {
++ };
++
++ cam1_clk: cam1_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ status = "disabled";
++ };
++
++ cam0_clk: cam0_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ status = "disabled";
++ };
++
++ cam0_reg: cam0_reg {
++ compatible = "regulator-fixed";
++ regulator-name = "cam0_reg";
++ enable-active-high;
++ status = "okay";
++ gpio = <&rp1_gpio 34 0>; // CD0_IO0_MICCLK, to MIPI 0 connector
++ };
++
++ cam1_reg: cam1_reg {
++ compatible = "regulator-fixed";
++ regulator-name = "cam1_reg";
++ enable-active-high;
++ status = "okay";
++ gpio = <&rp1_gpio 46 0>; // CD1_IO0_MICCLK, to MIPI 1 connector
++ };
++
++ cam_dummy_reg: cam_dummy_reg {
++ compatible = "regulator-fixed";
++ regulator-name = "cam-dummy-reg";
++ status = "okay";
++ };
++
++ dummy: dummy {
++ // A target for unwanted overlay fragments
++ };
++};
++
++rp1_target: &pcie2 {
++ brcm,vdm-qos-map = <0xbbaa9888>;
++ aspm-no-l0s;
++ status = "okay";
++};
++
++// Add some labels to 2712 device
++
++// The system UART
++uart10: &_uart0 { status = "okay"; };
++
++// The system SPI for the bootloader EEPROM
++spi10: &_spi0 { status = "okay"; };
++
++i2c_rp1boot: &_i2c3 { };
++
++#include "rp1.dtsi"
++
++&rp1 {
++ // PCIe address space layout:
++ // 00_00000000-00_00xxxxxx = RP1 peripherals
++ // 10_00000000-1x_xxxxxxxx = up to 64GB system RAM
++
++ // outbound access aimed at PCIe 0_00xxxxxx -> RP1 c0_40xxxxxx
++ // This is the RP1 peripheral space
++ ranges = <0xc0 0x40000000
++ 0x02000000 0x00 0x00000000
++ 0x00 0x00400000>;
++
++ dma-ranges =
++ // inbound RP1 1x_xxxxxxxx -> PCIe 1x_xxxxxxxx
++ <0x10 0x00000000
++ 0x43000000 0x10 0x00000000
++ 0x10 0x00000000>,
++
++ // inbound RP1 c0_40xxxxxx -> PCIe 00_00xxxxxx
++ // This allows the RP1 DMA controller to address RP1 hardware
++ <0xc0 0x40000000
++ 0x02000000 0x0 0x00000000
++ 0x0 0x00400000>,
++
++ // inbound RP1 0x_xxxxxxxx -> PCIe 1x_xxxxxxxx
++ <0x00 0x00000000
++ 0x02000000 0x10 0x00000000
++ 0x10 0x00000000>;
++};
++
++// Expose RP1 nodes as system nodes with labels
++
++&rp1_dma {
++ status = "okay";
++};
++
++&rp1_eth {
++ status = "okay";
++ phy-handle = <&phy1>;
++ phy-reset-gpios = <&rp1_gpio 32 GPIO_ACTIVE_LOW>;
++ phy-reset-duration = <5>;
++
++ phy1: ethernet-phy@1 {
++ reg = <0x1>;
++ brcm,powerdown-enable;
++ };
++};
++
++gpio: &rp1_gpio {
++ status = "okay";
++};
++
++aux: &dummy {};
++
++&rp1_usb0 {
++ pinctrl-0 = <&usb_vbus_pins>;
++ pinctrl-names = "default";
++ status = "okay";
++};
++
++&rp1_usb1 {
++ status = "okay";
++};
++
++#include "bcm2712-rpi.dtsi"
++
++// A few extra labels to keep overlays happy
++
++i2c0if: &rp1_gpio {};
++i2c0mux: &rp1_gpio {};
++
++i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only
++ pinctrl-0 = <&rp1_i2c6_38_39>;
++ pinctrl-names = "default";
++};
++
++i2c_csi_dsi1: &i2c4 { // Note: This is for MIPI1 connector only
++ pinctrl-0 = <&rp1_i2c4_40_41>;
++ pinctrl-names = "default";
++};
++
++i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
++
++csi0: &rp1_csi0 { };
++csi1: &rp1_csi1 { };
++dsi0: &rp1_dsi0 { };
++dsi1: &rp1_dsi1 { };
++dpi: &rp1_dpi { };
++vec: &rp1_vec { };
++dpi_gpio0: &rp1_dpi_24bit_gpio0 { };
++dpi_gpio1: &rp1_dpi_24bit_gpio2 { };
++dpi_18bit_cpadhi_gpio0: &rp1_dpi_18bit_cpadhi_gpio0 { };
++dpi_18bit_cpadhi_gpio2: &rp1_dpi_18bit_cpadhi_gpio2 { };
++dpi_18bit_gpio0: &rp1_dpi_18bit_gpio0 { };
++dpi_18bit_gpio2: &rp1_dpi_18bit_gpio2 { };
++dpi_16bit_cpadhi_gpio0: &rp1_dpi_16bit_cpadhi_gpio0 { };
++dpi_16bit_cpadhi_gpio2: &rp1_dpi_16bit_cpadhi_gpio2 { };
++dpi_16bit_gpio0: &rp1_dpi_16bit_gpio0 { };
++dpi_16bit_gpio2: &rp1_dpi_16bit_gpio2 { };
++
++/* Add the IOMMUs for some RP1 bus masters */
++
++&csi0 {
++ iommus = <&iommu5>;
++};
++
++&csi1 {
++ iommus = <&iommu5>;
++};
++
++&dsi0 {
++ iommus = <&iommu5>;
++};
++
++&dsi1 {
++ iommus = <&iommu5>;
++};
++
++&dpi {
++ iommus = <&iommu5>;
++};
++
++&vec {
++ iommus = <&iommu5>;
++};
++
++&ddc0 {
++ status = "disabled";
++};
++
++&ddc1 {
++ status = "disabled";
++};
++
++&hdmi0 {
++ clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 0>, <&clk_27MHz>;
++ clock-names = "hdmi", "bvb", "audio", "cec";
++ status = "disabled";
++};
++
++&hdmi1 {
++ clocks = <&firmware_clocks 13>, <&firmware_clocks 14>, <&dvp 1>, <&clk_27MHz>;
++ clock-names = "hdmi", "bvb", "audio", "cec";
++ status = "disabled";
++};
++
++&hvs {
++ clocks = <&firmware_clocks 4>, <&firmware_clocks 16>;
++ clock-names = "core", "disp";
++};
++
++&mop {
++ status = "disabled";
++};
++
++&moplet {
++ status = "disabled";
++};
++
++&pixelvalve0 {
++ status = "disabled";
++};
++
++&pixelvalve1 {
++ status = "disabled";
++};
++
++&disp_intr {
++ status = "disabled";
++};
++
++/* SDIO1 is used to drive the SD card */
++&sdio1 {
++ pinctrl-0 = <&emmc_sd_pulls>, <&emmc_aon_cd_pins>;
++ pinctrl-names = "default";
++ vqmmc-supply = <&sd_io_1v8_reg>;
++ vmmc-supply = <&sd_vcc_reg>;
++ bus-width = <4>;
++ sd-uhs-sdr50;
++ sd-uhs-ddr50;
++ sd-uhs-sdr104;
++ //broken-cd;
++ //no-1-8-v;
++ status = "okay";
++};
++
++&pinctrl_aon {
++ emmc_aon_cd_pins: emmc_aon_cd_pins {
++ function = "sd_card_g";
++ pins = "aon_gpio5";
++ bias-pull-up;
++ };
++
++ /* Slight hack - only one PWM pin (status LED) is usable */
++ aon_pwm_1pin: aon_pwm_1pin {
++ function = "aon_pwm";
++ pins = "aon_gpio9";
++ };
++};
++
++&pinctrl {
++ pwr_button_pins: pwr_button_pins {
++ function = "gpio";
++ pins = "gpio20";
++ bias-pull-up;
++ };
++
++ wl_on_pins: wl_on_pins {
++ function = "gpio";
++ pins = "gpio28";
++ };
++
++ bt_shutdown_pins: bt_shutdown_pins {
++ function = "gpio";
++ pins = "gpio29";
++ };
++
++ emmc_sd_pulls: emmc_sd_pulls {
++ function = "emmc_dat0", "emmc_dat1", "emmc_dat2", "emmc_dat3";
++ bias-pull-up;
++ };
++};
++
++/* uarta communicates with the BT module */
++&uarta {
++ uart-has-rtscts;
++ auto-flow-control;
++ status = "okay";
++ clock-frequency = <96000000>;
++ pinctrl-0 = <&uarta_24_pins &bt_shutdown_pins>;
++ pinctrl-names = "default";
++
++ bluetooth: bluetooth {
++ compatible = "brcm,bcm43438-bt";
++ max-speed = <3000000>;
++ shutdown-gpios = <&gio 29 GPIO_ACTIVE_HIGH>;
++ local-bd-address = [ 00 00 00 00 00 00 ];
++ };
++};
++
++&i2c_rp1boot {
++ clock-frequency = <400000>;
++ pinctrl-0 = <&i2c3_m4_agpio0_pins>;
++ pinctrl-names = "default";
++};
++
++/ {
++ chosen: chosen {
++ bootargs = "coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
++ stdout-path = "serial10:115200n8";
++ };
++
++ fan: cooling_fan {
++ status = "disabled";
++ compatible = "pwm-fan";
++ #cooling-cells = <2>;
++ cooling-min-state = <0>;
++ cooling-max-state = <3>;
++ cooling-levels = <0 75 125 175 250>;
++ pwms = <&rp1_pwm1 3 41566 PWM_POLARITY_INVERTED>;
++ rpm-regmap = <&rp1_pwm1>;
++ rpm-offset = <0x3c>;
++ };
++
++ pwr_button {
++ compatible = "gpio-keys";
++
++ pinctrl-names = "default";
++ pinctrl-0 = <&pwr_button_pins>;
++ status = "okay";
++
++ pwr_key: pwr {
++ label = "pwr_button";
++ // linux,code = <205>; // KEY_SUSPEND
++ linux,code = <116>; // KEY_POWER
++ gpios = <&gio 20 GPIO_ACTIVE_LOW>;
++ debounce-interval = <50>; // ms
++ };
++ };
++};
++
++&usb {
++ power-domains = <&power RPI_POWER_DOMAIN_USB>;
++};
++
++/* SDIO2 drives the WLAN interface */
++&sdio2 {
++ pinctrl-0 = <&sdio2_30_pins>;
++ pinctrl-names = "default";
++ bus-width = <4>;
++ vmmc-supply = <&wl_on_reg>;
++ sd-uhs-ddr50;
++ non-removable;
++ status = "okay";
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ wifi: wifi@1 {
++ reg = <1>;
++ compatible = "brcm,bcm4329-fmac";
++ local-mac-address = [00 00 00 00 00 00];
++ };
++};
++
++&rpivid {
++ status = "okay";
++};
++
++&pinctrl {
++ spi10_gpio2: spi10_gpio2 {
++ function = "vc_spi0";
++ pins = "gpio2", "gpio3", "gpio4";
++ bias-disable;
++ };
++
++ spi10_cs_gpio1: spi10_cs_gpio1 {
++ function = "gpio";
++ pins = "gpio1";
++ bias-pull-up;
++ };
++};
++
++spi10_pins: &spi10_gpio2 {};
++spi10_cs_pins: &spi10_cs_gpio1 {};
++
++&spi10 {
++ pinctrl-names = "default";
++ cs-gpios = <&gio 1 1>;
++ pinctrl-0 = <&spi10_pins &spi10_cs_pins>;
++
++ spidev10: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <20000000>;
++ status = "okay";
++ };
++};
++
++// =============================================
++// Board specific stuff here
++
++&gio_aon {
++ // Don't use GIO_AON as an interrupt controller because it will
++ // clash with the firmware monitoring the PMIC interrupt via the VPU.
++
++ /delete-property/ interrupt-controller;
++};
++
++&main_aon_irq {
++ // Don't use the MAIN_AON_IRQ interrupt controller because it will
++ // clash with the firmware monitoring the PMIC interrupt via the VPU.
++
++ status = "disabled";
++};
++
++&rp1_pwm1 {
++ status = "disabled";
++ pinctrl-0 = <&rp1_pwm1_gpio45>;
++ pinctrl-names = "default";
++};
++
++&thermal_trips {
++ cpu_tepid: cpu-tepid {
++ temperature = <50000>;
++ hysteresis = <5000>;
++ type = "active";
++ };
++
++ cpu_warm: cpu-warm {
++ temperature = <60000>;
++ hysteresis = <5000>;
++ type = "active";
++ };
++
++ cpu_hot: cpu-hot {
++ temperature = <67500>;
++ hysteresis = <5000>;
++ type = "active";
++ };
++
++ cpu_vhot: cpu-vhot {
++ temperature = <75000>;
++ hysteresis = <5000>;
++ type = "active";
++ };
++};
++
++&cooling_maps {
++ tepid {
++ trip = <&cpu_tepid>;
++ cooling-device = <&fan 1 1>;
++ };
++
++ warm {
++ trip = <&cpu_warm>;
++ cooling-device = <&fan 2 2>;
++ };
++
++ hot {
++ trip = <&cpu_hot>;
++ cooling-device = <&fan 3 3>;
++ };
++
++ vhot {
++ trip = <&cpu_vhot>;
++ cooling-device = <&fan 4 4>;
++ };
++
++ melt {
++ trip = <&cpu_crit>;
++ cooling-device = <&fan 4 4>;
++ };
++};
++
++&gio {
++ // The GPIOs above 35 are not used on Pi 5, so shrink the upper bank
++ // to reduce the clutter in gpioinfo/pinctrl
++ brcm,gpio-bank-widths = <32 4>;
++
++ gpio-line-names =
++ "-", // GPIO_000
++ "2712_BOOT_CS_N", // GPIO_001
++ "2712_BOOT_MISO", // GPIO_002
++ "2712_BOOT_MOSI", // GPIO_003
++ "2712_BOOT_SCLK", // GPIO_004
++ "-", // GPIO_005
++ "-", // GPIO_006
++ "-", // GPIO_007
++ "-", // GPIO_008
++ "-", // GPIO_009
++ "-", // GPIO_010
++ "-", // GPIO_011
++ "-", // GPIO_012
++ "-", // GPIO_013
++ "PCIE_SDA", // GPIO_014
++ "PCIE_SCL", // GPIO_015
++ "-", // GPIO_016
++ "-", // GPIO_017
++ "-", // GPIO_018
++ "-", // GPIO_019
++ "PWR_GPIO", // GPIO_020
++ "2712_G21_FS", // GPIO_021
++ "-", // GPIO_022
++ "-", // GPIO_023
++ "BT_RTS", // GPIO_024
++ "BT_CTS", // GPIO_025
++ "BT_TXD", // GPIO_026
++ "BT_RXD", // GPIO_027
++ "WL_ON", // GPIO_028
++ "BT_ON", // GPIO_029
++ "WIFI_SDIO_CLK", // GPIO_030
++ "WIFI_SDIO_CMD", // GPIO_031
++ "WIFI_SDIO_D0", // GPIO_032
++ "WIFI_SDIO_D1", // GPIO_033
++ "WIFI_SDIO_D2", // GPIO_034
++ "WIFI_SDIO_D3"; // GPIO_035
++};
++
++&gio_aon {
++ gpio-line-names =
++ "RP1_SDA", // AON_GPIO_00
++ "RP1_SCL", // AON_GPIO_01
++ "RP1_RUN", // AON_GPIO_02
++ "SD_IOVDD_SEL", // AON_GPIO_03
++ "SD_PWR_ON", // AON_GPIO_04
++ "SD_CDET_N", // AON_GPIO_05
++ "SD_FLG_N", // AON_GPIO_06
++ "-", // AON_GPIO_07
++ "2712_WAKE", // AON_GPIO_08
++ "2712_STAT_LED", // AON_GPIO_09
++ "-", // AON_GPIO_10
++ "-", // AON_GPIO_11
++ "PMIC_INT", // AON_GPIO_12
++ "UART_TX_FS", // AON_GPIO_13
++ "UART_RX_FS", // AON_GPIO_14
++ "-", // AON_GPIO_15
++ "-", // AON_GPIO_16
++
++ // Pad bank0 out to 32 entries
++ "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
++
++ "HDMI0_SCL", // AON_SGPIO_00
++ "HDMI0_SDA", // AON_SGPIO_01
++ "HDMI1_SCL", // AON_SGPIO_02
++ "HDMI1_SDA", // AON_SGPIO_03
++ "PMIC_SCL", // AON_SGPIO_04
++ "PMIC_SDA"; // AON_SGPIO_05
++
++ rp1_run_hog {
++ gpio-hog;
++ gpios = <2 GPIO_ACTIVE_HIGH>;
++ output-high;
++ line-name = "RP1 RUN pin";
++ };
++};
++
++&rp1_gpio {
++ gpio-line-names =
++ "ID_SD", // GPIO0
++ "ID_SC", // GPIO1
++ "PIN3", // GPIO2
++ "PIN5", // GPIO3
++ "PIN7", // GPIO4
++ "PIN29", // GPIO5
++ "PIN31", // GPIO6
++ "PIN26", // GPIO7
++ "PIN24", // GPIO8
++ "PIN21", // GPIO9
++ "PIN19", // GPIO10
++ "PIN23", // GPIO11
++ "PIN32", // GPIO12
++ "PIN33", // GPIO13
++ "PIN8", // GPIO14
++ "PIN10", // GPIO15
++ "PIN36", // GPIO16
++ "PIN11", // GPIO17
++ "PIN12", // GPIO18
++ "PIN35", // GPIO19
++ "PIN38", // GPIO20
++ "PIN40", // GPIO21
++ "PIN15", // GPIO22
++ "PIN16", // GPIO23
++ "PIN18", // GPIO24
++ "PIN22", // GPIO25
++ "PIN37", // GPIO26
++ "PIN13", // GPIO27
++
++ "PCIE_RP1_WAKE", // GPIO28
++ "FAN_TACH", // GPIO29
++ "HOST_SDA", // GPIO30
++ "HOST_SCL", // GPIO31
++ "ETH_RST_N", // GPIO32
++ "-", // GPIO33
++
++ "CD0_IO0_MICCLK", // GPIO34
++ "CD0_IO0_MICDAT0", // GPIO35
++ "RP1_PCIE_CLKREQ_N", // GPIO36
++ "-", // GPIO37
++ "CD0_SDA", // GPIO38
++ "CD0_SCL", // GPIO39
++ "CD1_SDA", // GPIO40
++ "CD1_SCL", // GPIO41
++ "USB_VBUS_EN", // GPIO42
++ "USB_OC_N", // GPIO43
++ "RP1_STAT_LED", // GPIO44
++ "FAN_PWM", // GPIO45
++ "CD1_IO0_MICCLK", // GPIO46
++ "2712_WAKE", // GPIO47
++ "CD1_IO1_MICDAT1", // GPIO48
++ "EN_MAX_USB_CUR", // GPIO49
++ "-", // GPIO50
++ "-", // GPIO51
++ "-", // GPIO52
++ "-"; // GPIO53
++
++ usb_vbus_pins: usb_vbus_pins {
++ function = "vbus1";
++ pins = "gpio42", "gpio43";
++ };
++};
++
++/ {
++ aliases: aliases {
++ blconfig = &blconfig;
++ bluetooth = &bluetooth;
++ console = &uart10;
++ ethernet0 = &rp1_eth;
++ wifi0 = &wifi;
++ fb = &fb;
++ mailbox = &mailbox;
++ mmc0 = &sdio1;
++ uart0 = &uart0;
++ uart1 = &uart1;
++ uart2 = &uart2;
++ uart3 = &uart3;
++ uart4 = &uart4;
++ uart10 = &uart10;
++ serial0 = &uart0;
++ serial1 = &uart1;
++ serial2 = &uart2;
++ serial3 = &uart3;
++ serial4 = &uart4;
++ serial10 = &uart10;
++ i2c = &i2c_arm;
++ i2c0 = &i2c0;
++ i2c1 = &i2c1;
++ i2c2 = &i2c2;
++ i2c3 = &i2c3;
++ i2c4 = &i2c4;
++ i2c5 = &i2c5;
++ i2c6 = &i2c6;
++ i2c10 = &i2c_rp1boot;
++ // Bit-bashed i2c_gpios start at 10
++ spi0 = &spi0;
++ spi1 = &spi1;
++ spi2 = &spi2;
++ spi3 = &spi3;
++ spi4 = &spi4;
++ spi5 = &spi5;
++ spi10 = &spi10;
++ gpio0 = &gpio;
++ gpio1 = &gio;
++ gpio2 = &gio_aon;
++ gpio3 = &pinctrl;
++ gpio4 = &pinctrl_aon;
++ usb0 = &rp1_usb0;
++ usb1 = &rp1_usb1;
++ };
++
++ __overrides__ {
++ bdaddr = <&bluetooth>, "local-bd-address[";
++ button_debounce = <&pwr_key>, "debounce-interval:0";
++ cooling_fan = <&fan>, "status", <&rp1_pwm1>, "status";
++ uart0_console = <&uart0>,"status", <&aliases>, "console=",&uart0;
++ i2c0 = <&i2c0>, "status";
++ i2c1 = <&i2c1>, "status";
++ i2c = <&i2c1>, "status";
++ i2c_arm = <&i2c_arm>, "status";
++ i2c_vc = <&i2c_vc>, "status";
++ i2c_csi_dsi = <&i2c_csi_dsi>, "status";
++ i2c_csi_dsi0 = <&i2c_csi_dsi0>, "status";
++ i2c_csi_dsi1 = <&i2c_csi_dsi1>, "status";
++ i2c0_baudrate = <&i2c0>, "clock-frequency:0";
++ i2c1_baudrate = <&i2c1>, "clock-frequency:0";
++ i2c_baudrate = <&i2c_arm>, "clock-frequency:0";
++ i2c_arm_baudrate = <&i2c_arm>, "clock-frequency:0";
++ i2c_vc_baudrate = <&i2c_vc>, "clock-frequency:0";
++ nvme = <&pciex1>, "status";
++ pciex1 = <&pciex1>, "status";
++ pciex1_gen = <&pciex1> , "max-link-speed:0";
++ pciex1_no_l0s = <&pciex1>, "aspm-no-l0s?";
++ random = <&random>, "status";
++ rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0";
++ spi = <&spi0>, "status";
++ suspend = <&pwr_key>, "linux,code:0=205";
++ uart0 = <&uart0>, "status";
++ wifiaddr = <&wifi>, "local-mac-address[";
++
++ act_led_activelow = <&act_led>, "active-low?";
++ act_led_trigger = <&act_led>, "linux,default-trigger";
++ pwr_led_activelow = <&pwr_led>, "gpios:8";
++ pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2712-rpi.dtsi
+@@ -0,0 +1,281 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <dt-bindings/power/raspberrypi-power.h>
++
++&soc {
++ firmware: firmware {
++ compatible = "raspberrypi,bcm2835-firmware", "simple-mfd";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ mboxes = <&mailbox>;
++ dma-ranges;
++
++ firmware_clocks: clocks {
++ compatible = "raspberrypi,firmware-clocks";
++ #clock-cells = <1>;
++ };
++
++ reset: reset {
++ compatible = "raspberrypi,firmware-reset";
++ #reset-cells = <1>;
++ };
++
++ vcio: vcio {
++ compatible = "raspberrypi,vcio";
++ };
++ };
++
++ power: power {
++ compatible = "raspberrypi,bcm2835-power";
++ firmware = <&firmware>;
++ #power-domain-cells = <1>;
++ };
++
++ fb: fb {
++ compatible = "brcm,bcm2708-fb";
++ firmware = <&firmware>;
++ status = "okay";
++ };
++
++ rpi_rtc: rpi_rtc {
++ compatible = "raspberrypi,rpi-rtc";
++ firmware = <&firmware>;
++ status = "okay";
++ trickle-charge-microvolt = <0>;
++ };
++
++ /* Define these notional regulators for use by overlays, etc. */
++ vdd_3v3_reg: fixedregulator_3v3 {
++ compatible = "regulator-fixed";
++ regulator-always-on;
++ regulator-max-microvolt = <3300000>;
++ regulator-min-microvolt = <3300000>;
++ regulator-name = "3v3";
++ };
++
++ vdd_5v0_reg: fixedregulator_5v0 {
++ compatible = "regulator-fixed";
++ regulator-always-on;
++ regulator-max-microvolt = <5000000>;
++ regulator-min-microvolt = <5000000>;
++ regulator-name = "5v0";
++ };
++};
++
++/ {
++ __overrides__ {
++ arm_freq;
++ };
++};
++
++pciex1: &pcie1 { };
++pciex4: &pcie2 { };
++
++&dma32 {
++ /* The VPU firmware uses DMA channel 11 for VCHIQ */
++ brcm,dma-channel-mask = <0x03f>;
++};
++
++&dma40 {
++ /* The VPU firmware DMA channel 11 for VCHIQ */
++ brcm,dma-channel-mask = <0x07c0>;
++};
++
++&hdmi0 {
++ dmas = <&dma40 (10|(1<<30)|(1<<24)|(10<<16)|(15<<20))>;
++};
++
++&hdmi1 {
++ dmas = <&dma40 (17|(1<<30)|(1<<24)|(10<<16)|(15<<20))>;
++};
++
++&spi10 {
++ dmas = <&dma40 6>, <&dma40 7>;
++ dma-names = "tx", "rx";
++};
++
++&usb {
++ power-domains = <&power RPI_POWER_DOMAIN_USB>;
++};
++
++&rmem {
++ /*
++ * RPi4's co-processor will copy the board's bootloader configuration
++ * into memory for the OS to consume. It'll also update this node with
++ * its placement information.
++ */
++ blconfig: nvram@0 {
++ compatible = "raspberrypi,bootloader-config", "nvmem-rmem";
++ #address-cells = <1>;
++ #size-cells = <1>;
++ reg = <0x0 0x0 0x0>;
++ no-map;
++ status = "disabled";
++ };
++};
++
++&rp1_adc {
++ 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>.
++ */
++
++&rp1 {
++ gpiomem@d0000 {
++ /* Export IO_BANKs, RIO_BANKs and PADS_BANKs to userspace */
++ compatible = "raspberrypi,gpiomem";
++ reg = <0xc0 0x400d0000 0x0 0x30000>;
++ chardev-name = "gpiomem0";
++ };
++};
++
++&soc {
++ gpiomem@7d508500 {
++ compatible = "raspberrypi,gpiomem";
++ reg = <0x7d508500 0x40>;
++ chardev-name = "gpiomem1";
++ };
++
++ gpiomem@7d517c00 {
++ compatible = "raspberrypi,gpiomem";
++ reg = <0x7d517c00 0x40>;
++ chardev-name = "gpiomem2";
++ };
++
++ gpiomem@7d504100 {
++ compatible = "raspberrypi,gpiomem";
++ reg = <0x7d504100 0x20>;
++ chardev-name = "gpiomem3";
++ };
++
++ gpiomem@7d510700 {
++ compatible = "raspberrypi,gpiomem";
++ reg = <0x7d510700 0x20>;
++ chardev-name = "gpiomem4";
++ };
++};
++
++i2c0: &rp1_i2c0 { };
++i2c1: &rp1_i2c1 { };
++i2c2: &rp1_i2c2 { };
++i2c3: &rp1_i2c3 { };
++i2c4: &rp1_i2c4 { };
++i2c5: &rp1_i2c5 { };
++i2c6: &rp1_i2c6 { };
++i2s: &rp1_i2s0 { };
++i2s_clk_producer: &rp1_i2s0 { };
++i2s_clk_consumer: &rp1_i2s1 { };
++pwm0: &rp1_pwm0 { };
++pwm1: &rp1_pwm1 { };
++pwm: &pwm0 { };
++spi0: &rp1_spi0 { };
++spi1: &rp1_spi1 { };
++spi2: &rp1_spi2 { };
++spi3: &rp1_spi3 { };
++spi4: &rp1_spi4 { };
++spi5: &rp1_spi5 { };
++
++uart0_pins: &rp1_uart0_14_15 {};
++uart0_ctsrts_pins: &rp1_uart0_ctsrts_16_17 {};
++uart0: &rp1_uart0 {
++ pinctrl-0 = <&uart0_pins>;
++};
++
++uart1_pins: &rp1_uart1_0_1 {};
++uart1_ctsrts_pins: &rp1_uart1_ctsrts_2_3 {};
++uart1: &rp1_uart1 { };
++
++uart2_pins: &rp1_uart2_4_5 {};
++uart2_ctsrts_pins: &rp1_uart2_ctsrts_6_7 {};
++uart2: &rp1_uart2 { };
++
++uart3_pins: &rp1_uart3_8_9 {};
++uart3_ctsrts_pins: &rp1_uart3_ctsrts_10_11 {};
++uart3: &rp1_uart3 { };
++
++uart4_pins: &rp1_uart4_12_13 {};
++uart4_ctsrts_pins: &rp1_uart4_ctsrts_14_15 {};
++uart4: &rp1_uart4 { };
++
++i2c_vc: &i2c0 { // This is pins 27,28 on the header (not MIPI)
++ pinctrl-0 = <&rp1_i2c0_0_1>;
++ pinctrl-names = "default";
++};
++
++i2c_arm: &i2c1 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rp1_i2c1_2_3>;
++};
++
++&i2c2 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rp1_i2c2_4_5>;
++};
++
++&i2c3 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rp1_i2c3_6_7>;
++};
++
++&i2s_clk_producer {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rp1_i2s0_18_21>;
++};
++
++&i2s_clk_consumer {
++ pinctrl-names = "default";
++ pinctrl-0 = <&rp1_i2s1_18_21>;
++};
++
++spi0_pins: &rp1_spi0_gpio9 {};
++spi0_cs_pins: &rp1_spi0_cs_gpio7 {};
++
++&spi0 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi0_pins &spi0_cs_pins>;
++ cs-gpios = <&gpio 8 1>, <&gpio 7 1>;
++
++ spidev0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ };
++
++ spidev1: spidev@1 {
++ compatible = "spidev";
++ reg = <1>; /* CE1 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ };
++};
++
++spi2_pins: &rp1_spi2_gpio1 {};
++&spi2 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi2_pins>;
++};
++
++spi3_pins: &rp1_spi3_gpio5 {};
++&spi3 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi3_pins>;
++};
++
++spi4_pins: &rp1_spi4_gpio9 {};
++&spi4 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi4_pins>;
++};
++
++spi5_pins: &rp1_spi5_gpio13 {};
++&spi5 {
++ pinctrl-names = "default";
++ pinctrl-0 = <&spi5_pins>;
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -0,0 +1,1287 @@
++// SPDX-License-Identifier: GPL-2.0
++#include <dt-bindings/interrupt-controller/arm-gic.h>
++#include <dt-bindings/soc/bcm2835-pm.h>
++#include <dt-bindings/phy/phy.h>
++
++/ {
++ compatible = "brcm,bcm2712", "brcm,bcm2711";
++ model = "BCM2712";
++
++ #address-cells = <2>;
++ #size-cells = <1>;
++
++ interrupt-parent = <&gicv2>;
++
++ rmem: reserved-memory {
++ #address-cells = <2>;
++ #size-cells = <1>;
++ ranges;
++
++ atf@0 {
++ reg = <0x0 0x0 0x80000>;
++ no-map;
++ };
++
++ cma: linux,cma {
++ compatible = "shared-dma-pool";
++ size = <0x4000000>; /* 64MB */
++ reusable;
++ linux,cma-default;
++
++ /*
++ * arm64 reserves the CMA by default somewhere in
++ * ZONE_DMA32, that's not good enough for the BCM2711
++ * as some devices can only address the lower 1G of
++ * memory (ZONE_DMA).
++ */
++ alloc-ranges = <0x0 0x00000000 0x40000000>;
++ };
++ };
++
++ thermal-zones {
++ cpu_thermal: cpu-thermal {
++ polling-delay-passive = <2000>;
++ polling-delay = <1000>;
++ coefficients = <(-550) 450000>;
++ thermal-sensors = <&thermal>;
++
++ thermal_trips: trips {
++ cpu_crit: cpu-crit {
++ temperature = <110000>;
++ hysteresis = <0>;
++ type = "critical";
++ };
++ };
++
++ cooling_maps: cooling-maps {
++ };
++ };
++ };
++
++ clk_27MHz: clk-27M {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <27000000>;
++ clock-output-names = "27MHz-clock";
++ };
++
++ clk_108MHz: clk-108M {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <108000000>;
++ clock-output-names = "108MHz-clock";
++ };
++
++ hvs: hvs@107c580000 {
++ compatible = "brcm,bcm2712-hvs";
++ reg = <0x10 0x7c580000 0x1a000>;
++ interrupt-parent = <&disp_intr>;
++ interrupts = <2>, <9>, <16>;
++ interrupt-names = "ch0-eof", "ch1-eof", "ch2-eof";
++ //iommus = <&iommu4>;
++ status = "disabled";
++ };
++
++ soc: soc {
++ compatible = "simple-bus";
++ #address-cells = <1>;
++ #size-cells = <1>;
++
++ ranges = <0x7c000000 0x10 0x7c000000 0x04000000>;
++ /* Emulate a contiguous 30-bit address range for DMA */
++ dma-ranges = <0xc0000000 0x00 0x00000000 0x40000000>,
++ <0x7c000000 0x10 0x7c000000 0x04000000>;
++
++ system_timer: timer@7c003000 {
++ compatible = "brcm,bcm2835-system-timer";
++ reg = <0x7c003000 0x1000>;
++ interrupts = <GIC_SPI 64 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
++ clock-frequency = <1000000>;
++ };
++
++ firmwarekms: firmwarekms@7d503000 {
++ compatible = "raspberrypi,rpi-firmware-kms";
++ /* SUN_L2 interrupt reg */
++ reg = <0x7d503000 0x18>;
++ interrupt-parent = <&cpu_l2_irq>;
++ interrupts = <19>;
++ brcm,firmware = <&firmware>;
++ status = "disabled";
++ };
++
++ mailbox: mailbox@7c013880 {
++ compatible = "brcm,bcm2835-mbox";
++ reg = <0x7c013880 0x40>;
++ interrupts = <GIC_SPI 33 IRQ_TYPE_LEVEL_HIGH>;
++ #mbox-cells = <0>;
++ };
++
++ pixelvalve0: pixelvalve@7c410000 {
++ compatible = "brcm,bcm2712-pixelvalve0";
++ reg = <0x7c410000 0x100>;
++ interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ pixelvalve1: pixelvalve@7c411000 {
++ compatible = "brcm,bcm2712-pixelvalve1";
++ reg = <0x7c411000 0x100>;
++ interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ usb: usb@7c480000 {
++ compatible = "brcm,bcm2835-usb";
++ reg = <0x7c480000 0x10000>;
++ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&clk_usb>;
++ clock-names = "otg";
++ phys = <&usbphy>;
++ phy-names = "usb2-phy";
++ status = "disabled";
++ };
++
++ mop: mop@7c500000 {
++ compatible = "brcm,bcm2712-mop";
++ reg = <0x7c500000 0x20>;
++ interrupt-parent = <&disp_intr>;
++ interrupts = <1>;
++ status = "disabled";
++ };
++
++ moplet: moplet@7c501000 {
++ compatible = "brcm,bcm2712-moplet";
++ reg = <0x7c501000 0x20>;
++ interrupt-parent = <&disp_intr>;
++ interrupts = <0>;
++ status = "disabled";
++ };
++
++ disp_intr: interrupt-controller@7c502000 {
++ compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
++ reg = <0x7c502000 0x30>;
++ interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ status = "disabled";
++ };
++
++ dvp: clock@7c700000 {
++ compatible = "brcm,brcm2711-dvp";
++ reg = <0x7c700000 0x10>;
++ clocks = <&clk_108MHz>;
++ #clock-cells = <1>;
++ #reset-cells = <1>;
++ };
++
++ /*
++ * This node is the provider for the enable-method for
++ * bringing up secondary cores.
++ */
++ local_intc: local_intc@7cd00000 {
++ compatible = "brcm,bcm2836-l1-intc";
++ reg = <0x7cd00000 0x100>;
++ };
++
++ uart0: serial@7d001000 {
++ compatible = "arm,pl011", "arm,primecell";
++ reg = <0x7d001000 0x200>;
++ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_uart>,
++ <&clk_vpu>;
++ clock-names = "uartclk", "apb_pclk";
++ arm,primecell-periphid = <0x00241011>;
++ status = "disabled";
++ };
++
++ uart2: serial@7d001400 {
++ compatible = "arm,pl011", "arm,primecell";
++ reg = <0x7d001400 0x200>;
++ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_uart>,
++ <&clk_vpu>;
++ clock-names = "uartclk", "apb_pclk";
++ arm,primecell-periphid = <0x00241011>;
++ status = "disabled";
++ };
++
++ uart3: serial@7d001600 {
++ compatible = "arm,pl011", "arm,primecell";
++ reg = <0x7d001600 0x200>;
++ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_uart>,
++ <&clk_vpu>;
++ clock-names = "uartclk", "apb_pclk";
++ arm,primecell-periphid = <0x00241011>;
++ status = "disabled";
++ };
++
++ uart4: serial@7d001800 {
++ compatible = "arm,pl011", "arm,primecell";
++ reg = <0x7d001800 0x200>;
++ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_uart>,
++ <&clk_vpu>;
++ clock-names = "uartclk", "apb_pclk";
++ arm,primecell-periphid = <0x00241011>;
++ status = "disabled";
++ };
++
++ uart5: serial@7d001a00 {
++ compatible = "arm,pl011", "arm,primecell";
++ reg = <0x7d001a00 0x200>;
++ interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_uart>,
++ <&clk_vpu>;
++ clock-names = "uartclk", "apb_pclk";
++ arm,primecell-periphid = <0x00241011>;
++ status = "disabled";
++ };
++
++ sdhost: mmc@7d002000 {
++ compatible = "brcm,bcm2835-sdhost";
++ reg = <0x7d002000 0x100>;
++ //interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ status = "disabled";
++ };
++
++ i2s: i2s@7d003000 {
++ compatible = "brcm,bcm2835-i2s";
++ reg = <0x7d003000 0x24>;
++ //clocks = <&cprman BCM2835_CLOCK_PCM>;
++ status = "disabled";
++ };
++
++ spi0: spi@7d004000 {
++ compatible = "brcm,bcm2835-spi";
++ reg = <0x7d004000 0x200>;
++ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ num-cs = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ spi3: spi@7d004600 {
++ compatible = "brcm,bcm2835-spi";
++ reg = <0x7d004600 0x0200>;
++ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ spi4: spi@7d004800 {
++ compatible = "brcm,bcm2835-spi";
++ reg = <0x7d004800 0x0200>;
++ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ spi5: spi@7d004a00 {
++ compatible = "brcm,bcm2835-spi";
++ reg = <0x7d004a00 0x0200>;
++ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ spi6: spi@7d004c00 {
++ compatible = "brcm,bcm2835-spi";
++ reg = <0x7d004c00 0x0200>;
++ interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ i2c0: i2c@7d005000 {
++ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++ reg = <0x7d005000 0x20>;
++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ i2c3: i2c@7d005600 {
++ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++ reg = <0x7d005600 0x20>;
++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ i2c4: i2c@7d005800 {
++ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++ reg = <0x7d005800 0x20>;
++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ i2c5: i2c@7d005a00 {
++ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++ reg = <0x7d005a00 0x20>;
++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ i2c6: i2c@7d005c00 {
++ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++ reg = <0x7d005c00 0x20>;
++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ i2c8: i2c@7d005e00 {
++ compatible = "brcm,bcm2711-i2c", "brcm,bcm2835-i2c";
++ reg = <0x7d005e00 0x20>;
++ interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_vpu>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ pwm0: pwm@7d00c000 {
++ compatible = "brcm,bcm2835-pwm";
++ reg = <0x7d00c000 0x28>;
++ assigned-clock-rates = <10000000>;
++ #pwm-cells = <2>;
++ status = "disabled";
++ };
++
++ pwm1: pwm@7d00c800 {
++ compatible = "brcm,bcm2835-pwm";
++ reg = <0x7d00c800 0x28>;
++ assigned-clock-rates = <10000000>;
++ #pwm-cells = <2>;
++ status = "disabled";
++ };
++
++ pm: watchdog@7d200000 {
++ compatible = "brcm,bcm2712-pm";
++ reg = <0x7d200000 0x308>;
++ reg-names = "pm";
++ #power-domain-cells = <1>;
++ #reset-cells = <1>;
++ //clocks = <&cprman BCM2835_CLOCK_V3D>,
++ // <&cprman BCM2835_CLOCK_PERI_IMAGE>,
++ // <&cprman BCM2835_CLOCK_H264>,
++ // <&cprman BCM2835_CLOCK_ISP>;
++ clock-names = "v3d", "peri_image", "h264", "isp";
++ system-power-controller;
++ };
++
++ cprman: cprman@7d202000 {
++ compatible = "brcm,bcm2711-cprman";
++ reg = <0x7d202000 0x2000>;
++ #clock-cells = <1>;
++
++ /* CPRMAN derives almost everything from the
++ * platform's oscillator. However, the DSI
++ * pixel clocks come from the DSI analog PHY.
++ */
++ clocks = <&clk_osc>;
++ status = "disabled";
++ };
++
++ random: rng@7d208000 {
++ compatible = "brcm,bcm2711-rng200";
++ reg = <0x7d208000 0x28>;
++ status = "okay";
++ };
++
++ cpu_l2_irq: intc@7d503000 {
++ compatible = "brcm,l2-intc";
++ reg = <0x7d503000 0x18>;
++ interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
++
++ pinctrl: pinctrl@7d504100 {
++ compatible = "brcm,bcm2712-pinctrl";
++ reg = <0x7d504100 0x30>;
++
++ uarta_24_pins: uarta_24_pins {
++ pin_rts {
++ function = "uart0";
++ pins = "gpio24";
++ bias-disable;
++ };
++ pin_cts {
++ function = "uart0";
++ pins = "gpio25";
++ bias-pull-up;
++ };
++ pin_txd {
++ function = "uart0";
++ pins = "gpio26";
++ bias-disable;
++ };
++ pin_rxd {
++ function = "uart0";
++ pins = "gpio27";
++ bias-pull-up;
++ };
++ };
++
++ sdio2_30_pins: sdio2_30_pins {
++ pin_clk {
++ function = "sd2";
++ pins = "gpio30";
++ bias-disable;
++ };
++ pin_cmd {
++ function = "sd2";
++ pins = "gpio31";
++ bias-pull-up;
++ };
++ pins_dat {
++ function = "sd2";
++ pins = "gpio32", "gpio33", "gpio34", "gpio35";
++ bias-pull-up;
++ };
++ };
++ };
++
++ ddc0: i2c@7d508200 {
++ compatible = "brcm,brcmstb-i2c";
++ reg = <0x7d508200 0x58>;
++ interrupt-parent = <&bsc_irq>;
++ interrupts = <1>;
++ clock-frequency = <200000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ ddc1: i2c@7d508280 {
++ compatible = "brcm,brcmstb-i2c";
++ reg = <0x7d508280 0x58>;
++ interrupt-parent = <&bsc_irq>;
++ interrupts = <2>;
++ clock-frequency = <200000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ bscd: i2c@7d508300 {
++ compatible = "brcm,brcmstb-i2c";
++ reg = <0x7d508300 0x58>;
++ interrupt-parent = <&bsc_irq>;
++ interrupts = <0>;
++ clock-frequency = <200000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ bsc_irq: intc@7d508380 {
++ compatible = "brcm,bcm7271-l2-intc";
++ reg = <0x7d508380 0x10>;
++ interrupts = <GIC_SPI 242 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
++
++ main_irq: intc@7d508400 {
++ compatible = "brcm,bcm7271-l2-intc";
++ reg = <0x7d508400 0x10>;
++ interrupts = <GIC_SPI 244 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
++
++ gio: gpio@7d508500 {
++ compatible = "brcm,brcmstb-gpio";
++ reg = <0x7d508500 0x40>;
++ interrupt-parent = <&main_irq>;
++ interrupts = <0>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ brcm,gpio-bank-widths = <32 22>;
++ brcm,gpio-direct;
++ };
++
++ uarta: serial@7d50c000 {
++ compatible = "brcm,bcm7271-uart";
++ reg = <0x7d50c000 0x20>;
++ reg-names = "uart";
++ reg-shift = <2>;
++ reg-io-width = <4>;
++ interrupts = <GIC_SPI 276 IRQ_TYPE_LEVEL_HIGH>;
++ skip-init;
++ status = "disabled";
++ };
++
++ uartb: serial@7d50d000 {
++ compatible = "brcm,bcm7271-uart";
++ reg = <0x7d50d000 0x20>;
++ reg-names = "uart";
++ reg-shift = <2>;
++ reg-io-width = <4>;
++ interrupts = <GIC_SPI 277 IRQ_TYPE_LEVEL_HIGH>;
++ skip-init;
++ status = "disabled";
++ };
++
++ uartc: serial@7d50e000 {
++ compatible = "brcm,bcm7271-uart";
++ reg = <0x7d50e000 0x20>;
++ reg-names = "uart";
++ reg-shift = <2>;
++ reg-io-width = <4>;
++ interrupts = <GIC_SPI 278 IRQ_TYPE_LEVEL_HIGH>;
++ skip-init;
++ status = "disabled";
++ };
++
++ aon_intr: interrupt-controller@7d510600 {
++ compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc";
++ reg = <0x7d510600 0x30>;
++ interrupts = <GIC_SPI 239 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ status = "disabled";
++ };
++
++ pinctrl_aon: pinctrl@7d510700 {
++ compatible = "brcm,bcm2712-aon-pinctrl";
++ reg = <0x7d510700 0x20>;
++
++ i2c3_m4_agpio0_pins: i2c3_m4_agpio0_pins {
++ function = "vc_i2c3";
++ pins = "aon_gpio0", "aon_gpio1";
++ bias-pull-up;
++ };
++
++ bsc_m1_agpio13_pins: bsc_m1_agpio13_pins {
++ function = "bsc_m1";
++ pins = "aon_gpio13", "aon_gpio14";
++ bias-pull-up;
++ };
++
++ bsc_pmu_sgpio4_pins: bsc_pmu_sgpio4_pins {
++ function = "avs_pmu_bsc";
++ pins = "aon_sgpio4", "aon_sgpio5";
++ };
++
++ bsc_m2_sgpio4_pins: bsc_m2_sgpio4_pins {
++ function = "bsc_m2";
++ pins = "aon_sgpio4", "aon_sgpio5";
++ };
++
++ pwm_aon_agpio1_pins: pwm_aon_agpio1_pins {
++ function = "aon_pwm";
++ pins = "aon_gpio1", "aon_gpio2";
++ };
++
++ pwm_aon_agpio4_pins: pwm_aon_agpio4_pins {
++ function = "vc_pwm0";
++ pins = "aon_gpio4", "aon_gpio5";
++ };
++
++ pwm_aon_agpio7_pins: pwm_aon_agpio7_pins {
++ function = "aon_pwm";
++ pins = "aon_gpio7", "aon_gpio9";
++ };
++ };
++
++ intc@7d517000 {
++ compatible = "brcm,bcm7271-l2-intc";
++ reg = <0x7d517000 0x10>;
++ interrupts = <GIC_SPI 247 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ status = "disabled";
++ };
++
++ bscc: i2c@7d517a00 {
++ compatible = "brcm,brcmstb-i2c";
++ reg = <0x7d517a00 0x58>;
++ interrupt-parent = <&bsc_aon_irq>;
++ interrupts = <0>;
++ clock-frequency = <200000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ pwm_aon: pwm@7d517a80 {
++ compatible = "brcm,bcm7038-pwm";
++ reg = <0x7d517a80 0x28>;
++ #pwm-cells = <2>;
++ clocks = <&clk_27MHz>;
++ };
++
++ main_aon_irq: intc@7d517ac0 {
++ compatible = "brcm,bcm7271-l2-intc";
++ reg = <0x7d517ac0 0x10>;
++ interrupts = <GIC_SPI 245 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
++
++ bsc_aon_irq: intc@7d517b00 {
++ compatible = "brcm,bcm7271-l2-intc";
++ reg = <0x7d517b00 0x10>;
++ interrupts = <GIC_SPI 243 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-controller;
++ #interrupt-cells = <1>;
++ };
++
++ gio_aon: gpio@7d517c00 {
++ compatible = "brcm,brcmstb-gpio";
++ reg = <0x7d517c00 0x40>;
++ interrupt-parent = <&main_aon_irq>;
++ interrupts = <0>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ brcm,gpio-bank-widths = <17 6>;
++ brcm,gpio-direct;
++ };
++
++ avs_monitor: avs-monitor@7d542000 {
++ compatible = "brcm,bcm2711-avs-monitor",
++ "syscon", "simple-mfd";
++ reg = <0x7d542000 0xf00>;
++ status = "okay";
++
++ thermal: thermal {
++ compatible = "brcm,bcm2711-thermal";
++ #thermal-sensor-cells = <0>;
++ };
++ };
++
++ bsc_pmu: i2c@7d544000 {
++ compatible = "brcm,brcmstb-i2c";
++ reg = <0x7d544000 0x58>;
++ interrupt-parent = <&bsc_aon_irq>;
++ interrupts = <1>;
++ clock-frequency = <200000>;
++ status = "disabled";
++ };
++
++ hdmi0: hdmi@7ef00700 {
++ compatible = "brcm,bcm2712-hdmi0";
++ reg = <0x7c701400 0x300>,
++ <0x7c701000 0x200>,
++ <0x7c701d00 0x300>,
++ <0x7c702000 0x80>,
++ <0x7c703800 0x200>,
++ <0x7c704000 0x800>,
++ <0x7c700100 0x80>,
++ <0x7d510800 0x100>,
++ <0x7c720000 0x100>;
++ reg-names = "hdmi",
++ "dvp",
++ "phy",
++ "rm",
++ "packet",
++ "metadata",
++ "csc",
++ "cec",
++ "hd";
++ resets = <&dvp 1>;
++ interrupt-parent = <&aon_intr>;
++ interrupts = <1>, <2>, <3>,
++ <7>, <8>;
++ interrupt-names = "cec-tx", "cec-rx", "cec-low",
++ "hpd-connected", "hpd-removed";
++ ddc = <&ddc0>;
++ dmas = <&dma32 10>;
++ dma-names = "audio-rx";
++ status = "disabled";
++ };
++
++ hdmi1: hdmi@7ef05700 {
++ compatible = "brcm,bcm2712-hdmi1";
++ reg = <0x7c706400 0x300>,
++ <0x7c706000 0x200>,
++ <0x7c706d00 0x300>,
++ <0x7c707000 0x80>,
++ <0x7c708800 0x200>,
++ <0x7c709000 0x800>,
++ <0x7c700180 0x80>,
++ <0x7d511000 0x100>,
++ <0x7c720000 0x100>;
++ reg-names = "hdmi",
++ "dvp",
++ "phy",
++ "rm",
++ "packet",
++ "metadata",
++ "csc",
++ "cec",
++ "hd";
++ ddc = <&ddc1>;
++ resets = <&dvp 2>;
++ interrupt-parent = <&aon_intr>;
++ interrupts = <11>, <12>, <13>,
++ <14>, <15>;
++ interrupt-names = "cec-tx", "cec-rx", "cec-low",
++ "hpd-connected", "hpd-removed";
++ dmas = <&dma32 17>;
++ dma-names = "audio-rx";
++ status = "disabled";
++ };
++
++ sound: sound {
++ };
++ };
++
++ arm-pmu {
++ compatible = "arm,cortex-a76-pmu";
++ interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 18 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>;
++ };
++
++ timer {
++ compatible = "arm,armv8-timer";
++ interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) |
++ IRQ_TYPE_LEVEL_LOW)>,
++ <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) |
++ IRQ_TYPE_LEVEL_LOW)>,
++ <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) |
++ IRQ_TYPE_LEVEL_LOW)>,
++ <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) |
++ IRQ_TYPE_LEVEL_LOW)>;
++ /* This only applies to the ARMv7 stub */
++ arm,cpu-registers-not-fw-configured;
++ };
++
++ cpus: cpus {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ enable-method = "brcm,bcm2836-smp"; // for ARM 32-bit
++
++ cpu0: cpu@0 {
++ device_type = "cpu";
++ compatible = "arm,cortex-a76";
++ reg = <0x000>;
++ enable-method = "psci";
++ next-level-cache = <&l2_cache>;
++ };
++
++ cpu1: cpu@1 {
++ device_type = "cpu";
++ compatible = "arm,cortex-a76";
++ reg = <0x100>;
++ enable-method = "psci";
++ next-level-cache = <&l2_cache>;
++ };
++
++ cpu2: cpu@2 {
++ device_type = "cpu";
++ compatible = "arm,cortex-a76";
++ reg = <0x200>;
++ enable-method = "psci";
++ next-level-cache = <&l2_cache>;
++ };
++
++ cpu3: cpu@3 {
++ device_type = "cpu";
++ compatible = "arm,cortex-a76";
++ reg = <0x300>;
++ enable-method = "psci";
++ next-level-cache = <&l2_cache>;
++ };
++
++ l2_cache: l2-cache {
++ compatible = "cache";
++ next-level-cache = <&l3_cache>;
++ };
++
++ l3_cache: l3-cache {
++ compatible = "cache";
++ };
++ };
++
++ psci {
++ method = "smc";
++ compatible = "arm,psci-1.0", "arm,psci-0.2", "arm,psci";
++ cpu_on = <0xc4000003>;
++ cpu_suspend = <0xc4000001>;
++ cpu_off = <0x84000002>;
++ };
++
++ axi: axi {
++ compatible = "simple-bus";
++ #address-cells = <2>;
++ #size-cells = <2>;
++
++ ranges = <0x00 0x00000000 0x00 0x00000000 0x10 0x00000000>,
++ <0x10 0x00000000 0x10 0x00000000 0x01 0x00000000>,
++ <0x14 0x00000000 0x14 0x00000000 0x04 0x00000000>,
++ <0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>,
++ <0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>;
++
++ dma-ranges = <0x00 0x00000000 0x00 0x00000000 0x10 0x00000000>,
++ <0x10 0x00000000 0x10 0x00000000 0x01 0x00000000>,
++ <0x14 0x00000000 0x14 0x00000000 0x04 0x00000000>,
++ <0x18 0x00000000 0x18 0x00000000 0x04 0x00000000>,
++ <0x1c 0x00000000 0x1c 0x00000000 0x04 0x00000000>;
++
++ vc4: gpu {
++ compatible = "brcm,bcm2712-vc6";
++ };
++
++ iommu2: iommu@5100 {
++ /* IOMMU2 for PISP-BE, HEVC; and (unused) H264 accelerators */
++ compatible = "brcm,bcm2712-iommu";
++ reg = <0x10 0x5100 0x0 0x80>;
++ cache = <&iommuc>;
++ #iommu-cells = <0>;
++ };
++
++ iommu4: iommu@5200 {
++ /* IOMMU4 for HVS, MPL/TXP; and (unused) Unicam, PISP-FE, MiniBVN */
++ compatible = "brcm,bcm2712-iommu";
++ reg = <0x10 0x5200 0x0 0x80>;
++ cache = <&iommuc>;
++ #iommu-cells = <0>;
++ #interconnect-cells = <0>;
++ };
++
++ iommu5: iommu@5280 {
++ /* IOMMU5 for PCIe2 (RP1); and (unused) BSTM */
++ compatible = "brcm,bcm2712-iommu";
++ reg = <0x10 0x5280 0x0 0x80>;
++ cache = <&iommuc>;
++ #iommu-cells = <0>;
++ dma-iova-offset = <0x10 0x00000000>; // HACK for RP1 masters over PCIe
++ };
++
++ iommuc: iommuc@5b00 {
++ compatible = "brcm,bcm2712-iommuc";
++ reg = <0x10 0x5b00 0x0 0x80>;
++ };
++
++ dma32: dma@10000 {
++ compatible = "brcm,bcm2712-dma";
++ reg = <0x10 0x00010000 0 0x600>;
++ interrupts = <GIC_SPI 80 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 81 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "dma0",
++ "dma1",
++ "dma2",
++ "dma3",
++ "dma4",
++ "dma5";
++ #dma-cells = <1>;
++ brcm,dma-channel-mask = <0x0035>;
++ };
++
++ dma40: dma@10600 {
++ compatible = "brcm,bcm2712-dma";
++ reg = <0x10 0x00010600 0 0x600>;
++ interrupts =
++ <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>, /* dma4 6 */
++ <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>, /* dma4 7 */
++ <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>, /* dma4 8 */
++ <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>, /* dma4 9 */
++ <GIC_SPI 90 IRQ_TYPE_LEVEL_HIGH>, /* dma4 10 */
++ <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>; /* dma4 11 */
++ interrupt-names = "dma6",
++ "dma7",
++ "dma8",
++ "dma9",
++ "dma10",
++ "dma11";
++ #dma-cells = <1>;
++ brcm,dma-channel-mask = <0x0fc0>;
++ };
++
++ // Single-lane Gen3 PCIe
++ // Outbound window at 0x14_000000-0x17_ffffff
++ pcie0: pcie@100000 {
++ compatible = "brcm,bcm2712-pcie";
++ reg = <0x10 0x00100000 0x0 0x9310>;
++ device_type = "pci";
++ max-link-speed = <2>;
++ #address-cells = <3>;
++ #interrupt-cells = <1>;
++ #size-cells = <2>;
++ /*
++ * Unused interrupts:
++ * 208: AER
++ * 215: NMI
++ * 216: PME
++ */
++ interrupt-parent = <&gicv2>;
++ interrupts = <GIC_SPI 213 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 214 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "pcie", "msi";
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 209
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 2 &gicv2 GIC_SPI 210
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 3 &gicv2 GIC_SPI 211
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 4 &gicv2 GIC_SPI 212
++ IRQ_TYPE_LEVEL_HIGH>;
++ resets = <&bcm_reset 5>, <&bcm_reset 42>, <&pcie_rescal>;
++ reset-names = "swinit", "bridge", "rescal";
++ msi-controller;
++ msi-parent = <&pcie0>;
++
++ ranges = <0x02000000 0x00 0x00000000
++ 0x17 0x00000000
++ 0x0 0xfffffffc>,
++ <0x43000000 0x04 0x00000000
++ 0x14 0x00000000
++ 0x3 0x00000000>;
++
++ dma-ranges = <0x43000000 0x10 0x00000000
++ 0x00 0x00000000
++ 0x10 0x00000000>;
++
++ status = "disabled";
++ };
++
++ // Single-lane Gen3 PCIe
++ // Outbound window at 0x18_000000-0x1b_ffffff
++ pcie1: pcie@110000 {
++ compatible = "brcm,bcm2712-pcie";
++ reg = <0x10 0x00110000 0x0 0x9310>;
++ device_type = "pci";
++ max-link-speed = <2>;
++ #address-cells = <3>;
++ #interrupt-cells = <1>;
++ #size-cells = <2>;
++ /*
++ * Unused interrupts:
++ * 218: AER
++ * 225: NMI
++ * 226: PME
++ */
++ interrupt-parent = <&gicv2>;
++ interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 224 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "pcie", "msi";
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 219
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 2 &gicv2 GIC_SPI 220
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 3 &gicv2 GIC_SPI 221
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 4 &gicv2 GIC_SPI 222
++ IRQ_TYPE_LEVEL_HIGH>;
++ resets = <&bcm_reset 7>, <&bcm_reset 43>, <&pcie_rescal>;
++ reset-names = "swinit", "bridge", "rescal";
++ msi-controller;
++ msi-parent = <&mip1>;
++
++ ranges = <0x02000000 0x00 0x00000000
++ 0x1b 0x00000000
++ 0x00 0xfffffffc>,
++ <0x43000000 0x04 0x00000000
++ 0x18 0x00000000
++ 0x03 0x00000000>;
++
++ dma-ranges = <0x03000000 0x10 0x00000000
++ 0x00 0x00000000
++ 0x10 0x00000000>;
++
++ brcm,enable-l1ss;
++ status = "disabled";
++ };
++
++ pcie_rescal: reset-controller@119500 {
++ compatible = "brcm,bcm7216-pcie-sata-rescal";
++ reg = <0x10 0x00119500 0x0 0x10>;
++ #reset-cells = <0>;
++ };
++
++ // Quad-lane Gen3 PCIe
++ // Outbound window at 0x1c_000000-0x1f_ffffff
++ pcie2: pcie@120000 {
++ compatible = "brcm,bcm2712-pcie";
++ reg = <0x10 0x00120000 0x0 0x9310>;
++ device_type = "pci";
++ max-link-speed = <2>;
++ #address-cells = <3>;
++ #interrupt-cells = <1>;
++ #size-cells = <2>;
++ /*
++ * Unused interrupts:
++ * 228: AER
++ * 235: NMI
++ * 236: PME
++ */
++ interrupt-parent = <&gicv2>;
++ interrupts = <GIC_SPI 233 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 234 IRQ_TYPE_LEVEL_HIGH>;
++ interrupt-names = "pcie", "msi";
++ interrupt-map-mask = <0x0 0x0 0x0 0x7>;
++ interrupt-map = <0 0 0 1 &gicv2 GIC_SPI 229
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 2 &gicv2 GIC_SPI 230
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 3 &gicv2 GIC_SPI 231
++ IRQ_TYPE_LEVEL_HIGH>,
++ <0 0 0 4 &gicv2 GIC_SPI 232
++ IRQ_TYPE_LEVEL_HIGH>;
++ resets = <&bcm_reset 32>, <&bcm_reset 44>, <&pcie_rescal>;
++ reset-names = "swinit", "bridge", "rescal";
++ msi-controller;
++ msi-parent = <&mip0>;
++
++ // ~4GB, 32-bit, not-prefetchable at PCIe 00_00000000
++ ranges = <0x02000000 0x00 0x00000000
++ 0x1f 0x00000000
++ 0x0 0xfffffffc>,
++ // 12GB, 64-bit, prefetchable at PCIe 04_00000000
++ <0x43000000 0x04 0x00000000
++ 0x1c 0x00000000
++ 0x03 0x00000000>;
++
++ // 64GB system RAM space at PCIe 10_00000000
++ dma-ranges = <0x02000000 0x00 0x00000000
++ 0x1f 0x00000000
++ 0x00 0x00400000>,
++ <0x43000000 0x10 0x00000000
++ 0x00 0x00000000
++ 0x10 0x00000000>;
++
++ brcm,enable-mps-rcb;
++ brcm,enable-l1ss;
++ status = "disabled";
++ };
++
++ mip0: msi-controller@130000 {
++ compatible = "brcm,bcm2712-mip-intc";
++ reg = <0x10 0x00130000 0x0 0xc0>;
++ msi-controller;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ brcm,msi-base-spi = <128>;
++ brcm,msi-num-spis = <64>;
++ brcm,msi-offset = <0>;
++ brcm,msi-pci-addr = <0xff 0xfffff000>;
++ };
++
++ mip1: msi-controller@131000 {
++ compatible = "brcm,bcm2712-mip-intc";
++ reg = <0x10 0x00131000 0x0 0xc0>;
++ msi-controller;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++ brcm,msi-base-spi = <247>;
++ /* Actually 20 total, but the others are
++ * both sparse and non-consecutive */
++ brcm,msi-num-spis = <8>;
++ brcm,msi-offset = <8>;
++ brcm,msi-pci-addr = <0xff 0xffffe000>;
++ };
++
++ genet: ethernet@1300000 {
++ compatible = "brcm,bcm2711-genet-v5";
++ reg = <0x10 0x01300000 0x0 0x20010>;
++ #address-cells = <0x1>;
++ #size-cells = <0x0>;
++ interrupts = <GIC_SPI 265 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 266 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ phy-mode = "rgmii";
++ fixed-link = <0x0 0x1 0x3e8 0x0 0x0>;
++ phy-speed = <0x3e8>;
++ phy-id = <0x101>;
++ phy-type = <0x6>;
++ local-mac-address = [ 00 10 18 d8 45 de ];
++ device_type = "network";
++
++ genet_mdio: mdio@e14 {
++ compatible = "brcm,genet-mdio-v5";
++ reg = <0xe14 0x8>;
++ #address-cells = <0x1>;
++ #size-cells = <0x0>;
++ };
++ };
++
++ syscon_piarbctl: syscon@400018 {
++ compatible = "brcm,syscon-piarbctl", "syscon", "simple-mfd";
++ reg = <0x10 0x00400018 0x0 0x18>;
++ };
++
++ rpivid: codec@800000 {
++ compatible = "raspberrypi,rpivid-vid-decoder";
++ reg = <0x10 0x00800000 0x0 0x10000>, /* HEVC */
++ <0x10 0x00840000 0x0 0x1000>; /* INTC */
++ reg-names = "hevc",
++ "intc";
++
++ interrupts = <GIC_SPI 98 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&firmware_clocks 11>;
++ clock-names = "hevc";
++ status = "disabled";
++ };
++
++ sdio1: mmc@fff000 {
++ compatible = "brcm,bcm2712-sdhci";
++ reg = <0x10 0x00fff000 0x0 0x260>,
++ <0x10 0x00fff400 0x0 0x200>,
++ <0x10 0x015040b0 0x0 0x4>, // Bus isolation control
++ <0x10 0x015200f0 0x0 0x24>; // LCPLL control misc0-8
++ reg-names = "host", "cfg", "busisol", "lcpll";
++ interrupts = <GIC_SPI 273 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_emmc2>;
++ sdhci-caps-mask = <0x0000C000 0x0>;
++ sdhci-caps = <0x0 0x0>;
++ supports-cqe;
++ mmc-ddr-3_3v;
++ };
++
++ sdio2: mmc@1100000 {
++ compatible = "brcm,bcm2712-sdhci";
++ reg = <0x10 0x01100000 0x0 0x260>,
++ <0x10 0x01100400 0x0 0x200>;
++ reg-names = "host", "cfg";
++ interrupts = <GIC_SPI 274 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_emmc2>;
++ sdhci-caps-mask = <0x0000C000 0x0>;
++ sdhci-caps = <0x0 0x0>;
++ supports-cqe;
++ mmc-ddr-3_3v;
++ status = "disabled";
++ };
++
++ sdio0: mmc@1108000 {
++ compatible = "brcm,bcm2711-emmc2";
++ reg = <0x10 0x01108000 0x0 0x100>;
++ interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&clk_emmc2>;
++ mmc-ddr-3_3v;
++ status = "disabled";
++ };
++
++ bcm_reset: reset-controller@1504318 {
++ compatible = "brcm,brcmstb-reset";
++ reg = <0x10 0x01504318 0x0 0x30>;
++ #reset-cells = <1>;
++ };
++
++ v3d: v3d@2000000 {
++ compatible = "brcm,2712-v3d";
++ reg = <0x10 0x02000000 0x0 0x4000>,
++ <0x10 0x02008000 0x0 0x6000>;
++ reg-names = "hub", "core0";
++
++ power-domains = <&pm BCM2835_POWER_DOMAIN_GRAFX_V3D>;
++ resets = <&pm BCM2835_RESET_V3D>;
++ clocks = <&firmware_clocks 5>;
++ clocks-names = "v3d";
++ interrupts = <GIC_SPI 250 IRQ_TYPE_LEVEL_HIGH>,
++ <GIC_SPI 249 IRQ_TYPE_LEVEL_HIGH>;
++ status = "disabled";
++ };
++
++ gicv2: interrupt-controller@7fff9000 {
++ interrupt-controller;
++ #interrupt-cells = <3>;
++ compatible = "arm,gic-400";
++ reg = <0x10 0x7fff9000 0x0 0x1000>,
++ <0x10 0x7fffa000 0x0 0x2000>,
++ <0x10 0x7fffc000 0x0 0x2000>,
++ <0x10 0x7fffe000 0x0 0x2000>;
++ interrupts = <GIC_PPI 9 (GIC_CPU_MASK_SIMPLE(4) |
++ IRQ_TYPE_LEVEL_HIGH)>;
++ };
++
++ pisp_be: pisp_be@880000 {
++ compatible = "raspberrypi,pispbe";
++ reg = <0x10 0x00880000 0x0 0x4000>;
++ interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&firmware_clocks 7>;
++ clocks-names = "isp_be";
++ status = "okay";
++ iommus = <&iommu2>;
++ };
++ };
++
++ clocks {
++ /* The oscillator is the root of the clock tree. */
++ clk_osc: clk-osc {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "osc";
++ clock-frequency = <54000000>;
++ };
++
++ clk_usb: clk-usb {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "otg";
++ clock-frequency = <480000000>;
++ };
++
++ clk_vpu: clk_vpu {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <750000000>;
++ clock-output-names = "vpu-clock";
++ };
++
++ clk_uart: clk_uart {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <9216000>;
++ clock-output-names = "uart-clock";
++ };
++
++ clk_emmc2: clk_emmc2 {
++ #clock-cells = <0>;
++ compatible = "fixed-clock";
++ clock-frequency = <54000000>;
++ clock-output-names = "emmc2-clock";
++ };
++ };
++
++ usbphy: phy {
++ compatible = "usb-nop-xceiv";
++ #phy-cells = <0>;
++ };
++};
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -49,8 +49,10 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ dionaudio-loco.dtbo \
+ dionaudio-loco-v2.dtbo \
+ disable-bt.dtbo \
++ disable-bt-pi5.dtbo \
+ disable-emmc2.dtbo \
+ disable-wifi.dtbo \
++ disable-wifi-pi5.dtbo \
+ dpi18.dtbo \
+ dpi18cpadhi.dtbo \
+ dpi24.dtbo \
+@@ -106,8 +108,12 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ i2c-rtc-gpio.dtbo \
+ i2c-sensor.dtbo \
+ i2c0.dtbo \
++ i2c0-pi5.dtbo \
+ i2c1.dtbo \
++ i2c1-pi5.dtbo \
++ i2c2-pi5.dtbo \
+ i2c3.dtbo \
++ i2c3-pi5.dtbo \
+ i2c4.dtbo \
+ i2c5.dtbo \
+ i2c6.dtbo \
+@@ -150,10 +156,15 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ media-center.dtbo \
+ merus-amp.dtbo \
+ midi-uart0.dtbo \
++ midi-uart0-pi5.dtbo \
+ midi-uart1.dtbo \
++ midi-uart1-pi5.dtbo \
+ midi-uart2.dtbo \
++ midi-uart2-pi5.dtbo \
+ midi-uart3.dtbo \
++ midi-uart3-pi5.dtbo \
+ midi-uart4.dtbo \
++ midi-uart4-pi5.dtbo \
+ midi-uart5.dtbo \
+ minipitft13.dtbo \
+ miniuart-bt.dtbo \
+@@ -231,14 +242,20 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ spi1-2cs.dtbo \
+ spi1-3cs.dtbo \
+ spi2-1cs.dtbo \
++ spi2-1cs-pi5.dtbo \
+ spi2-2cs.dtbo \
++ spi2-2cs-pi5.dtbo \
+ spi2-3cs.dtbo \
+ spi3-1cs.dtbo \
++ spi3-1cs-pi5.dtbo \
+ spi3-2cs.dtbo \
++ spi3-2cs-pi5.dtbo \
+ spi4-1cs.dtbo \
+ spi4-2cs.dtbo \
+ spi5-1cs.dtbo \
++ spi5-1cs-pi5.dtbo \
+ spi5-2cs.dtbo \
++ spi5-2cs-pi5.dtbo \
+ spi6-1cs.dtbo \
+ spi6-2cs.dtbo \
+ ssd1306.dtbo \
+@@ -253,10 +270,15 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ tpm-slb9670.dtbo \
+ tpm-slb9673.dtbo \
+ uart0.dtbo \
++ uart0-pi5.dtbo \
+ uart1.dtbo \
++ uart1-pi5.dtbo \
+ uart2.dtbo \
++ uart2-pi5.dtbo \
+ uart3.dtbo \
++ uart3-pi5.dtbo \
+ uart4.dtbo \
++ uart4-pi5.dtbo \
+ uart5.dtbo \
+ udrc.dtbo \
+ ugreen-dabboard.dtbo \
+@@ -276,6 +298,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ vc4-kms-kippah-7inch.dtbo \
+ vc4-kms-v3d.dtbo \
+ vc4-kms-v3d-pi4.dtbo \
++ vc4-kms-v3d-pi5.dtbo \
+ vc4-kms-vga666.dtbo \
+ vga666.dtbo \
+ vl805.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -151,6 +151,9 @@ Params:
+ bdaddr=06:05:04:03:02:01
+ will set the BDADDR to 01:02:03:04:05:06.
+
++ button_debounce Set the debounce delay (in ms) on the power/
++ shutdown button (default 50ms)
++
+ cam0_reg Enables CAM 0 regulator.
+ Only required on CM1 & 3.
+
+@@ -167,6 +170,9 @@ Params:
+ Default of GPIO expander 5 on CM4, but override
+ switches to normal GPIO.
+
++ cooling_fan Enables the Pi 5 cooling fan (enabled
++ automatically by the firmware)
++
+ eee Enable Energy Efficient Ethernet support for
+ compatible devices (default "on"). See also
+ "tx_lpi_timer". Pi3B+ only.
+@@ -206,23 +212,29 @@ Params:
+ hdmi Set to "off" to disable the HDMI interface
+ (default "on")
+
++ i2c An alias for i2c_arm
++
+ i2c_arm Set to "on" to enable the ARM's i2c interface
+ (default "off")
+
++ i2c_arm_baudrate Set the baudrate of the ARM's i2c interface
++ (default "100000")
++
++ i2c_baudrate An alias for i2c_arm_baudrate
++
++ i2c_csi_dsi Set to "on" to enable the i2c_csi_dsi interface
++
++ i2c_csi_dsi0 Set to "on" to enable the i2c_csi_dsi0 interface
++
++ i2c_csi_dsi1 Set to "on" to enable the i2c_csi_dsi1 interface
++
+ i2c_vc Set to "on" to enable the i2c interface
+ usually reserved for the VideoCore processor
+ (default "off")
+
+- i2c An alias for i2c_arm
+-
+- i2c_arm_baudrate Set the baudrate of the ARM's i2c interface
+- (default "100000")
+-
+ i2c_vc_baudrate Set the baudrate of the VideoCore i2c interface
+ (default "100000")
+
+- i2c_baudrate An alias for i2c_arm_baudrate
+-
+ i2s Set to "on" to enable the i2s interface
+ (default "off")
+
+@@ -237,11 +249,23 @@ Params:
+ krnbt_baudrate Set the baudrate of the PL011 UART when used
+ with krnbt=on
+
++ nvme Alias for "pciex1" (2712 only)
++
+ pcie Set to "off" to disable the PCIe interface
+ (default "on")
+ (2711 only, but not applicable on CM4S)
+ N.B. USB-A ports on 4B are subsequently disabled
+
++ pciex1 Set to "on" to enable the external PCIe link
++ (2712 only, default "off")
++
++ pciex1_gen Sets the PCIe "GEN"/speed for the external PCIe
++ link (2712 only, default "2")
++
++ pciex1_no_l0s Set to "on" to disable ASPM L0s on the external
++ PCIe link for devices that have broken
++ implementations (2712 only, default "off")
++
+ spi Set to "on" to enable the spi interfaces
+ (default "off")
+
+@@ -252,6 +276,11 @@ Params:
+ random Set to "on" to enable the hardware random
+ number generator (default "on")
+
++ rtc_bbat_vchg Set the RTC backup battery charging voltage in
++ microvolts. If set to 0 or not specified, the
++ trickle charger is disabled.
++ (2712 only, default "0")
++
+ sd Set to "off" to disable the SD card (or eMMC on
+ non-lite SKU of CM4).
+ (default "on")
+@@ -276,18 +305,30 @@ Params:
+ sdio_overclock Clock (in MHz) to use when the MMC framework
+ requests 50MHz for the SDIO/WLAN interface.
+
++ suspend Make the power button trigger a suspend rather
++ than a power-off (2712 only, default "off")
++
+ tx_lpi_timer Set the delay in microseconds between going idle
+ and entering the low power state (default 600).
+ Requires EEE to be enabled - see "eee".
+
+ uart0 Set to "off" to disable uart0 (default "on")
+
++ uart0_console Move the kernel boot console to UART0 on pins
++ 6, 8 and 10 of the 40-way header (2712 only,
++ default "off")
++
+ uart1 Set to "on" or "off" to enable or disable uart1
+ (default varies)
+
+ watchdog Set to "on" to enable the hardware watchdog
+ (default "off")
+
++ wifiaddr Set an alternative WiFi MAC address.
++ The value should be a 6-byte hexadecimal value,
++ with or without colon separators, written in the
++ natural (big-endian) order.
++
+ act_led_trigger Choose which activity the LED tracks.
+ Use "heartbeat" for a nice load indicator.
+ (default "mmc")
+@@ -919,14 +960,16 @@ Params: 24db_digital_gain Allow ga
+
+
+ Name: disable-bt
+-Info: Disable onboard Bluetooth on Pi 3B, 3B+, 3A+, 4B and Zero W, restoring
+- UART0/ttyAMA0 over GPIOs 14 & 15.
+- N.B. To disable the systemd service that initialises the modem so it
+- doesn't use the UART, use 'sudo systemctl disable hciuart'.
++Info: Disable onboard Bluetooth on Bluetooth-capable Raspberry Pis. On Pis
++ prior to Pi 5 this restores UART0/ttyAMA0 over GPIOs 14 & 15.
+ Load: dtoverlay=disable-bt
+ Params: <None>
+
+
++Name: disable-bt-pi5
++Info: See disable-bt
++
++
+ Name: disable-emmc2
+ Info: Disable EMMC2 controller on BCM2711.
+ The allows the onboard EMMC storage on Compute Module 4 to be disabled
+@@ -936,11 +979,15 @@ Params: <None>
+
+
+ Name: disable-wifi
+-Info: Disable onboard WLAN on Pi 3B, 3B+, 3A+, 4B and Zero W.
++Info: Disable onboard WLAN on WiFi-capable Raspberry Pis.
+ Load: dtoverlay=disable-wifi
+ Params: <None>
+
+
++Name: disable-wifi-pi5
++Info: See disable-wifi
++
++
+ Name: dpi18
+ Info: Overlay for a generic 18-bit DPI display
+ This uses GPIOs 0-21 (so no I2C, uart etc.), and activates the output
+@@ -2233,6 +2280,15 @@ Info: Deprecated, legacy version of i2
+ Load: <Deprecated>
+
+
++Name: i2c0-pi5
++Info: Enable i2c0 (Pi 5 only)
++Load: dtoverlay=i2c0-pi5,<param>=<val>
++Params: pins_0_1 Use GPIOs 0 and 1 (default)
++ pins_8_9 Use GPIOs 8 and 9
++ baudrate Set the baudrate for the interface (default
++ "100000")
++
++
+ Name: i2c1
+ Info: Change i2c1 pin usage. Not all pin combinations are usable on all
+ platforms - platforms other then Compute Modules can only use this
+@@ -2249,6 +2305,24 @@ Info: Deprecated, legacy version of i2
+ Load: <Deprecated>
+
+
++Name: i2c1-pi5
++Info: Enable i2c1 (Pi 5 only)
++Load: dtoverlay=i2c1-pi5,<param>=<val>
++Params: pins_2_3 Use GPIOs 2 and 3 (default)
++ pins_10_11 Use GPIOs 10 and 11
++ baudrate Set the baudrate for the interface (default
++ "100000")
++
++
++Name: i2c2-pi5
++Info: Enable i2c2 (Pi 5 only)
++Load: dtoverlay=i2c2-pi5,<param>=<val>
++Params: pins_4_5 Use GPIOs 4 and 5 (default)
++ pins_12_13 Use GPIOs 12 and 13
++ baudrate Set the baudrate for the interface (default
++ "100000")
++
++
+ Name: i2c3
+ Info: Enable the i2c3 bus. BCM2711 only.
+ Load: dtoverlay=i2c3,<param>
+@@ -2258,6 +2332,16 @@ Params: pins_2_3 Use GPIO
+ "100000")
+
+
++Name: i2c3-pi5
++Info: Enable i2c3 (Pi 5 only)
++Load: dtoverlay=i2c3-pi5,<param>=<val>
++Params: pins_6_7 Use GPIOs 6 and 7 (default)
++ pins_14_15 Use GPIOs 14 and 15
++ pins_22_23 Use GPIOs 22 and 23
++ baudrate Set the baudrate for the interface (default
++ "100000")
++
++
+ Name: i2c4
+ Info: Enable the i2c4 bus. BCM2711 only.
+ Load: dtoverlay=i2c4,<param>
+@@ -2869,6 +2953,10 @@ Load: dtoverlay=midi-uart0
+ Params: <None>
+
+
++Name: midi-uart0-pi5
++Info: See midi-uart0 (this is the Pi 5 version)
++
++
+ Name: midi-uart1
+ Info: Configures UART1 (ttyS0) so that a requested 38.4kbaud actually gets
+ 31.25kbaud, the frequency required for MIDI
+@@ -2876,29 +2964,45 @@ Load: dtoverlay=midi-uart1
+ Params: <None>
+
+
++Name: midi-uart1-pi5
++Info: See midi-uart1 (this is the Pi 5 version)
++
++
+ Name: midi-uart2
+-Info: Configures UART2 (ttyAMA1) so that a requested 38.4kbaud actually gets
++Info: Configures UART2 (ttyAMA2) so that a requested 38.4kbaud actually gets
+ 31.25kbaud, the frequency required for MIDI
+ Load: dtoverlay=midi-uart2
+ Params: <None>
+
+
++Name: midi-uart2-pi5
++Info: See midi-uart2 (this is the Pi 5 version)
++
++
+ Name: midi-uart3
+-Info: Configures UART3 (ttyAMA2) so that a requested 38.4kbaud actually gets
++Info: Configures UART3 (ttyAMA3) so that a requested 38.4kbaud actually gets
+ 31.25kbaud, the frequency required for MIDI
+ Load: dtoverlay=midi-uart3
+ Params: <None>
+
+
++Name: midi-uart3-pi5
++Info: See midi-uart3 (this is the Pi 5 version)
++
++
+ Name: midi-uart4
+-Info: Configures UART4 (ttyAMA3) so that a requested 38.4kbaud actually gets
++Info: Configures UART4 (ttyAMA4) so that a requested 38.4kbaud actually gets
+ 31.25kbaud, the frequency required for MIDI
+ Load: dtoverlay=midi-uart4
+ Params: <None>
+
+
++Name: midi-uart4-pi5
++Info: See midi-uart4 (this is the Pi 5 version)
++
++
+ Name: midi-uart5
+-Info: Configures UART5 (ttyAMA4) so that a requested 38.4kbaud actually gets
++Info: Configures UART5 (ttyAMA5) so that a requested 38.4kbaud actually gets
+ 31.25kbaud, the frequency required for MIDI
+ Load: dtoverlay=midi-uart5
+ Params: <None>
+@@ -3921,105 +4025,131 @@ Name: spi1-1cs
+ Info: Enables spi1 with a single chip select (CS) line and associated spidev
+ dev node. The gpio pin number for the CS line and spidev device node
+ creation are configurable.
+- N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+- A+, B+, Zero and PI2 B; as well as the Compute Module.
++ N.B.: spi1 is not accessible on old Pis without a 40-pin header.
+ Load: dtoverlay=spi1-1cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+- cs0_spidev Set to 'disabled' to stop the creation of a
++ cs0_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev1.0 (default
+- is 'okay' or enabled).
++ is 'on' or enabled).
+
+
+ Name: spi1-2cs
+ Info: Enables spi1 with two chip select (CS) lines and associated spidev
+ dev nodes. The gpio pin numbers for the CS lines and spidev device node
+ creation are configurable.
+- N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+- A+, B+, Zero and PI2 B; as well as the Compute Module.
++ N.B.: spi1 is not accessible on old Pis without a 40-pin header.
+ Load: dtoverlay=spi1-2cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+ cs1_pin GPIO pin for CS1 (default 17 - BCM SPI1_CE1).
+- cs0_spidev Set to 'disabled' to stop the creation of a
++ cs0_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev1.0 (default
+- is 'okay' or enabled).
+- cs1_spidev Set to 'disabled' to stop the creation of a
++ is 'on' or enabled).
++ cs1_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev1.1 (default
+- is 'okay' or enabled).
++ is 'on' or enabled).
+
+
+ Name: spi1-3cs
+ Info: Enables spi1 with three chip select (CS) lines and associated spidev
+ dev nodes. The gpio pin numbers for the CS lines and spidev device node
+ creation are configurable.
+- N.B.: spi1 is only accessible on devices with a 40pin header, eg:
+- A+, B+, Zero and PI2 B; as well as the Compute Module.
++ N.B.: spi1 is not accessible on old Pis without a 40-pin header.
+ Load: dtoverlay=spi1-3cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 18 - BCM SPI1_CE0).
+ cs1_pin GPIO pin for CS1 (default 17 - BCM SPI1_CE1).
+ cs2_pin GPIO pin for CS2 (default 16 - BCM SPI1_CE2).
+- cs0_spidev Set to 'disabled' to stop the creation of a
++ cs0_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev1.0 (default
+- is 'okay' or enabled).
+- cs1_spidev Set to 'disabled' to stop the creation of a
++ is 'on' or enabled).
++ cs1_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev1.1 (default
+- is 'okay' or enabled).
+- cs2_spidev Set to 'disabled' to stop the creation of a
++ is 'on' or enabled).
++ cs2_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev1.2 (default
+- is 'okay' or enabled).
++ is 'on' or enabled).
+
+
+ Name: spi2-1cs
+-Info: Enables spi2 with a single chip select (CS) line and associated spidev
+- dev node. The gpio pin number for the CS line and spidev device node
+- creation are configurable.
+- N.B.: spi2 is only accessible with the Compute Module.
++Info: Enables spi2 on GPIOs 40-42 with a single chip select (CS) line and
++ associated spidev dev node. The gpio pin number for the CS line and
++ spidev device node creation are configurable. spi2-2cs-pi5 is
++ substituted on a Pi 5.
++ N.B.: spi2 is only accessible with the Compute Module or Pi 5.
+ Load: dtoverlay=spi2-1cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+- cs0_spidev Set to 'disabled' to stop the creation of a
++ cs0_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev2.0 (default
+- is 'okay' or enabled).
++ is 'on' or enabled).
++
++
++Name: spi2-1cs-pi5
++Info: Enables spi2 on GPIOs 1-3 with a single chip select (CS) line and
++ associated spidev dev node. The gpio pin number for the CS line and
++ spidev device node creation are configurable. Pi 5 only.
++Load: dtoverlay=spi2-1cs-pi5,<param>=<val>
++Params: cs0_pin GPIO pin for CS0 (default 0).
++ cs0_spidev Set to 'off' to stop the creation of a
++ userspace device node /dev/spidev2.0 (default
++ is 'on' or enabled).
+
+
+ Name: spi2-2cs
+-Info: Enables spi2 with two chip select (CS) lines and associated spidev
+- dev nodes. The gpio pin numbers for the CS lines and spidev device node
+- creation are configurable.
+- N.B.: spi2 is only accessible with the Compute Module.
++Info: Enables spi2 on GPIOs 40-42 with two chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. spi2-2cs-pi5 is
++ substituted on a Pi 5.
++ N.B.: spi2 is only accessible with the Compute Module or Pi 5.
+ Load: dtoverlay=spi2-2cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+ cs1_pin GPIO pin for CS1 (default 44 - BCM SPI2_CE1).
+- cs0_spidev Set to 'disabled' to stop the creation of a
++ cs0_spidev Set to 'off' to stop the creation of a
++ userspace device node /dev/spidev2.0 (default
++ is 'on' or enabled).
++ cs1_spidev Set to 'off' to stop the creation of a
++ userspace device node /dev/spidev2.1 (default
++ is 'on' or enabled).
++
++
++Name: spi2-2cs-pi5
++Info: Enables spi2 on GPIOs 1-3 with two chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. Pi 5 only.
++Load: dtoverlay=spi2-2cs-pi5,<param>=<val>
++Params: cs0_pin GPIO pin for CS0 (default 0).
++ cs1_pin GPIO pin for CS1 (default 24).
++ cs0_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev2.0 (default
+- is 'okay' or enabled).
+- cs1_spidev Set to 'disabled' to stop the creation of a
++ is 'on' or enabled).
++ cs1_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev2.1 (default
+- is 'okay' or enabled).
++ is 'on' or enabled).
+
+
+ Name: spi2-3cs
+-Info: Enables spi2 with three chip select (CS) lines and associated spidev
+- dev nodes. The gpio pin numbers for the CS lines and spidev device node
+- creation are configurable.
+- N.B.: spi2 is only accessible with the Compute Module.
++Info: Enables spi2 on GPIOs 40-42 with three chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable.
++ N.B.: spi2 is only accessible with the Compute Module or Pi 5.
+ Load: dtoverlay=spi2-3cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 43 - BCM SPI2_CE0).
+ cs1_pin GPIO pin for CS1 (default 44 - BCM SPI2_CE1).
+ cs2_pin GPIO pin for CS2 (default 45 - BCM SPI2_CE2).
+- cs0_spidev Set to 'disabled' to stop the creation of a
++ cs0_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev2.0 (default
+- is 'okay' or enabled).
+- cs1_spidev Set to 'disabled' to stop the creation of a
++ is 'on' or enabled).
++ cs1_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev2.1 (default
+- is 'okay' or enabled).
+- cs2_spidev Set to 'disabled' to stop the creation of a
++ is 'on' or enabled).
++ cs2_spidev Set to 'off' to stop the creation of a
+ userspace device node /dev/spidev2.2 (default
+- is 'okay' or enabled).
++ is 'on' or enabled).
+
+
+ Name: spi3-1cs
+-Info: Enables spi3 with a single chip select (CS) line and associated spidev
+- dev node. The gpio pin number for the CS line and spidev device node
+- creation are configurable. BCM2711 only.
++Info: Enables spi3 on GPIOs 1-3 with a single chip select (CS) line and
++ associated spidev dev node. The gpio pin number for the CS line and
++ spidev device node creation are configurable. BCM2711 only,
++ spi3-1cs-pi5 is substituted on Pi 5.
+ Load: dtoverlay=spi3-1cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 0 - BCM SPI3_CE0).
+ cs0_spidev Set to 'off' to prevent the creation of a
+@@ -4027,10 +4157,22 @@ Params: cs0_pin GPIO pin
+ is 'on' or enabled).
+
+
++Name: spi3-1cs-pi5
++Info: Enables spi3 on GPIOs 5-7 with a single chip select (CS) line and
++ associated spidev dev node. The gpio pin number for the CS line and
++ spidev device node creation are configurable. Pi 5 only.
++Load: dtoverlay=spi3-1cs-pi5,<param>=<val>
++Params: cs0_pin GPIO pin for CS0 (default 4).
++ cs0_spidev Set to 'off' to prevent the creation of a
++ userspace device node /dev/spidev3.0 (default
++ is 'on' or enabled).
++
++
+ Name: spi3-2cs
+-Info: Enables spi3 with two chip select (CS) lines and associated spidev
+- dev nodes. The gpio pin numbers for the CS lines and spidev device node
+- creation are configurable. BCM2711 only.
++Info: Enables spi3 on GPIO2 1-3 with two chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. BCM2711 only,
++ spi3-2cs-pi5 is substituted on Pi 5.
+ Load: dtoverlay=spi3-2cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 0 - BCM SPI3_CE0).
+ cs1_pin GPIO pin for CS1 (default 24 - BCM SPI3_CE1).
+@@ -4042,10 +4184,25 @@ Params: cs0_pin GPIO pin
+ is 'on' or enabled).
+
+
++Name: spi3-2cs-pi5
++Info: Enables spi3 on GPIOs 5-7 with two chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. Pi 5 only.
++Load: dtoverlay=spi3-2cs-pi5,<param>=<val>
++Params: cs0_pin GPIO pin for CS0 (default 4).
++ cs1_pin GPIO pin for CS1 (default 25).
++ cs0_spidev Set to 'off' to prevent the creation of a
++ userspace device node /dev/spidev3.0 (default
++ is 'on' or enabled).
++ cs1_spidev Set to 'off' to prevent the creation of a
++ userspace device node /dev/spidev3.1 (default
++ is 'on' or enabled).
++
++
+ Name: spi4-1cs
+-Info: Enables spi4 with a single chip select (CS) line and associated spidev
+- dev node. The gpio pin number for the CS line and spidev device node
+- creation are configurable. BCM2711 only.
++Info: Enables spi4 on GPIOs 5-7 with a single chip select (CS) line and
++ associated spidev dev node. The gpio pin number for the CS line and
++ spidev device node creation are configurable. BCM2711 only.
+ Load: dtoverlay=spi4-1cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 4 - BCM SPI4_CE0).
+ cs0_spidev Set to 'off' to prevent the creation of a
+@@ -4054,9 +4211,9 @@ Params: cs0_pin GPIO pin
+
+
+ Name: spi4-2cs
+-Info: Enables spi4 with two chip select (CS) lines and associated spidev
+- dev nodes. The gpio pin numbers for the CS lines and spidev device node
+- creation are configurable. BCM2711 only.
++Info: Enables spi4 on GPIOs 5-6 with two chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. BCM2711 only.
+ Load: dtoverlay=spi4-2cs,<param>=<val>
+ Params: cs0_pin GPIO pin for CS0 (default 4 - BCM SPI4_CE0).
+ cs1_pin GPIO pin for CS1 (default 25 - BCM SPI4_CE1).
+@@ -4069,23 +4226,27 @@ Params: cs0_pin GPIO pin
+
+
+ Name: spi5-1cs
+-Info: Enables spi5 with a single chip select (CS) line and associated spidev
+- dev node. The gpio pin numbers for the CS lines and spidev device node
+- creation are configurable. BCM2711 only.
++Info: Enables spi5 on GPIOs 13-15 with a single chip select (CS) line and
++ associated spidev dev node. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. BCM2711 and Pi 5.
+ Load: dtoverlay=spi5-1cs,<param>=<val>
+-Params: cs0_pin GPIO pin for CS0 (default 12 - BCM SPI5_CE0).
++Params: cs0_pin GPIO pin for CS0 (default 12).
+ cs0_spidev Set to 'off' to prevent the creation of a
+ userspace device node /dev/spidev5.0 (default
+ is 'on' or enabled).
+
+
++Name: spi5-1cs-pi5
++Info: See spi5-1cs
++
++
+ Name: spi5-2cs
+-Info: Enables spi5 with two chip select (CS) lines and associated spidev
+- dev nodes. The gpio pin numbers for the CS lines and spidev device node
+- creation are configurable. BCM2711 only.
++Info: Enables spi5 on GPIOs 13-15 with two chip select (CS) lines and
++ associated spidev dev nodes. The gpio pin numbers for the CS lines and
++ spidev device node creation are configurable. BCM2711 and Pi 5.
+ Load: dtoverlay=spi5-2cs,<param>=<val>
+-Params: cs0_pin GPIO pin for CS0 (default 12 - BCM SPI5_CE0).
+- cs1_pin GPIO pin for CS1 (default 26 - BCM SPI5_CE1).
++Params: cs0_pin GPIO pin for CS0 (default 12).
++ cs1_pin GPIO pin for CS1 (default 26).
+ cs0_spidev Set to 'off' to prevent the creation of a
+ userspace device node /dev/spidev5.0 (default
+ is 'on' or enabled).
+@@ -4094,6 +4255,10 @@ Params: cs0_pin GPIO pin
+ is 'on' or enabled).
+
+
++Name: spi5-2cs-pi5
++Info: See spi5-2cs
++
++
+ Name: spi6-1cs
+ Info: Enables spi6 with a single chip select (CS) line and associated spidev
+ dev node. The gpio pin number for the CS line and spidev device node
+@@ -4296,6 +4461,12 @@ Params: txd0_pin GPIO pin
+ 7(Alt3) for 32&33, 6(Alt2) for 36&37
+
+
++Name: uart0-pi5
++Info: Enable uart 0 on GPIOs 14-15. Pi 5 only.
++Load: dtoverlay=uart0-pi5,<param>
++Params: ctsrts Enable CTS/RTS on GPIOs 16-17 (default off)
++
++
+ Name: uart1
+ Info: Change the pin usage of uart1
+ Load: dtoverlay=uart1,<param>=<val>
+@@ -4304,24 +4475,48 @@ Params: txd1_pin GPIO pin
+ rxd1_pin GPIO pin for RXD1 (15, 33 or 41 - default 15)
+
+
++Name: uart1-pi5
++Info: Enable uart 1 on GPIOs 0-1. Pi 5 only.
++Load: dtoverlay=uart1-pi5,<param>
++Params: ctsrts Enable CTS/RTS on GPIOs 2-3 (default off)
++
++
+ Name: uart2
+ Info: Enable uart 2 on GPIOs 0-3. BCM2711 only.
+ Load: dtoverlay=uart2,<param>
+ Params: ctsrts Enable CTS/RTS on GPIOs 2-3 (default off)
+
+
++Name: uart2-pi5
++Info: Enable uart 2 on GPIOs 4-5. Pi 5 only.
++Load: dtoverlay=uart2-pi5,<param>
++Params: ctsrts Enable CTS/RTS on GPIOs 6-7 (default off)
++
++
+ Name: uart3
+ Info: Enable uart 3 on GPIOs 4-7. BCM2711 only.
+ Load: dtoverlay=uart3,<param>
+ Params: ctsrts Enable CTS/RTS on GPIOs 6-7 (default off)
+
+
++Name: uart3-pi5
++Info: Enable uart 3 on GPIOs 8-9. Pi 5 only.
++Load: dtoverlay=uart3-pi5,<param>
++Params: ctsrts Enable CTS/RTS on GPIOs 10-11 (default off)
++
++
+ Name: uart4
+ Info: Enable uart 4 on GPIOs 8-11. BCM2711 only.
+ Load: dtoverlay=uart4,<param>
+ Params: ctsrts Enable CTS/RTS on GPIOs 10-11 (default off)
+
+
++Name: uart4-pi5
++Info: Enable uart 4 on GPIOs 12-13. Pi 5 only.
++Load: dtoverlay=uart4-pi5,<param>
++Params: ctsrts Enable CTS/RTS on GPIOs 14-15 (default off)
++
++
+ Name: uart5
+ Info: Enable uart 5 on GPIOs 12-15. BCM2711 only.
+ Load: dtoverlay=uart5,<param>
+@@ -4530,6 +4725,8 @@ Params: sizex Touchscr
+ invy Touchscreen inverted y axis
+ swapxy Touchscreen swapped x y axis
+ disable_touch Disables the touch screen overlay driver
++ dsi0 Use DSI0 and i2c_csi_dsi0 (rather than
++ the default DSI1 and i2c_csi_dsi).
+
+
+ Name: vc4-kms-dsi-lt070me05000
+@@ -4579,6 +4776,8 @@ Params: 2_8_inch 2.8" 480
+ invx Touchscreen inverted x axis
+ invy Touchscreen inverted y axis
+ swapxy Touchscreen swapped x y axis
++ dsi0 Use DSI0 and i2c_csi_dsi0 (rather than
++ the default DSI1 and i2c_csi_dsi).
+
+
+ Name: vc4-kms-kippah-7inch
+@@ -4633,6 +4832,9 @@ Params: cma-512 CMA is 5
+ nohdmi1 Disable HDMI 1 output
+
+
++Name: vc4-kms-v3d-pi5
++Info: See vc4-kms-v3d-pi4 (this is the Pi 5 version)
++
+
+ Name: vc4-kms-vga666
+ Info: Enable the VGA666 (resistor ladder ADC) for the vc4-kms-v3d driver.
+--- a/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/adau1977-adc-overlay.dts
+@@ -23,7 +23,7 @@
+ };
+
+ fragment@1 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -33,7 +33,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "adi,adau1977-adc";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts
++++ b/arch/arm/boot/dts/overlays/adau7002-simple-overlay.dts
+@@ -5,7 +5,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -37,7 +37,7 @@
+ "PDM_DAT", "Microphone Jack";
+ status = "okay";
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+ dailink0_slave: simple-audio-card,codec {
+ sound-dai = <&adau7002_codec>;
+--- a/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/akkordion-iqdacplus-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -38,7 +38,7 @@
+ card_name = "Akkordion";
+ dai_name = "IQaudIO DAC";
+ dai_stream_name = "IQaudIO DAC HiFi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-boss-dac-pcm512x-audio-overlay.dts
+@@ -18,8 +18,8 @@
+ };
+ };
+
+- fragment@1 {
+- target = <&i2s>;
++ frag1: fragment@1 {
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -46,7 +46,7 @@
+ target = <&sound>;
+ boss_dac: __overlay__ {
+ compatible = "allo,boss-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ mute-gpios = <&gpio 6 1>;
+ status = "okay";
+ };
+@@ -54,6 +54,8 @@
+
+ __overrides__ {
+ 24db_digital_gain = <&boss_dac>,"allo,24db_digital_gain?";
+- slave = <&boss_dac>,"allo,slave?";
++ slave = <&boss_dac>,"allo,slave?",
++ <&frag1>,"target:0=",<&i2s_clk_producer>,
++ <&boss_dac>,"i2s-controller:0=",<&i2s_clk_producer>;
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-boss2-dac-audio-overlay.dts
+@@ -8,7 +8,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ #sound-dai-cells = <0>;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/allo-digione-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-digione-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -35,7 +35,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "allo,allo-digione";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ clock44-gpio = <&gpio 5 0>;
+ clock48-gpio = <&gpio 6 0>;
+--- a/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-katana-dac-audio-overlay.dts
+@@ -9,7 +9,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ #sound-dai-cells = <0>;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-piano-dac-pcm512x-audio-overlay.dts
+@@ -16,7 +16,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -42,7 +42,7 @@
+ target = <&sound>;
+ piano_dac: __overlay__ {
+ compatible = "allo,piano-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/allo-piano-dac-plus-pcm512x-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -41,7 +41,7 @@
+ piano_dac: __overlay__ {
+ compatible = "allo,piano-dac-plus";
+ audio-codec = <&allo_pcm5122_4c &allo_pcm5122_4d>;
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ mute1-gpios = <&gpio 6 1>;
+ mute2-gpios = <&gpio 25 1>;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/applepi-dac-overlay.dts
+@@ -16,7 +16,7 @@
+ format = "i2s";
+
+ p_cpu_dai: cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+@@ -40,7 +40,7 @@
+ };
+
+ fragment@2 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ #sound-dai-cells = <0>;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/arducam-64mp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/arducam-64mp-overlay.dts
+@@ -67,7 +67,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/arducam-pivariety-overlay.dts
++++ b/arch/arm/boot/dts/overlays/arducam-pivariety-overlay.dts
+@@ -85,7 +85,7 @@
+ rotation = <&arducam_pivariety>,"rotation:0";
+ orientation = <&arducam_pivariety>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&arducam_pivariety>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-addons-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -48,7 +48,7 @@
+ mult-gpios = <&gpio 27 0>, <&gpio 22 0>, <&gpio 23 0>,
+ <&gpio 24 0>;
+ reset-gpios = <&gpio 5 0>;
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ codec = <&cs42448>;
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/audioinjector-bare-i2s-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-bare-i2s-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -27,7 +27,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "simple-audio-card";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+
+ simple-audio-card,name = "audioinjector-bare";
+@@ -37,7 +37,7 @@
+ simple-audio-card,frame-master = <&dailink0_master>;
+
+ dailink0_master: simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+--- a/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-isolated-soundcard-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -47,7 +47,7 @@
+ snd: __overlay__ {
+ compatible = "ai,audioinjector-isolated-soundcard";
+ mute-gpios = <&gpio 17 0>;
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ codec = <&cs4272>;
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-ultra-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -33,7 +33,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "simple-audio-card";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+
+ simple-audio-card,name = "audioinjector-ultra";
+@@ -57,7 +57,7 @@
+ simple-audio-card,frame-master = <&sound_master>;
+
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_consumer>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+--- a/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audioinjector-wm8731-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "ai,audioinjector-pi-soundcard";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/audiosense-pi-overlay.dts
+@@ -8,7 +8,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -75,7 +75,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "as,audiosense-pi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/chipdip-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/chipdip-dac-overlay.dts
+@@ -9,7 +9,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "chipdip,chipdip-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ sr0-gpios = <&gpio 5 0>;
+ sr1-gpios = <&gpio 6 0>;
+ sr2-gpios = <&gpio 12 0>;
+--- a/arch/arm/boot/dts/overlays/cirrus-wm5102-overlay.dts
++++ b/arch/arm/boot/dts/overlays/cirrus-wm5102-overlay.dts
+@@ -9,7 +9,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -165,7 +165,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "wlf,rpi-cirrus";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/dacberry400-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dacberry400-overlay.dts
+@@ -5,7 +5,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -62,7 +62,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "osaelectronics,dacberry400";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/dionaudio-kiwi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dionaudio-kiwi-overlay.dts
+@@ -11,7 +11,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "dionaudio,dionaudio-kiwi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dionaudio-loco-overlay.dts
+@@ -11,7 +11,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "dionaudio,loco-pcm5242-tpa3118";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts
++++ b/arch/arm/boot/dts/overlays/dionaudio-loco-v2-overlay.dts
+@@ -15,13 +15,13 @@
+ target = <&sound>;
+ frag0: __overlay__ {
+ compatible = "dionaudio,dionaudio-loco-v2";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+
+ fragment@1 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/disable-bt-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/* Disable Bluetooth */
++
++#include <dt-bindings/gpio/gpio.h>
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&bluetooth>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/disable-wifi-pi5-overlay.dts
+@@ -0,0 +1,13 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&sdio2>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++};
+--- a/arch/arm/boot/dts/overlays/draws-overlay.dts
++++ b/arch/arm/boot/dts/overlays/draws-overlay.dts
+@@ -9,7 +9,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -131,7 +131,7 @@
+ target = <&sound>;
+ snd: __overlay__ {
+ compatible = "simple-audio-card";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+
+ simple-audio-card,name = "draws";
+@@ -153,7 +153,7 @@
+ "Line Out", "LOL";
+
+ dailink0_master: simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+
+ simple-audio-card,codec {
+--- a/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
++++ b/arch/arm/boot/dts/overlays/edt-ft5406-overlay.dts
+@@ -25,21 +25,21 @@
+ };
+
+ __overrides__ {
+- i2c0 = <&frag13>,"target:0=",<&i2c0>;
+- i2c1 = <&frag13>, "target?=0",
+- <&frag13>, "target-path=i2c1",
++ i2c0 = <&ts_i2c_frag>,"target:0=",<&i2c0>;
++ i2c1 = <&ts_i2c_frag>, "target?=0",
++ <&ts_i2c_frag>, "target-path=i2c1",
+ <0>,"-0-1";
+- i2c3 = <&frag13>, "target?=0",
+- <&frag13>, "target-path=i2c3",
++ i2c3 = <&ts_i2c_frag>, "target?=0",
++ <&ts_i2c_frag>, "target-path=i2c3",
+ <0>,"-0-1";
+- i2c4 = <&frag13>, "target?=0",
+- <&frag13>, "target-path=i2c4",
++ i2c4 = <&ts_i2c_frag>, "target?=0",
++ <&ts_i2c_frag>, "target-path=i2c4",
+ <0>,"-0-1";
+- i2c5 = <&frag13>, "target?=0",
+- <&frag13>, "target-path=i2c5",
++ i2c5 = <&ts_i2c_frag>, "target?=0",
++ <&ts_i2c_frag>, "target-path=i2c5",
+ <0>,"-0-1";
+- i2c6 = <&frag13>, "target?=0",
+- <&frag13>, "target-path=i2c6",
++ i2c6 = <&ts_i2c_frag>, "target?=0",
++ <&ts_i2c_frag>, "target-path=i2c6",
+ <0>,"-0-1";
+ addr = <&ft5406>,"reg:0";
+ };
+--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
+@@ -37,7 +37,7 @@
+ };
+ };
+
+- frag13: fragment@13 {
++ ts_i2c_frag: fragment@13 {
+ target = <&i2c_csi_dsi>;
+ i2cbus: __overlay__ {
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/fe-pi-audio-overlay.dts
+@@ -53,7 +53,7 @@
+ };
+
+ fragment@3 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -63,7 +63,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "fe-pi,fe-pi-audio";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ghost-amp-overlay.dts
+@@ -14,7 +14,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -43,7 +43,7 @@
+ target = <&sound>;
+ iqaudio_dac: __overlay__ {
+ compatible = "iqaudio,iqaudio-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ mute-gpios = <& 0 0>;
+ iqaudio-dac,auto-mute-amp;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/googlevoicehat-soundcard-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -42,7 +42,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "googlevoicehat,googlevoicehat-soundcard";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-amp-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberry,hifiberry-amp";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-amp100-overlay.dts
+@@ -15,8 +15,8 @@
+ };
+ };
+
+- fragment@1 {
+- target = <&i2s>;
++ frag1: fragment@1 {
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -46,7 +46,7 @@
+ target = <&sound>;
+ hifiberry_dacplus: __overlay__ {
+ compatible = "hifiberry,hifiberry-dacplus";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ mute-gpio = <&gpio 4 0>;
+ reset-gpio = <&gpio 17 0x11>;
+@@ -56,7 +56,10 @@
+ __overrides__ {
+ 24db_digital_gain =
+ <&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
+- slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
++ slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?",
++ <&frag1>,"target:0=",<&i2s_clk_producer>,
++ <&hifiberry_dacplus>,"i2s-controller:0=",<&i2s_clk_producer>;
++
+ leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
+ mute_ext_ctl = <&hifiberry_dacplus>,"hifiberry-dacplus,mute_ext_ctl:0";
+ auto_mute = <&hifiberry_dacplus>,"hifiberry-dacplus,auto_mute?";
+--- a/arch/arm/boot/dts/overlays/hifiberry-amp3-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-amp3-overlay.dts
+@@ -10,7 +10,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -50,7 +50,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberry,hifiberry-amp3";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -27,7 +27,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberry,hifiberry-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplus-overlay.dts
+@@ -15,8 +15,8 @@
+ };
+ };
+
+- fragment@1 {
+- target = <&i2s>;
++ frag1: fragment@1 {
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -51,7 +51,7 @@
+ target = <&sound>;
+ hifiberry_dacplus: __overlay__ {
+ compatible = "hifiberry,hifiberry-dacplus";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+@@ -59,7 +59,10 @@
+ __overrides__ {
+ 24db_digital_gain =
+ <&hifiberry_dacplus>,"hifiberry,24db_digital_gain?";
+- slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?";
++ slave = <&hifiberry_dacplus>,"hifiberry-dacplus,slave?",
++ <&frag1>,"target:0=",<&i2s_clk_producer>,
++ <&hifiberry_dacplus>,"i2s-controller:0=",<&i2s_clk_producer>;
++
+ leds_off = <&hifiberry_dacplus>,"hifiberry-dacplus,leds_off?";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadc-overlay.dts
+@@ -15,8 +15,8 @@
+ };
+ };
+
+- fragment@1 {
+- target = <&i2s>;
++ frag1: fragment@1 {
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -58,7 +58,7 @@
+ target = <&sound>;
+ hifiberry_dacplusadc: __overlay__ {
+ compatible = "hifiberry,hifiberry-dacplusadc";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+@@ -66,7 +66,9 @@
+ __overrides__ {
+ 24db_digital_gain =
+ <&hifiberry_dacplusadc>,"hifiberry,24db_digital_gain?";
+- slave = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,slave?";
++ slave = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,slave?",
++ <&frag1>,"target:0=",<&i2s_clk_producer>,
++ <&hifiberry_dacplusadc>,"i2s-controller:0=",<&i2s_clk_producer>;
+ leds_off = <&hifiberry_dacplusadc>,"hifiberry-dacplusadc,leds_off?";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusadcpro-overlay.dts
+@@ -15,8 +15,8 @@
+ };
+ };
+
+- fragment@1 {
+- target = <&i2s>;
++ frag1: fragment@1 {
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -56,7 +56,7 @@
+ hifiberry_dacplusadcpro: __overlay__ {
+ compatible = "hifiberry,hifiberry-dacplusadcpro";
+ audio-codec = <&hb_dac &hb_adc>;
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+@@ -64,7 +64,9 @@
+ __overrides__ {
+ 24db_digital_gain =
+ <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,24db_digital_gain?";
+- slave = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,slave?";
++ slave = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,slave?",
++ <&frag1>,"target:0=",<&i2s_clk_producer>,
++ <&hifiberry_dacplusadcpro>,"i2s-controller:0=",<&i2s_clk_producer>;
+ leds_off = <&hifiberry_dacplusadcpro>,"hifiberry-dacplusadcpro,leds_off?";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplusdsp-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -27,7 +27,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberrydacplusdsp,hifiberrydacplusdsp-soundcard";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-dacplushd-overlay.dts
+@@ -8,7 +8,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -84,7 +84,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberry,hifiberry-dacplushd";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ clocks = <&pll 0>;
+ reset-gpio = <&gpio 16 GPIO_ACTIVE_LOW>;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-digi-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -34,7 +34,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberry,hifiberry-digi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts
++++ b/arch/arm/boot/dts/overlays/hifiberry-digi-pro-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -34,7 +34,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "hifiberry,hifiberry-digi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ clock44-gpio = <&gpio 5 0>;
+ clock48-gpio = <&gpio 6 0>;
+--- a/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i-sabre-q2m-overlay.dts
+@@ -9,13 +9,13 @@
+ target = <&sound>;
+ frag0: __overlay__ {
+ compatible = "audiophonics,i-sabre-q2m";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+
+ fragment@1 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c0-pi5-overlay.dts
+@@ -0,0 +1,34 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&i2c0>;
++ frag0: __overlay__ {
++ status = "okay";
++ clock-frequency = <100000>;
++ };
++ };
++
++ fragment@1 {
++ target = <&frag0>;
++ __overlay__ {
++ pinctrl-0 = <&rp1_i2c0_0_1>;
++ };
++ };
++
++ fragment@2 {
++ target = <&frag0>;
++ __dormant__ {
++ pinctrl-0 = <&rp1_i2c0_8_9>;
++ };
++ };
++
++ __overrides__ {
++ pins_0_1 = <0>,"+1-2";
++ pins_8_9 = <0>,"-1+2";
++ baudrate = <&frag0>, "clock-frequency:0";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c1-pi5-overlay.dts
+@@ -0,0 +1,34 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&i2c1>;
++ frag0: __overlay__ {
++ status = "okay";
++ clock-frequency = <100000>;
++ };
++ };
++
++ fragment@1 {
++ target = <&frag0>;
++ __overlay__ {
++ pinctrl-0 = <&rp1_i2c1_2_3>;
++ };
++ };
++
++ fragment@2 {
++ target = <&frag0>;
++ __dormant__ {
++ pinctrl-0 = <&rp1_i2c1_10_11>;
++ };
++ };
++
++ __overrides__ {
++ pins_2_3 = <0>,"+1-2";
++ pins_10_11 = <0>,"-1+2";
++ baudrate = <&frag0>, "clock-frequency:0";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c2-pi5-overlay.dts
+@@ -0,0 +1,21 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&i2c2>;
++ frag0: __overlay__ {
++ status = "okay";
++ clock-frequency = <100000>;
++ pinctrl-0 = <&rp1_i2c2_4_5>;
++ };
++ };
++
++ __overrides__ {
++ pins_4_5 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c2_4_5>;
++ pins_12_13 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c2_12_13>;
++ baudrate = <&frag0>, "clock-frequency:0";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/i2c3-pi5-overlay.dts
+@@ -0,0 +1,22 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&i2c3>;
++ frag0: __overlay__ {
++ status = "okay";
++ clock-frequency = <100000>;
++ pinctrl-0 = <&rp1_i2c3_6_7>;
++ };
++ };
++
++ __overrides__ {
++ pins_6_7 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c3_6_7>;
++ pins_14_15 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c3_14_15>;
++ pins_22_23 = <&frag0>,"pinctrl-0:0=", <&rp1_i2c3_22_23>;
++ baudrate = <&frag0>, "clock-frequency:0";
++ };
++};
+--- a/arch/arm/boot/dts/overlays/i2s-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/i2s-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -27,7 +27,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "rpi,rpi-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/imx219-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx219-overlay.dts
+@@ -69,7 +69,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx258-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx258-overlay.dts
+@@ -110,7 +110,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+--- a/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx290_327-overlay.dtsi
+@@ -95,7 +95,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx296-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx296-overlay.dts
+@@ -94,7 +94,7 @@
+ rotation = <&imx296>,"rotation:0";
+ orientation = <&imx296>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&imx296>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
+@@ -65,7 +65,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+--- a/arch/arm/boot/dts/overlays/imx519-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx519-overlay.dts
+@@ -69,7 +69,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/imx708-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx708-overlay.dts
+@@ -79,12 +79,12 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+- <&cam_node>, "VANA1-supply:0=",<&cam0_reg>,
++ <&cam_node>, "vana1-supply:0=",<&cam0_reg>,
+ <&vcm_node>, "VDD-supply:0=",<&cam0_reg>;
+ vcm = <&vcm_node>, "status",
+ <0>, "=4";
+--- a/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-codec-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ iqaudio_dac: __overlay__ {
+ compatible = "iqaudio,iqaudio-codec";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -35,7 +35,7 @@
+ target = <&sound>;
+ frag2: __overlay__ {
+ compatible = "iqaudio,iqaudio-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-dacplus-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -35,7 +35,7 @@
+ target = <&sound>;
+ iqaudio_dac: __overlay__ {
+ compatible = "iqaudio,iqaudio-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ mute-gpios = <&gpio 22 0>;
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/iqaudio-digi-wm8804-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -34,7 +34,7 @@
+ target = <&sound>;
+ wm8804_digi: __overlay__ {
+ compatible = "iqaudio,wm8804-digi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/irs1125-overlay.dts
++++ b/arch/arm/boot/dts/overlays/irs1125-overlay.dts
+@@ -82,7 +82,7 @@
+
+ __overrides__ {
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&irs1125>, "clocks:0=",<&cam0_clk>;
+--- a/arch/arm/boot/dts/overlays/justboom-both-overlay.dts
++++ b/arch/arm/boot/dts/overlays/justboom-both-overlay.dts
+@@ -7,7 +7,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -54,7 +54,7 @@
+ target = <&sound>;
+ frag3: __overlay__ {
+ compatible = "justboom,justboom-both";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/justboom-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -35,7 +35,7 @@
+ target = <&sound>;
+ frag2: __overlay__ {
+ compatible = "justboom,justboom-dac";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts
++++ b/arch/arm/boot/dts/overlays/justboom-digi-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -34,7 +34,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "justboom,justboom-digi";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/max98357a-overlay.dts
++++ b/arch/arm/boot/dts/overlays/max98357a-overlay.dts
+@@ -12,7 +12,7 @@
+
+ /* Enable I2S */
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -52,7 +52,7 @@
+ simple-audio-card,name = "MAX98357A";
+ status = "okay";
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+ simple-audio-card,codec {
+ sound-dai = <&max98357a_dac>;
+@@ -69,7 +69,7 @@
+ simple-audio-card,name = "MAX98357A";
+ status = "okay";
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+ simple-audio-card,codec {
+ sound-dai = <&max98357a_nsd>;
+--- a/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mbed-dac-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "simple-audio-card";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+
+ simple-audio-card,name = "mbed-DAC";
+@@ -52,7 +52,7 @@
+ simple-audio-card,format = "i2s";
+
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+
+ sound_master: simple-audio-card,codec {
+--- a/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
++++ b/arch/arm/boot/dts/overlays/merus-amp-overlay.dts
+@@ -9,7 +9,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -52,7 +52,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "merus,merus-amp";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+ };
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart0-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ * 100000000*38400/31250 = 122880000
++ */
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target-path = "/";
++ __overlay__ {
++ midi_clk: midi_clk0 {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "uart0_pclk";
++ clock-frequency = <122880000>;
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&uart0>;
++ __overlay__ {
++ clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart1-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ * 100000000*38400/31250 = 122880000
++ */
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target-path = "/";
++ __overlay__ {
++ midi_clk: midi_clk1 {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "uart1_pclk";
++ clock-frequency = <122880000>;
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&uart1>;
++ __overlay__ {
++ clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart2-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ * 100000000*38400/31250 = 122880000
++ */
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target-path = "/";
++ __overlay__ {
++ midi_clk: midi_clk2 {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "uart2_pclk";
++ clock-frequency = <122880000>;
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&uart2>;
++ __overlay__ {
++ clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart3-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ * 100000000*38400/31250 = 122880000
++ */
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target-path = "/";
++ __overlay__ {
++ midi_clk: midi_clk3 {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "uart3_pclk";
++ clock-frequency = <122880000>;
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&uart3>;
++ __overlay__ {
++ clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++ };
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/midi-uart4-pi5-overlay.dts
+@@ -0,0 +1,35 @@
++/dts-v1/;
++/plugin/;
++
++#include <dt-bindings/clock/rp1.h>
++
++/*
++ * Fake a higher clock rate to get a larger divisor, and thereby a lower
++ * baudrate. The real clock is 100MHz, which we scale so that requesting
++ * 38.4kHz results in an actual 31.25kHz.
++ *
++ * 100000000*38400/31250 = 122880000
++ */
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target-path = "/";
++ __overlay__ {
++ midi_clk: midi_clk4 {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "uart4_pclk";
++ clock-frequency = <122880000>;
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&uart4>;
++ __overlay__ {
++ clocks = <&midi_clk &rp1_clocks RP1_PLL_SYS_PRI_PH>;
++ };
++ };
++};
+--- a/arch/arm/boot/dts/overlays/ov2311-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov2311-overlay.dts
+@@ -60,7 +60,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -72,7 +72,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <®_frag>, "target:0=",<&cam0_reg>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/ov7251-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov7251-overlay.dts
+@@ -60,7 +60,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/ov9281-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov9281-overlay.dts
+@@ -61,7 +61,7 @@
+ rotation = <&cam_node>,"rotation:0";
+ orientation = <&cam_node>,"orientation:0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -1,32 +1,100 @@
+ /dts-v1/;
+
+ / {
++ audremap {
++ bcm2835;
++ bcm2711;
++ };
++
++ balena-fin {
++ bcm2835;
++ bcm2711;
++ };
++
+ bmp085_i2c-sensor {
+ deprecated = "use i2c-sensor,bmp085";
+ };
+
++ cm-swap-i2c0 {
++ bcm2835;
++ bcm2711;
++ };
++
+ cutiepi-panel {
+ bcm2711;
+ };
+
++ disable-bt {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "disable-bt-pi5";
++ };
++
++ disable-bt-pi5 {
++ bcm2712;
++ };
++
+ disable-emmc2 {
+ bcm2711;
+ };
+
++ disable-wifi {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "disable-wifi-pi5";
++ };
++
++ disable-wifi-pi5 {
++ bcm2712;
++ };
++
+ highperi {
+ bcm2711;
+ };
+
++ i2c0 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "i2c0-pi5";
++ };
++
+ i2c0-bcm2708 {
+ deprecated = "use i2c0";
+ };
+
++ i2c0-pi5 {
++ bcm2712;
++ };
++
++ i2c1 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "i2c1-pi5";
++ };
++
+ i2c1-bcm2708 {
+ deprecated = "use i2c1";
+ };
+
++ i2c1-pi5 {
++ bcm2712;
++ };
++
++ i2c2 {
++ bcm2712 = "i2c2-pi5";
++ };
++
++ i2c2-pi5 {
++ bcm2712;
++ };
++
+ i2c3 {
+ bcm2711;
++ bcm2712 = "i2c3-pi5";
++ };
++
++ i2c3-pi5 {
++ bcm2712;
+ };
+
+ i2c4 {
+@@ -41,26 +109,76 @@
+ bcm2711;
+ };
+
++ i2s-gpio28-31 {
++ bcm2835;
++ bcm2711;
++ };
++
+ lirc-rpi {
+ deprecated = "use gpio-ir";
+ };
+
++ midi-uart0 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "midi-uart0-pi5";
++ };
++
++ midi-uart0-pi5 {
++ bcm2712;
++ };
++
++ midi-uart1 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "midi-uart1-pi5";
++ };
++
++ midi-uart1-pi5 {
++ bcm2712;
++ };
++
+ midi-uart2 {
+ bcm2711;
++ bcm2712 = "midi-uart2-pi5";
++ };
++
++ midi-uart2-pi5 {
++ bcm2712;
+ };
+
+ midi-uart3 {
+ bcm2711;
++ bcm2712 = "midi-uart3-pi5";
++ };
++
++ midi-uart3-pi5 {
++ bcm2712;
+ };
+
+ midi-uart4 {
+ bcm2711;
++ bcm2712 = "midi-uart4-pi5";
++ };
++
++ midi-uart4-pi5 {
++ bcm2712;
+ };
+
+ midi-uart5 {
+ bcm2711;
+ };
+
++ miniuart-bt {
++ bcm2835;
++ bcm2711;
++ };
++
++ mmc {
++ bcm2835;
++ bcm2711;
++ };
++
+ mpu6050 {
+ deprecated = "use i2c-sensor,mpu6050";
+ };
+@@ -118,6 +236,16 @@
+ deprecated = "no longer necessary";
+ };
+
++ sdhost {
++ bcm2835;
++ bcm2711;
++ };
++
++ sdio {
++ bcm2835;
++ bcm2711;
++ };
++
+ sdio-1bit {
+ deprecated = "use sdio,bus_width=1,gpios_22_25";
+ };
+@@ -126,6 +254,21 @@
+ deprecated = "use 'dtparam=sd_poll_once' etc.";
+ };
+
++ smi {
++ bcm2835;
++ bcm2711;
++ };
++
++ smi-dev {
++ bcm2835;
++ bcm2711;
++ };
++
++ smi-nand {
++ bcm2835;
++ bcm2711;
++ };
++
+ spi0-cs {
+ renamed = "spi0-2cs";
+ };
+@@ -134,12 +277,42 @@
+ deprecated = "no longer necessary";
+ };
+
++ spi2-1cs {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "spi2-1cs-pi5";
++ };
++
++ spi2-1cs-pi5 {
++ bcm2712;
++ };
++
++ spi2-2cs {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "spi2-2cs-pi5";
++ };
++
++ spi2-2cs-pi5 {
++ bcm2712;
++ };
++
+ spi3-1cs {
+ bcm2711;
++ bcm2712 = "spi3-1cs-pi5";
++ };
++
++ spi3-1cs-pi5 {
++ bcm2712;
+ };
+
+ spi3-2cs {
+ bcm2711;
++ bcm2712 = "spi3-2cs-pi5";
++ };
++
++ spi3-2cs-pi5 {
++ bcm2712;
+ };
+
+ spi4-1cs {
+@@ -152,10 +325,20 @@
+
+ spi5-1cs {
+ bcm2711;
++ bcm2712 = "spi5-1cs-pi5";
++ };
++
++ spi5-1cs-pi5 {
++ bcm2712;
+ };
+
+ spi5-2cs {
+ bcm2711;
++ bcm2712 = "spi5-2cs-pi5";
++ };
++
++ spi5-2cs-pi5 {
++ bcm2712;
+ };
+
+ spi6-1cs {
+@@ -166,16 +349,51 @@
+ bcm2711;
+ };
+
++ uart0 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "uart0-pi5";
++ };
++
++ uart0-pi5 {
++ bcm2712;
++ };
++
++ uart1 {
++ bcm2835;
++ bcm2711;
++ bcm2712 = "uart1-pi5";
++ };
++
++ uart1-pi5 {
++ bcm2712;
++ };
++
+ uart2 {
+ bcm2711;
++ bcm2712 = "uart2-pi5";
++ };
++
++ uart2-pi5 {
++ bcm2712;
+ };
+
+ uart3 {
+ bcm2711;
++ bcm2712 = "uart3-pi5";
++ };
++
++ uart3-pi5 {
++ bcm2712;
+ };
+
+ uart4 {
+ bcm2711;
++ bcm2712 = "uart4-pi5";
++ };
++
++ uart4-pi5 {
++ bcm2712;
+ };
+
+ uart5 {
+@@ -198,10 +416,12 @@
+ vc4-fkms-v3d {
+ bcm2835;
+ bcm2711 = "vc4-fkms-v3d-pi4";
++ bcm2712 = "vc4-fkms-v3d-pi4";
+ };
+
+ vc4-fkms-v3d-pi4 {
+ bcm2711;
++ bcm2712;
+ };
+
+ vc4-kms-dpi-at056tn53v1 {
+@@ -211,10 +431,16 @@
+ vc4-kms-v3d {
+ bcm2835;
+ bcm2711 = "vc4-kms-v3d-pi4";
++ bcm2712 = "vc4-kms-v3d-pi5";
+ };
+
+ vc4-kms-v3d-pi4 {
+ bcm2711;
++ bcm2712 = "vc4-kms-v3d-pi5";
++ };
++
++ vc4-kms-v3d-pi5 {
++ bcm2712;
+ };
+
+ vl805 {
+--- a/arch/arm/boot/dts/overlays/pibell-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pibell-overlay.dts
+@@ -24,7 +24,7 @@
+ };
+
+ fragment@1 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ #sound-dai-cells = <0>;
+ status = "okay";
+@@ -43,7 +43,7 @@
+ format = "i2s";
+
+ r_cpu_dai: cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+
+ /* example TDM slot configuration
+ dai-tdm-slot-num = <2>;
+@@ -60,7 +60,7 @@
+ format = "i2s";
+
+ p_cpu_dai: cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+
+ /* example TDM slot configuration
+ dai-tdm-slot-num = <2>;
+--- a/arch/arm/boot/dts/overlays/pifi-40-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-40-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -42,7 +42,7 @@
+ pifi_40: __overlay__ {
+ compatible = "pifi,pifi-40";
+ audio-codec = <&tas5711l &tas5711r>;
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ pdn-gpios = <&gpio 23 1>;
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-dac-hd-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -38,7 +38,7 @@
+ simple-audio-card,dai-link@1 {
+ format = "i2s";
+ cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+ codec {
+ sound-dai = <&pcm5142>;
+--- a/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-dac-zero-overlay.dts
+@@ -16,7 +16,7 @@
+ format = "i2s";
+
+ cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+@@ -40,7 +40,7 @@
+ };
+
+ fragment@2 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ #sound-dai-cells = <0>;
+ status = "okay";
+--- a/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pifi-mini-210-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -34,7 +34,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "pifi,pifi-mini-210";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/pisound-overlay.dts
++++ b/arch/arm/boot/dts/overlays/pisound-overlay.dts
+@@ -75,7 +75,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "blokaslabs,pisound";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+
+ pinctrl-names = "default";
+@@ -108,7 +108,7 @@
+ };
+
+ fragment@7 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/proto-codec-overlay.dts
++++ b/arch/arm/boot/dts/overlays/proto-codec-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -32,7 +32,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "rpi,rpi-proto";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/rra-digidac1-wm8741-audio-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -42,7 +42,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "rra,digidac1-soundcard";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+ };
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi2-1cs-pi5-overlay.dts
+@@ -0,0 +1,33 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&spi2>;
++ frag1: __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cs-gpios = <&gpio 0 1>;
++ status = "okay";
++
++ spidev2_0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag1>,"cs-gpios:4";
++ cs0_spidev = <&spidev2_0>,"status";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi2-2cs-pi5-overlay.dts
+@@ -0,0 +1,44 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&spi2>;
++ frag1: __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cs-gpios = <&gpio 0 1>, <&gpio 24 1>;
++ status = "okay";
++
++ spidev2_0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++
++ spidev2_1: spidev@1 {
++ compatible = "spidev";
++ reg = <1>; /* CE1 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag1>,"cs-gpios:4";
++ cs1_pin = <&frag1>,"cs-gpios:16";
++ cs0_spidev = <&spidev2_0>,"status";
++ cs1_spidev = <&spidev2_1>,"status";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi3-1cs-pi5-overlay.dts
+@@ -0,0 +1,33 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&spi3>;
++ frag1: __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cs-gpios = <&gpio 4 1>;
++ status = "okay";
++
++ spidev3_0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag1>,"cs-gpios:4";
++ cs0_spidev = <&spidev3_0>,"status";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi3-2cs-pi5-overlay.dts
+@@ -0,0 +1,44 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&spi3>;
++ frag1: __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cs-gpios = <&gpio 4 1>, <&gpio 25 1>;
++ status = "okay";
++
++ spidev3_0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++
++ spidev3_1: spidev@1 {
++ compatible = "spidev";
++ reg = <1>; /* CE1 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag1>,"cs-gpios:4";
++ cs1_pin = <&frag1>,"cs-gpios:16";
++ cs0_spidev = <&spidev3_0>,"status";
++ cs1_spidev = <&spidev3_1>,"status";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi5-1cs-pi5-overlay.dts
+@@ -0,0 +1,33 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&spi5>;
++ frag1: __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cs-gpios = <&gpio 12 1>;
++ status = "okay";
++
++ spidev5_0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag1>,"cs-gpios:4";
++ cs0_spidev = <&spidev5_0>,"status";
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/spi5-2cs-pi5-overlay.dts
+@@ -0,0 +1,44 @@
++/dts-v1/;
++/plugin/;
++
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&spi5>;
++ frag1: __overlay__ {
++ /* needed to avoid dtc warning */
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ cs-gpios = <&gpio 12 1>, <&gpio 26 1>;
++ status = "okay";
++
++ spidev5_0: spidev@0 {
++ compatible = "spidev";
++ reg = <0>; /* CE0 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++
++ spidev5_1: spidev@1 {
++ compatible = "spidev";
++ reg = <1>; /* CE1 */
++ #address-cells = <1>;
++ #size-cells = <0>;
++ spi-max-frequency = <125000000>;
++ status = "okay";
++ };
++ };
++ };
++
++ __overrides__ {
++ cs0_pin = <&frag1>,"cs-gpios:4";
++ cs1_pin = <&frag1>,"cs-gpios:16";
++ cs0_spidev = <&spidev5_0>,"status";
++ cs1_spidev = <&spidev5_1>,"status";
++ };
++};
+--- a/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/superaudioboard-overlay.dts
+@@ -9,7 +9,7 @@
+ target = <&sound>;
+ __overlay__ {
+ compatible = "simple-audio-card";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_consumer>;
+ status = "okay";
+
+ simple-audio-card,name = "SuperAudioBoard";
+@@ -32,7 +32,7 @@
+ simple-audio-card,frame-master = <&sound_master>;
+
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_consumer>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+@@ -45,7 +45,7 @@
+ };
+
+ fragment@1 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+--- a/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-audio-overlay.dts
+@@ -8,7 +8,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -31,16 +31,16 @@
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,name = "tc358743";
+- simple-audio-card,bitclock-master = <&dailink0_slave>;
+- simple-audio-card,frame-master = <&dailink0_slave>;
++ simple-audio-card,bitclock-master = <&dailink0_master>;
++ simple-audio-card,frame-master = <&dailink0_master>;
+ status = "okay";
+
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_consumer>;
+ dai-tdm-slot-num = <2>;
+ dai-tdm-slot-width = <32>;
+ };
+- dailink0_slave: simple-audio-card,codec {
++ dailink0_master: simple-audio-card,codec {
+ sound-dai = <&tc358743_codec>;
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/tc358743-overlay.dts
++++ b/arch/arm/boot/dts/overlays/tc358743-overlay.dts
+@@ -101,7 +101,7 @@
+ 4lane = <0>, "-2+3-7+8";
+ link-frequency = <&tc358743_0>,"link-frequencies#0";
+ media-controller = <&csi>,"brcm,media-controller?";
+- cam0 = <&i2c_frag>, "target:0=",<&i2c_vc>,
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&tc358743>, "clocks:0=",<&cam0_clk>;
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&uart0>;
++ frag0: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ __overrides__ {
++ ctsrts = <&frag0>,"pinctrl-0:4=",<&uart0_ctsrts_pins>;
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&uart1>;
++ frag0: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ __overrides__ {
++ ctsrts = <&frag0>,"pinctrl-0:4=",<&uart1_ctsrts_pins>;
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&uart2>;
++ frag0: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ __overrides__ {
++ ctsrts = <&frag0>,"pinctrl-0:4=",<&uart2_ctsrts_pins>;
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&uart3>;
++ frag0: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ __overrides__ {
++ ctsrts = <&frag0>,"pinctrl-0:4=",<&uart3_ctsrts_pins>;
++ };
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
+@@ -0,0 +1,17 @@
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&uart4>;
++ frag0: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ __overrides__ {
++ ctsrts = <&frag0>,"pinctrl-0:4=",<&uart4_ctsrts_pins>;
++ };
++};
+--- a/arch/arm/boot/dts/overlays/udrc-overlay.dts
++++ b/arch/arm/boot/dts/overlays/udrc-overlay.dts
+@@ -9,7 +9,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ clocks = <&clocks BCM2835_CLOCK_PCM>;
+ clock-names = "pcm";
+@@ -71,7 +71,7 @@
+ target = <&sound>;
+ snd: __overlay__ {
+ compatible = "simple-audio-card";
+- i2s-controller = <&i2s>;
++ i2s-controller = <&i2s_clk_producer>;
+ status = "okay";
+
+ simple-audio-card,name = "udrc";
+@@ -93,7 +93,7 @@
+ "Line Out", "LOL";
+
+ dailink0_master: simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+
+ simple-audio-card,codec {
+--- a/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ugreen-dabboard-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_consumer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -29,14 +29,14 @@
+ compatible = "simple-audio-card";
+ simple-audio-card,format = "i2s";
+ simple-audio-card,name = "dabboard";
+- simple-audio-card,bitclock-master = <&dailink0_slave>;
+- simple-audio-card,frame-master = <&dailink0_slave>;
++ simple-audio-card,bitclock-master = <&dailink0_master>;
++ simple-audio-card,frame-master = <&dailink0_master>;
+ simple-audio-card,widgets = "Microphone", "Microphone Jack";
+ status = "okay";
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_consumer>;
+ };
+- dailink0_slave: simple-audio-card,codec {
++ dailink0_master: simple-audio-card,codec {
+ #sound-dai-cells = <0>;
+ sound-dai = <&dmic_codec>;
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-overlay.dts
+@@ -37,4 +37,10 @@
+ status = "okay";
+ };
+ };
++ fragment@5 {
++ target-path = "/chosen";
++ __overlay__ {
++ bootargs = "clk_ignore_unused";
++ };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-fkms-v3d-pi4-overlay.dts
+@@ -41,4 +41,10 @@
+ status = "okay";
+ };
+ };
++ fragment@5 {
++ target-path = "/chosen";
++ __overlay__ {
++ bootargs = "clk_ignore_unused";
++ };
++ };
+ };
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
+@@ -11,7 +11,7 @@
+ / {
+ /* No compatible as it will have come from edt-ft5406.dtsi */
+
+- fragment@0 {
++ dsi_frag: fragment@0 {
+ target = <&dsi1>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -51,8 +51,8 @@
+ fragment@1 {
+ target-path = "/";
+ __overlay__ {
+- panel_disp1: panel_disp1@0 {
+- reg = <0>;
++ panel_disp: panel_disp@1 {
++ reg = <1>;
+ compatible = "raspberrypi,7inch-dsi", "simple-panel";
+ backlight = <®_display>;
+ power-supply = <®_display>;
+@@ -64,8 +64,8 @@
+ };
+ };
+
+- reg_bridge: reg_bridge@0 {
+- reg = <0>;
++ reg_bridge: reg_bridge@1 {
++ reg = <1>;
+ compatible = "regulator-fixed";
+ regulator-name = "bridge_reg";
+ gpio = <®_display 0 0>;
+@@ -75,7 +75,7 @@
+ };
+ };
+
+- fragment@2 {
++ i2c_frag: fragment@2 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -113,6 +113,12 @@
+ };
+
+ __overrides__ {
++ dsi0 = <&dsi_frag>, "target:0=",<&dsi0>,
++ <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&ts_i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&panel_disp>, "reg:0=0",
++ <®_bridge>, "reg:0=0",
++ <®_bridge>, "regulator-name=bridge_reg_0";
+ disable_touch = <0>, "-10-11-12";
+ };
+ };
+--- 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
+@@ -9,7 +9,7 @@
+ / {
+ compatible = "brcm,bcm2835";
+
+- fragment@0 {
++ dsi_frag: fragment@0 {
+ target = <&dsi1>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -29,7 +29,7 @@
+ };
+ };
+
+- frag2: fragment@2 {
++ i2c_frag: fragment@2 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -112,12 +112,14 @@
+ <&touch>, "touchscreen-size-y:0=1480",
+ <&touch>, "touchscreen-inverted-x?",
+ <&touch>, "touchscreen-swapped-x-y?";
+- i2c1 = <&frag2>, "target:0=",<&i2c1>,
++ i2c1 = <&i2c_frag>, "target:0=",<&i2c1>,
+ <0>, "-3-4+5";
+ disable_touch = <&touch>, "status=disabled";
+ rotation = <&panel>, "rotation:0";
+ invx = <&touch>,"touchscreen-inverted-x?";
+ invy = <&touch>,"touchscreen-inverted-y?";
+ swapxy = <&touch>,"touchscreen-swapped-x-y?";
++ dsi0 = <&dsi_frag>, "target:0=",<&dsi0>,
++ <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>;
+ };
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/vc4-kms-v3d-pi5-overlay.dts
+@@ -0,0 +1,147 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include "cma-overlay.dts"
++
++&frag0 {
++ size = <((320-4)*1024*1024)>;
++};
++
++/ {
++ compatible = "brcm,bcm2712";
++
++ fragment@1 {
++ target = <&fb>;
++ __overlay__ {
++ status = "disabled";
++ };
++ };
++
++ fragment@2 {
++ target = <&aon_intr>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@3 {
++ target = <&ddc0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@4 {
++ target = <&ddc1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@5 {
++ target = <&hdmi0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@6 {
++ target = <&hdmi1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@7 {
++ target = <&hvs>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@8 {
++ target = <&mop>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@9 {
++ target = <&moplet>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@10 {
++ target = <&pixelvalve0>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@11 {
++ target = <&pixelvalve1>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@12 {
++ target = <&v3d>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@13 {
++ target = <&vec>;
++ frag13: __overlay__ {
++ status = "disabled";
++ };
++ };
++
++ fragment@14 {
++ target = <&hdmi0>;
++ __dormant__ {
++ dmas;
++ };
++ };
++
++ fragment@15 {
++ target = <&hdmi1>;
++ __dormant__ {
++ dmas;
++ };
++ };
++
++ fragment@16 {
++ target = <&disp_intr>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@17 {
++ target = <&vc4>;
++ __overlay__ {
++ /* IOMMU attaches here, where we allocate DMA buffers */
++ iommus = <&iommu4>;
++ };
++ };
++
++ __overrides__ {
++ audio = <0>,"!14";
++ audio1 = <0>,"!15";
++ noaudio = <0>,"=14", <0>,"=15";
++ composite = <0>, "!3",
++ <0>, "!4",
++ <0>, "!5",
++ <0>, "!6",
++ <0>, "!10",
++ <0>, "!11",
++ <&frag13>, "status";
++ nohdmi0 = <0>, "-3-5-10";
++ nohdmi1 = <0>, "-4-6-11";
++ nohdmi = <0>, "-3-4-5-6-10-11";
++ };
++};
+--- a/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-vga666-overlay.dts
+@@ -94,7 +94,14 @@
+ };
+ };
+
++ fragment@5 {
++ target = <&i2c_vc>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+- ddc = <0>,"=2", <0>,"=3", <0>,"=4";
++ ddc = <0>,"=2", <0>,"=3", <0>,"=4", <0>,"=5";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
++++ b/arch/arm/boot/dts/overlays/wm8960-soundcard-overlay.dts
+@@ -6,7 +6,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2s>;
++ target = <&i2s_clk_producer>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -65,7 +65,7 @@
+ "RINPUT2", "Mic Jack";
+
+ simple-audio-card,cpu {
+- sound-dai = <&i2s>;
++ sound-dai = <&i2s_clk_producer>;
+ };
+ dailink0_slave: simple-audio-card,codec {
+ sound-dai = <&wm8960>;
+--- /dev/null
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -0,0 +1,1168 @@
++#include <dt-bindings/clock/rp1.h>
++#include <dt-bindings/interrupt-controller/irq.h>
++#include <dt-bindings/mfd/rp1.h>
++
++&rp1_target {
++ rp1: rp1 {
++ compatible = "simple-bus";
++ #address-cells = <2>;
++ #size-cells = <2>;
++ #interrupt-cells = <2>;
++ interrupt-controller;
++ interrupt-parent = <&rp1>;
++
++ // ranges and dma-ranges must be provided by the includer
++
++ rp1_clocks: clocks@18000 {
++ compatible = "raspberrypi,rp1-clocks";
++ #clock-cells = <1>;
++ reg = <0xc0 0x40018000 0x0 0x10038>;
++ clocks = <&clk_xosc>;
++
++ assigned-clocks = <&rp1_clocks RP1_PLL_SYS_CORE>,
++ <&rp1_clocks RP1_PLL_AUDIO_CORE>,
++ // 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>,
++ <&rp1_clocks RP1_PLL_SYS_PRI_PH>,
++ // RP1_CLK_SLOW_SYS is used for the frequency counter (FC0)
++ <&rp1_clocks RP1_CLK_SLOW_SYS>,
++ <&rp1_clocks RP1_CLK_SDIO_TIMER>,
++ <&rp1_clocks RP1_CLK_SDIO_ALT_SRC>,
++ <&rp1_clocks RP1_CLK_ETH_TSU>;
++
++ assigned-clock-rates = <1000000000>, // RP1_PLL_SYS_CORE
++ <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
++ <100000000>, // RP1_PLL_SYS_PRI_PH
++ // Must match the XOSC frequency
++ <50000000>, // RP1_CLK_SLOW_SYS
++ <1000000>, // RP1_CLK_SDIO_TIMER
++ <200000000>, // RP1_CLK_SDIO_ALT_SRC
++ <50000000>; // RP1_CLK_ETH_TSU
++ };
++
++ rp1_uart0: serial@30000 {
++ compatible = "arm,pl011-axi";
++ reg = <0xc0 0x40030000 0x0 0x100>;
++ 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";
++ pinctrl-names = "default";
++ arm,primecell-periphid = <0x00541011>;
++ uart-has-rtscts;
++ cts-event-workaround;
++ skip-init;
++ status = "disabled";
++ };
++
++ rp1_uart1: serial@34000 {
++ compatible = "arm,pl011-axi";
++ reg = <0xc0 0x40034000 0x0 0x100>;
++ interrupts = <RP1_INT_UART1 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_UART1_TX>,
++ // <&rp1_dma RP1_DMA_UART1_RX>;
++ // dma-names = "tx", "rx";
++ pinctrl-names = "default";
++ arm,primecell-periphid = <0x00541011>;
++ uart-has-rtscts;
++ cts-event-workaround;
++ skip-init;
++ status = "disabled";
++ };
++
++ rp1_uart2: serial@38000 {
++ compatible = "arm,pl011-axi";
++ reg = <0xc0 0x40038000 0x0 0x100>;
++ interrupts = <RP1_INT_UART2 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_UART2_TX>,
++ // <&rp1_dma RP1_DMA_UART2_RX>;
++ // dma-names = "tx", "rx";
++ pinctrl-names = "default";
++ arm,primecell-periphid = <0x00541011>;
++ uart-has-rtscts;
++ cts-event-workaround;
++ skip-init;
++ status = "disabled";
++ };
++
++ rp1_uart3: serial@3c000 {
++ compatible = "arm,pl011-axi";
++ reg = <0xc0 0x4003c000 0x0 0x100>;
++ interrupts = <RP1_INT_UART3 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_UART3_TX>,
++ // <&rp1_dma RP1_DMA_UART3_RX>;
++ // dma-names = "tx", "rx";
++ pinctrl-names = "default";
++ arm,primecell-periphid = <0x00541011>;
++ uart-has-rtscts;
++ cts-event-workaround;
++ skip-init;
++ status = "disabled";
++ };
++
++ rp1_uart4: serial@40000 {
++ compatible = "arm,pl011-axi";
++ reg = <0xc0 0x40040000 0x0 0x100>;
++ interrupts = <RP1_INT_UART4 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_UART4_TX>,
++ // <&rp1_dma RP1_DMA_UART4_RX>;
++ // dma-names = "tx", "rx";
++ pinctrl-names = "default";
++ arm,primecell-periphid = <0x00541011>;
++ uart-has-rtscts;
++ cts-event-workaround;
++ skip-init;
++ status = "disabled";
++ };
++
++ rp1_uart5: serial@44000 {
++ compatible = "arm,pl011-axi";
++ reg = <0xc0 0x40044000 0x0 0x100>;
++ interrupts = <RP1_INT_UART5 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_UART5_TX>,
++ // <&rp1_dma RP1_DMA_UART5_RX>;
++ // dma-names = "tx", "rx";
++ pinctrl-names = "default";
++ arm,primecell-periphid = <0x00541011>;
++ uart-has-rtscts;
++ cts-event-workaround;
++ skip-init;
++ status = "disabled";
++ };
++
++ rp1_spi8: spi@4c000 {
++ reg = <0xc0 0x4004c000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI8 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI8_TX>,
++ <&rp1_dma RP1_DMA_SPI8_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ rp1_spi0: spi@50000 {
++ reg = <0xc0 0x40050000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI0 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI0_TX>,
++ <&rp1_dma RP1_DMA_SPI0_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ rp1_spi1: spi@54000 {
++ reg = <0xc0 0x40054000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI1 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <0>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI1_TX>,
++ <&rp1_dma RP1_DMA_SPI1_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ rp1_spi2: spi@58000 {
++ reg = <0xc0 0x40058000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI2 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI2_TX>,
++ <&rp1_dma RP1_DMA_SPI2_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ rp1_spi3: spi@5c000 {
++ reg = <0xc0 0x4005c000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI3 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI3_TX>,
++ <&rp1_dma RP1_DMA_SPI3_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ // SPI4 is a target/slave interface
++ rp1_spi4: spi@60000 {
++ reg = <0xc0 0x40060000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI4 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <0>;
++ #size-cells = <0>;
++ num-cs = <1>;
++ spi-slave;
++ dmas = <&rp1_dma RP1_DMA_SPI4_TX>,
++ <&rp1_dma RP1_DMA_SPI4_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++
++ slave {
++ compatible = "spidev";
++ spi-max-frequency = <1000000>;
++ };
++ };
++
++ rp1_spi5: spi@64000 {
++ reg = <0xc0 0x40064000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI5 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI5_TX>,
++ <&rp1_dma RP1_DMA_SPI5_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ // SPI7 is a target/slave interface
++ rp1_spi7: spi@6c000 {
++ reg = <0xc0 0x4006c000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI7 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <0>;
++ #size-cells = <0>;
++ num-cs = <1>;
++ spi-slave;
++ dmas = <&rp1_dma RP1_DMA_SPI7_TX>,
++ <&rp1_dma RP1_DMA_SPI7_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++
++ slave {
++ compatible = "spidev";
++ spi-max-frequency = <1000000>;
++ };
++ };
++
++ rp1_i2c0: i2c@70000 {
++ reg = <0xc0 0x40070000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C0 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_i2c1: i2c@74000 {
++ reg = <0xc0 0x40074000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C1 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_i2c2: i2c@78000 {
++ reg = <0xc0 0x40078000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C2 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_i2c3: i2c@7c000 {
++ reg = <0xc0 0x4007c000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C3 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_i2c4: i2c@80000 {
++ reg = <0xc0 0x40080000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C4 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_i2c5: i2c@84000 {
++ reg = <0xc0 0x40084000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C5 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_i2c6: i2c@88000 {
++ reg = <0xc0 0x40088000 0x0 0x1000>;
++ compatible = "snps,designware-i2c";
++ interrupts = <RP1_INT_I2C6 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ status = "disabled";
++ };
++
++ rp1_pwm0: pwm@98000 {
++ compatible = "raspberrypi,rp1-pwm";
++ reg = <0xc0 0x40098000 0x0 0x100>;
++ #pwm-cells = <3>;
++ clocks = <&rp1_clocks RP1_CLK_PWM0>;
++ assigned-clocks = <&rp1_clocks RP1_CLK_PWM0>;
++ assigned-clock-rates = <6144000>;
++ status = "disabled";
++ };
++
++ rp1_pwm1: pwm@9c000 {
++ compatible = "raspberrypi,rp1-pwm";
++ reg = <0xc0 0x4009c000 0x0 0x100>;
++ #pwm-cells = <3>;
++ clocks = <&rp1_clocks RP1_CLK_PWM1>;
++ assigned-clocks = <&rp1_clocks RP1_CLK_PWM1>;
++ assigned-clock-rates = <6144000>;
++ status = "disabled";
++ };
++
++ rp1_i2s0: i2s@a0000 {
++ reg = <0xc0 0x400a0000 0x0 0x1000>;
++ compatible = "snps,designware-i2s";
++ // Providing an interrupt disables DMA
++ // interrupts = <RP1_INT_I2S0 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_I2S>;
++ clock-names = "i2sclk";
++ #sound-dai-cells = <0>;
++ dmas = <&rp1_dma RP1_DMA_I2S0_TX>,<&rp1_dma RP1_DMA_I2S0_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ rp1_i2s1: i2s@a4000 {
++ reg = <0xc0 0x400a4000 0x0 0x1000>;
++ compatible = "snps,designware-i2s";
++ // Providing an interrupt disables DMA
++ // interrupts = <RP1_INT_I2S1 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_I2S>;
++ clock-names = "i2sclk";
++ #sound-dai-cells = <0>;
++ dmas = <&rp1_dma RP1_DMA_I2S1_TX>,<&rp1_dma RP1_DMA_I2S1_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
++
++ rp1_i2s2: i2s@a8000 {
++ reg = <0xc0 0x400a8000 0x0 0x1000>;
++ compatible = "snps,designware-i2s";
++ // Providing an interrupt disables DMA
++ // interrupts = <RP1_INT_I2S2 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_I2S>;
++ status = "disabled";
++ };
++
++ rp1_sdio_clk0: sdio_clk0@b0004 {
++ compatible = "raspberrypi,rp1-sdio-clk";
++ reg = <0xc0 0x400b0004 0x0 0x1c>;
++ clocks = <&sdio_src &sdhci_core>;
++ clock-names = "src", "base";
++ #clock-cells = <0>;
++ status = "disabled";
++ };
++
++ rp1_sdio_clk1: sdio_clk1@b4004 {
++ compatible = "raspberrypi,rp1-sdio-clk";
++ reg = <0xc0 0x400b4004 0x0 0x1c>;
++ clocks = <&sdio_src &sdhci_core>;
++ clock-names = "src", "base";
++ #clock-cells = <0>;
++ status = "disabled";
++ };
++
++ rp1_adc: adc@c8000 {
++ compatible = "raspberrypi,rp1-adc";
++ reg = <0xc0 0x400c8000 0x0 0x4000>;
++ clocks = <&rp1_clocks RP1_CLK_ADC>;
++ clock-names = "adcclk";
++ #clock-cells = <0>;
++ vref-supply = <&rp1_vdd_3v3>;
++ status = "disabled";
++ };
++
++ rp1_gpio: gpio@d0000 {
++ reg = <0xc0 0x400d0000 0x0 0xc000>,
++ <0xc0 0x400e0000 0x0 0xc000>,
++ <0xc0 0x400f0000 0x0 0xc000>;
++ compatible = "raspberrypi,rp1-gpio";
++ interrupts = <RP1_INT_IO_BANK0 IRQ_TYPE_LEVEL_HIGH>,
++ <RP1_INT_IO_BANK1 IRQ_TYPE_LEVEL_HIGH>,
++ <RP1_INT_IO_BANK2 IRQ_TYPE_LEVEL_HIGH>;
++ gpio-controller;
++ #gpio-cells = <2>;
++ interrupt-controller;
++ #interrupt-cells = <2>;
++
++ rp1_uart0_14_15: rp1_uart0_14_15 {
++ pin_txd {
++ function = "uart0";
++ pins = "gpio14";
++ bias-disable;
++ };
++ pin_rxd {
++ function = "uart0";
++ pins = "gpio15";
++ bias-pull-up;
++ };
++ };
++ rp1_uart0_ctsrts_16_17: rp1_uart0_ctsrts_16_17 {
++ pin_cts {
++ function = "uart0";
++ pins = "gpio16";
++ bias-pull-up;
++ };
++ pin_rts {
++ function = "uart0";
++ pins = "gpio17";
++ bias-disable;
++ };
++ };
++ rp1_uart1_0_1: rp1_uart1_0_1 {
++ pin_txd {
++ function = "uart1";
++ pins = "gpio0";
++ bias-disable;
++ };
++ pin_rxd {
++ function = "uart1";
++ pins = "gpio1";
++ bias-pull-up;
++ };
++ };
++ rp1_uart1_ctsrts_2_3: rp1_uart1_ctsrts_2_3 {
++ pin_cts {
++ function = "uart1";
++ pins = "gpio2";
++ bias-pull-up;
++ };
++ pin_rts {
++ function = "uart1";
++ pins = "gpio3";
++ bias-disable;
++ };
++ };
++ rp1_uart2_4_5: rp1_uart2_4_5 {
++ pin_txd {
++ function = "uart2";
++ pins = "gpio4";
++ bias-disable;
++ };
++ pin_rxd {
++ function = "uart2";
++ pins = "gpio5";
++ bias-pull-up;
++ };
++ };
++ rp1_uart2_ctsrts_6_7: rp1_uart2_ctsrts_6_7 {
++ pin_cts {
++ function = "uart2";
++ pins = "gpio6";
++ bias-pull-up;
++ };
++ pin_rts {
++ function = "uart2";
++ pins = "gpio7";
++ bias-disable;
++ };
++ };
++ rp1_uart3_8_9: rp1_uart3_8_9 {
++ pin_txd {
++ function = "uart3";
++ pins = "gpio8";
++ bias-disable;
++ };
++ pin_rxd {
++ function = "uart3";
++ pins = "gpio9";
++ bias-pull-up;
++ };
++ };
++ rp1_uart3_ctsrts_10_11: rp1_uart3_ctsrts_10_11 {
++ pin_cts {
++ function = "uart3";
++ pins = "gpio10";
++ bias-pull-up;
++ };
++ pin_rts {
++ function = "uart3";
++ pins = "gpio11";
++ bias-disable;
++ };
++ };
++ rp1_uart4_12_13: rp1_uart4_12_13 {
++ pin_txd {
++ function = "uart4";
++ pins = "gpio12";
++ bias-disable;
++ };
++ pin_rxd {
++ function = "uart4";
++ pins = "gpio13";
++ bias-pull-up;
++ };
++ };
++ rp1_uart4_ctsrts_14_15: rp1_uart4_ctsrts_14_15 {
++ pin_cts {
++ function = "uart4";
++ pins = "gpio14";
++ bias-pull-up;
++ };
++ pin_rts {
++ function = "uart4";
++ pins = "gpio15";
++ bias-disable;
++ };
++ };
++
++ rp1_sdio0_22_27: rp1_sdio0_22_27 {
++ pin_clk {
++ function = "sd0";
++ pins = "gpio22";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++ pin_cmd {
++ function = "sd0";
++ pins = "gpio23";
++ bias-pull-up;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++ pins_dat {
++ function = "sd0";
++ pins = "gpio24", "gpio25", "gpio26", "gpio27";
++ bias-pull-up;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++ };
++
++ rp1_sdio1_28_33: rp1_sdio1_28_33 {
++ pin_clk {
++ function = "sd1";
++ pins = "gpio28";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++ pin_cmd {
++ function = "sd1";
++ pins = "gpio29";
++ bias-pull-up;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++ pins_dat {
++ function = "sd1";
++ pins = "gpio30", "gpio31", "gpio32", "gpio33";
++ bias-pull-up;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++ };
++
++ rp1_i2s0_18_21: rp1_i2s0_18_21 {
++ function = "i2s0";
++ pins = "gpio18", "gpio19", "gpio20", "gpio21";
++ bias-disable;
++ };
++
++ rp1_i2s1_18_21: rp1_i2s1_18_21 {
++ function = "i2s1";
++ pins = "gpio18", "gpio19", "gpio20", "gpio21";
++ bias-disable;
++ };
++
++ rp1_i2c4_34_35: rp1_i2c4_34_35 {
++ function = "i2c4";
++ pins = "gpio34", "gpio35";
++ bias-pull-up;
++ };
++ rp1_i2c6_38_39: rp1_i2c6_38_39 {
++ function = "i2c6";
++ pins = "gpio38", "gpio39";
++ bias-pull-up;
++ };
++ rp1_i2c4_40_41: rp1_i2c4_40_41 {
++ function = "i2c4";
++ pins = "gpio40", "gpio41";
++ bias-pull-up;
++ };
++ rp1_i2c5_44_45: rp1_i2c5_44_45 {
++ function = "i2c5";
++ pins = "gpio44", "gpio45";
++ bias-pull-up;
++ };
++ rp1_i2c0_0_1: rp1_i2c0_0_1 {
++ function = "i2c0";
++ pins = "gpio0", "gpio1";
++ bias-pull-up;
++ };
++ rp1_i2c0_8_9: rp1_i2c0_8_9 {
++ function = "i2c0";
++ pins = "gpio8", "gpio9";
++ bias-pull-up;
++ };
++ rp1_i2c1_2_3: rp1_i2c1_2_3 {
++ function = "i2c1";
++ pins = "gpio2", "gpio3";
++ bias-pull-up;
++ };
++ rp1_i2c1_10_11: rp1_i2c1_10_11 {
++ function = "i2c1";
++ pins = "gpio10", "gpio11";
++ bias-pull-up;
++ };
++ rp1_i2c2_4_5: rp1_i2c2_4_5 {
++ function = "i2c2";
++ pins = "gpio4", "gpio5";
++ bias-pull-up;
++ };
++ rp1_i2c2_12_13: rp1_i2c2_12_13 {
++ function = "i2c2";
++ pins = "gpio12", "gpio13";
++ bias-pull-up;
++ };
++ rp1_i2c3_6_7: rp1_i2c3_6_7 {
++ function = "i2c3";
++ pins = "gpio6", "gpio7";
++ bias-pull-up;
++ };
++ rp1_i2c3_14_15: rp1_i2c3_14_15 {
++ function = "i2c3";
++ pins = "gpio14", "gpio15";
++ bias-pull-up;
++ };
++ rp1_i2c3_22_23: rp1_i2c3_22_23 {
++ function = "i2c3";
++ pins = "gpio22", "gpio23";
++ bias-pull-up;
++ };
++
++ // DPI mappings with HSYNC,VSYNC but without PIXCLK,DE
++ rp1_dpi_16bit_gpio2: rp1_dpi_16bit_gpio2 { /* Mode 2, not fully supported by RP1 */
++ function = "dpi";
++ pins = "gpio2", "gpio3", "gpio4", "gpio5",
++ "gpio6", "gpio7", "gpio8", "gpio9",
++ "gpio10", "gpio11", "gpio12", "gpio13",
++ "gpio14", "gpio15", "gpio16", "gpio17",
++ "gpio18", "gpio19";
++ bias-disable;
++ };
++ rp1_dpi_16bit_cpadhi_gpio2: rp1_dpi_16bit_cpadhi_gpio2 { /* Mode 3 */
++ function = "dpi";
++ pins = "gpio2", "gpio3", "gpio4", "gpio5",
++ "gpio6", "gpio7", "gpio8",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17",
++ "gpio20", "gpio21", "gpio22", "gpio23",
++ "gpio24";
++ bias-disable;
++ };
++ rp1_dpi_16bit_pad666_gpio2: rp1_dpi_16bit_pad666_gpio2 { /* Mode 4 */
++ function = "dpi";
++ pins = "gpio2", "gpio3",
++ "gpio5", "gpio6", "gpio7", "gpio8",
++ "gpio9",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17",
++ "gpio21", "gpio22", "gpio23", "gpio24",
++ "gpio25";
++ bias-disable;
++ };
++ rp1_dpi_18bit_gpio2: rp1_dpi_18bit_gpio2 { /* Mode 5, not fully supported by RP1 */
++ function = "dpi";
++ pins = "gpio2", "gpio3", "gpio4", "gpio5",
++ "gpio6", "gpio7", "gpio8", "gpio9",
++ "gpio10", "gpio11", "gpio12", "gpio13",
++ "gpio14", "gpio15", "gpio16", "gpio17",
++ "gpio18", "gpio19", "gpio20", "gpio21";
++ bias-disable;
++ };
++ rp1_dpi_18bit_cpadhi_gpio2: rp1_dpi_18bit_cpadhi_gpio2 { /* Mode 6 */
++ function = "dpi";
++ pins = "gpio2", "gpio3", "gpio4", "gpio5",
++ "gpio6", "gpio7", "gpio8", "gpio9",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17",
++ "gpio20", "gpio21", "gpio22", "gpio23",
++ "gpio24", "gpio25";
++ bias-disable;
++ };
++ rp1_dpi_24bit_gpio2: rp1_dpi_24bit_gpio2 { /* Mode 7 */
++ function = "dpi";
++ pins = "gpio2", "gpio3", "gpio4", "gpio5",
++ "gpio6", "gpio7", "gpio8", "gpio9",
++ "gpio10", "gpio11", "gpio12", "gpio13",
++ "gpio14", "gpio15", "gpio16", "gpio17",
++ "gpio18", "gpio19", "gpio20", "gpio21",
++ "gpio22", "gpio23", "gpio24", "gpio25",
++ "gpio26", "gpio27";
++ bias-disable;
++ };
++ rp1_dpi_hvsync: rp1_dpi_hvsync { /* Sync only, for use with int VDAC */
++ function = "dpi";
++ pins = "gpio2", "gpio3";
++ bias-disable;
++ };
++
++ // More DPI mappings, including PIXCLK,DE on GPIOs 0,1
++ rp1_dpi_16bit_gpio0: rp1_dpi_16bit_gpio0 { /* Mode 2, not fully supported by RP1 */
++ function = "dpi";
++ pins = "gpio0", "gpio1", "gpio2", "gpio3",
++ "gpio4", "gpio5", "gpio6", "gpio7",
++ "gpio8", "gpio9", "gpio10", "gpio11",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17", "gpio18", "gpio19";
++ bias-disable;
++ };
++ rp1_dpi_16bit_cpadhi_gpio0: rp1_dpi_16bit_cpadhi_gpio0 { /* Mode 3 */
++ function = "dpi";
++ pins = "gpio0", "gpio1", "gpio2", "gpio3",
++ "gpio4", "gpio5", "gpio6", "gpio7",
++ "gpio8",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17",
++ "gpio20", "gpio21", "gpio22", "gpio23",
++ "gpio24";
++ bias-disable;
++ };
++ rp1_dpi_16bit_pad666_gpio0: rp1_dpi_16bit_pad666_gpio0 { /* Mode 4 */
++ function = "dpi";
++ pins = "gpio0", "gpio1", "gpio2", "gpio3",
++ "gpio5", "gpio6", "gpio7", "gpio8",
++ "gpio9",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17",
++ "gpio21", "gpio22", "gpio23", "gpio24",
++ "gpio25";
++ bias-disable;
++ };
++ rp1_dpi_18bit_gpio0: rp1_dpi_18bit_gpio0 { /* Mode 5, not fully supported by RP1 */
++ function = "dpi";
++ pins = "gpio0", "gpio1", "gpio2", "gpio3",
++ "gpio4", "gpio5", "gpio6", "gpio7",
++ "gpio8", "gpio9", "gpio10", "gpio11",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17", "gpio18", "gpio19",
++ "gpio20", "gpio21";
++ bias-disable;
++ };
++ rp1_dpi_18bit_cpadhi_gpio0: rp1_dpi_18bit_cpadhi_gpio0 { /* Mode 6 */
++ function = "dpi";
++ pins = "gpio0", "gpio1", "gpio2", "gpio3",
++ "gpio4", "gpio5", "gpio6", "gpio7",
++ "gpio8", "gpio9",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17",
++ "gpio20", "gpio21", "gpio22", "gpio23",
++ "gpio24", "gpio25";
++ bias-disable;
++ };
++ rp1_dpi_24bit_gpio0: rp1_dpi_24bit_gpio0 { /* Mode 7 -- All GPIOs used! */
++ function = "dpi";
++ pins = "gpio0", "gpio1", "gpio2", "gpio3",
++ "gpio4", "gpio5", "gpio6", "gpio7",
++ "gpio8", "gpio9", "gpio10", "gpio11",
++ "gpio12", "gpio13", "gpio14", "gpio15",
++ "gpio16", "gpio17", "gpio18", "gpio19",
++ "gpio20", "gpio21", "gpio22", "gpio23",
++ "gpio24", "gpio25", "gpio26", "gpio27";
++ bias-disable;
++ };
++
++ rp1_pwm1_gpio45: rp1_pwm1_gpio45 {
++ function = "pwm1";
++ pins = "gpio45";
++ bias-pull-down;
++ };
++
++ rp1_spi0_gpio9: rp1_spi0_gpio9 {
++ function = "spi0";
++ pins = "gpio9", "gpio10", "gpio11";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi0_cs_gpio7: rp1_spi0_cs_gpio7 {
++ function = "spi0";
++ pins = "gpio7", "gpio8";
++ bias-pull-up;
++ };
++
++ rp1_spi1_gpio19: rp1_spi1_gpio19 {
++ function = "spi1";
++ pins = "gpio19", "gpio20", "gpio21";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi2_gpio1: rp1_spi2_gpio1 {
++ function = "spi2";
++ pins = "gpio1", "gpio2", "gpio3";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi3_gpio5: rp1_spi3_gpio5 {
++ function = "spi3";
++ pins = "gpio5", "gpio6", "gpio7";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi4_gpio9: rp1_spi4_gpio9 {
++ function = "spi4";
++ pins = "gpio9", "gpio10", "gpio11";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi5_gpio13: rp1_spi5_gpio13 {
++ function = "spi5";
++ pins = "gpio13", "gpio14", "gpio15";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi8_gpio49: rp1_spi8_gpio49 {
++ function = "spi8";
++ pins = "gpio49", "gpio50", "gpio51";
++ bias-disable;
++ drive-strength = <12>;
++ slew-rate = <1>;
++ };
++
++ rp1_spi8_cs_gpio52: rp1_spi8_cs_gpio52 {
++ function = "spi0";
++ pins = "gpio52", "gpio53";
++ bias-pull-up;
++ };
++ };
++
++ rp1_eth: ethernet@100000 {
++ reg = <0xc0 0x40100000 0x0 0x4000>;
++ compatible = "cdns,macb";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ interrupts = <RP1_INT_ETH IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&macb_pclk &macb_hclk &rp1_clocks RP1_CLK_ETH_TSU>;
++ clock-names = "pclk", "hclk", "tsu_clk";
++ phy-mode = "rgmii-id";
++ cdns,aw2w-max-pipe = /bits/ 8 <8>;
++ cdns,ar2r-max-pipe = /bits/ 8 <8>;
++ cdns,use-aw2b-fill;
++ local-mac-address = [00 00 00 00 00 00];
++ status = "disabled";
++ };
++
++ rp1_csi0: csi@110000 {
++ compatible = "raspberrypi,rp1-cfe";
++ reg = <0xc0 0x40110000 0x0 0x100>, // CSI2 DMA address
++ <0xc0 0x40114000 0x0 0x100>, // PHY/CSI Host address
++ <0xc0 0x40120000 0x0 0x100>, // MIPI CFG address
++ <0xc0 0x40124000 0x0 0x1000>; // PiSP FE address
++
++ // interrupts must match rp1_pisp_fe setup
++ interrupts = <RP1_INT_MIPI0 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>;
++ assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>;
++ assigned-clock-rates = <25000000>;
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ rp1_csi1: csi@128000 {
++ compatible = "raspberrypi,rp1-cfe";
++ reg = <0xc0 0x40128000 0x0 0x100>, // CSI2 DMA address
++ <0xc0 0x4012c000 0x0 0x100>, // PHY/CSI Host address
++ <0xc0 0x40138000 0x0 0x100>, // MIPI CFG address
++ <0xc0 0x4013c000 0x0 0x1000>; // PiSP FE address
++
++ // interrupts must match rp1_pisp_fe setup
++ interrupts = <RP1_INT_MIPI1 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>;
++ assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>;
++ assigned-clock-rates = <25000000>;
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "disabled";
++ };
++
++ rp1_mmc0: mmc@180000 {
++ reg = <0xc0 0x40180000 0x0 0x100>;
++ compatible = "snps,dwcmshc-sdhci";
++ interrupts = <RP1_INT_SDIO0 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
++ &rp1_clocks RP1_CLK_SDIO_TIMER
++ &rp1_sdio_clk0>;
++ clock-names = "bus", "core", "timeout", "sdio";
++ /* Bank 0 VDDIO is fixed */
++ no-1-8-v;
++ bus-width = <4>;
++ vmmc-supply = <&rp1_vdd_3v3>;
++ broken-cd;
++ status = "disabled";
++ };
++
++ rp1_mmc1: mmc@184000 {
++ reg = <0xc0 0x40184000 0x0 0x100>;
++ compatible = "snps,dwcmshc-sdhci";
++ interrupts = <RP1_INT_SDIO1 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
++ &rp1_clocks RP1_CLK_SDIO_TIMER
++ &rp1_sdio_clk1>;
++ clock-names = "bus", "core", "timeout", "sdio";
++ bus-width = <4>;
++ vmmc-supply = <&rp1_vdd_3v3>;
++ /* Nerf SDR speeds */
++ sdhci-caps-mask = <0x3 0x0>;
++ broken-cd;
++ status = "disabled";
++ };
++
++ rp1_dma: dma@188000 {
++ reg = <0xc0 0x40188000 0x0 0x1000>;
++ compatible = "snps,axi-dma-1.01a";
++ interrupts = <RP1_INT_DMA IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&sdhci_core &rp1_clocks RP1_CLK_SYS>;
++ clock-names = "core-clk", "cfgr-clk";
++
++ #dma-cells = <1>;
++ dma-channels = <8>;
++ snps,dma-masters = <1>;
++ snps,dma-targets = <64>;
++ snps,data-width = <4>; // (8 << 4) == 128 bits
++ snps,block-size = <0x40000 0x40000 0x40000 0x40000 0x40000 0x40000 0x40000 0x40000>;
++ snps,priority = <0 1 2 3 4 5 6 7>;
++ snps,axi-max-burst-len = <8>;
++ status = "disabled";
++ };
++
++ rp1_usb0: usb@200000 {
++ reg = <0xc0 0x40200000 0x0 0x100000>;
++ compatible = "snps,dwc3";
++ dr_mode = "host";
++ usb3-lpm-capable;
++ snps,axi-pipe-limit = /bits/ 8 <8>;
++ snps,dis_rxdet_inp3_quirk;
++ snps,tx-max-burst-prd = <8>;
++ snps,tx-thr-num-pkt-prd = <2>;
++ interrupts = <RP1_INT_USBHOST0_0 IRQ_TYPE_EDGE_RISING>;
++ status = "disabled";
++ };
++
++ rp1_usb1: usb@300000 {
++ reg = <0xc0 0x40300000 0x0 0x100000>;
++ compatible = "snps,dwc3";
++ dr_mode = "host";
++ usb3-lpm-capable;
++ snps,axi-pipe-limit = /bits/ 8 <8>;
++ snps,dis_rxdet_inp3_quirk;
++ snps,tx-max-burst-prd = <8>;
++ snps,tx-thr-num-pkt-prd = <2>;
++ interrupts = <RP1_INT_USBHOST1_0 IRQ_TYPE_EDGE_RISING>;
++ status = "disabled";
++ };
++
++ rp1_dsi0: dsi@110000 {
++ compatible = "raspberrypi,rp1dsi";
++ status = "disabled";
++ reg = <0xc0 0x40118000 0x0 0x1000>, // MIPI0 DSI DMA (ArgonDPI)
++ <0xc0 0x4011c000 0x0 0x1000>, // MIPI0 DSI Host (SNPS)
++ <0xc0 0x40120000 0x0 0x1000>; // MIPI0 CFG
++
++ interrupts = <RP1_INT_MIPI0 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>, // required, config bus clock
++ <&rp1_clocks RP1_CLK_MIPI0_DPI>, // required, pixel clock
++ <&clksrc_mipi0_dsi_byteclk>, // internal, parent for divide
++ <&clk_xosc>; // hardwired to DSI "refclk"
++ clock-names = "cfgclk", "dpiclk", "byteclk", "refclk";
++
++ assigned-clocks = <&rp1_clocks RP1_CLK_MIPI0_CFG>,
++ <&rp1_clocks RP1_CLK_MIPI0_DPI>;
++ assigned-clock-rates = <25000000>;
++ assigned-clock-parents = <0>, <&clksrc_mipi0_dsi_byteclk>;
++ };
++
++ rp1_dsi1: dsi@128000 {
++ compatible = "raspberrypi,rp1dsi";
++ status = "disabled";
++ reg = <0xc0 0x40130000 0x0 0x1000>, // MIPI1 DSI DMA (ArgonDPI)
++ <0xc0 0x40134000 0x0 0x1000>, // MIPI1 DSI Host (SNPS)
++ <0xc0 0x40138000 0x0 0x1000>; // MIPI1 CFG
++
++ interrupts = <RP1_INT_MIPI1 IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>, // required, config bus clock
++ <&rp1_clocks RP1_CLK_MIPI1_DPI>, // required, pixel clock
++ <&clksrc_mipi1_dsi_byteclk>, // internal, parent for divide
++ <&clk_xosc>; // hardwired to DSI "refclk"
++ clock-names = "cfgclk", "dpiclk", "byteclk", "refclk";
++
++ assigned-clocks = <&rp1_clocks RP1_CLK_MIPI1_CFG>,
++ <&rp1_clocks RP1_CLK_MIPI1_DPI>;
++ assigned-clock-rates = <25000000>;
++ assigned-clock-parents = <0>, <&clksrc_mipi1_dsi_byteclk>;
++ };
++
++ /* VEC and DPI both need to control PLL_VIDEO and cannot work together; */
++ /* config.txt should enable one or other using dtparam=vec or an overlay. */
++ rp1_vec: vec@144000 {
++ compatible = "raspberrypi,rp1vec";
++ status = "disabled";
++ reg = <0xc0 0x40144000 0x0 0x1000>, // VIDEO_OUT_VEC
++ <0xc0 0x40140000 0x0 0x1000>; // VIDEO_OUT_CFG
++
++ interrupts = <RP1_INT_VIDEO_OUT IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&rp1_clocks RP1_CLK_VEC>;
++
++ assigned-clocks = <&rp1_clocks RP1_PLL_VIDEO_CORE>,
++ <&rp1_clocks RP1_PLL_VIDEO_SEC>,
++ <&rp1_clocks RP1_CLK_VEC>;
++ assigned-clock-rates = <1188000000>,
++ <108000000>,
++ <108000000>;
++ assigned-clock-parents = <0>,
++ <&rp1_clocks RP1_PLL_VIDEO_CORE>,
++ <&rp1_clocks RP1_PLL_VIDEO_SEC>;
++ };
++
++ rp1_dpi: dpi@148000 {
++ compatible = "raspberrypi,rp1dpi";
++ status = "disabled";
++ reg = <0xc0 0x40148000 0x0 0x1000>, // VIDEO_OUT DPI
++ <0xc0 0x40140000 0x0 0x1000>; // VIDEO_OUT_CFG
++
++ interrupts = <RP1_INT_VIDEO_OUT IRQ_TYPE_LEVEL_HIGH>;
++
++ clocks = <&rp1_clocks RP1_CLK_DPI>, // DPI pixel clock
++ <&rp1_clocks RP1_PLL_VIDEO>, // PLL primary divider, and
++ <&rp1_clocks RP1_PLL_VIDEO_CORE>; // VCO, which we also control
++ clock-names = "dpiclk", "plldiv", "pllcore";
++
++ assigned-clocks = <&rp1_clocks RP1_CLK_DPI>;
++ assigned-clock-parents = <&rp1_clocks RP1_PLL_VIDEO>;
++ };
++ };
++};
++
++&clocks {
++ clk_xosc: clk_xosc {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "xosc";
++ clock-frequency = <50000000>;
++ };
++ macb_pclk: macb_pclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "pclk";
++ clock-frequency = <200000000>;
++ };
++ macb_hclk: macb_hclk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "hclk";
++ clock-frequency = <200000000>;
++ };
++ sdio_src: sdio_src {
++ // 400 MHz on FPGA. PLL sys VCO on asic
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "src";
++ clock-frequency = <1000000000>;
++ };
++ sdhci_core: sdhci_core {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "core";
++ clock-frequency = <50000000>;
++ };
++ clksrc_mipi0_dsi_byteclk: clksrc_mipi0_dsi_byteclk {
++ // This clock is synthesized by MIPI0 D-PHY, when DSI is running.
++ // Its frequency is not known a priori (until a panel driver attaches)
++ // so assign a made-up frequency of 72MHz so it can be divided for DPI.
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "clksrc_mipi0_dsi_byteclk";
++ clock-frequency = <72000000>;
++ };
++ clksrc_mipi1_dsi_byteclk: clksrc_mipi1_dsi_byteclk {
++ // This clock is synthesized by MIPI1 D-PHY, when DSI is running.
++ // Its frequency is not known a priori (until a panel driver attaches)
++ // so assign a made-up frequency of 72MHz so it can be divided for DPI.
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-output-names = "clksrc_mipi1_dsi_byteclk";
++ clock-frequency = <72000000>;
++ };
++};
++
++/ {
++ rp1_vdd_3v3: rp1_vdd_3v3 {
++ compatible = "regulator-fixed";
++ regulator-name = "vdd-3v3";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-always-on;
++ };
++};
+--- a/arch/arm64/boot/dts/broadcom/Makefile
++++ b/arch/arm64/boot/dts/broadcom/Makefile
+@@ -16,6 +16,7 @@ dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rp
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2710-rpi-cm3.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-cm4.dtb
+ dtb-$(CONFIG_ARCH_BCM2835) += bcm2711-rpi-cm4s.dtb
++dtb-$(CONFIG_ARCH_BCM2835) += bcm2712-rpi-5-b.dtb
+
+ subdir-y += bcmbca
+ subdir-y += northstar2
+--- /dev/null
++++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+@@ -0,0 +1 @@
++#include "../../../../arm/boot/dts/bcm2712-rpi-5-b.dts"
--- /dev/null
+From fa18902ee1e53ad391a455a01be3ab2ea1c5af5f Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 21 May 2021 12:33:38 +0100
+Subject: [PATCH] gpio_brcmstb: Allow to build for ARCH_BCM2835
+
+gpio-brcmstb: Report the correct bank width
+
+gpio: brcmstb: Use bank address as gpiochip label
+
+If the path to the device node is used as gpiochip label then
+gpio-brcmstb instances with multiple banks end up with duplicated
+names. Instead, use a combination of the driver name with the physical
+address of the bank, which is both unique and helpful for devmem
+debugging.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+gpio: mmio: Add DIRECT mode for shared access
+
+The generic MMIO GPIO library uses shadow registers for efficiency,
+but this breaks attempts by raspi-gpio to change other GPIOs in the
+same bank. Add a DIRECT mode that makes fewer assumptions about the
+existing register contents, but note that genuinely simultaneous
+accesses are likely to lose updates.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+gpio: brcmstb: Don't always clear interrupt mask
+
+If the GPIO controller is not being used as an interrupt source
+leave the interrupt mask register alone. On BCM2712 it might be used
+to generate interrupts to the VPU firmware, and on other devices it
+doesn't matter since no interrupts will be generated.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/gpio/Kconfig | 2 +-
+ drivers/gpio/gpio-brcmstb.c | 14 ++--
+ drivers/gpio/gpio-mmio.c | 124 ++++++++++++++++++++++++++++++++++--
+ include/linux/gpio/driver.h | 1 +
+ 4 files changed, 131 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -203,7 +203,7 @@ config GPIO_BCM_VIRT
+ config GPIO_BRCMSTB
+ tristate "BRCMSTB GPIO support"
+ default y if (ARCH_BRCMSTB || BMIPS_GENERIC)
+- depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST)
++ depends on OF_GPIO && (ARCH_BRCMSTB || BMIPS_GENERIC || ARCH_BCM2835 || COMPILE_TEST)
+ select GPIO_GENERIC
+ select IRQ_DOMAIN
+ help
+--- a/drivers/gpio/gpio-brcmstb.c
++++ b/drivers/gpio/gpio-brcmstb.c
+@@ -640,6 +640,8 @@ static int brcmstb_gpio_probe(struct pla
+ #if defined(CONFIG_MIPS) && defined(__BIG_ENDIAN)
+ flags = BGPIOF_BIG_ENDIAN_BYTE_ORDER;
+ #endif
++ if (of_property_read_bool(np, "brcm,gpio-direct"))
++ flags |= BGPIOF_REG_DIRECT;
+
+ of_property_for_each_u32(np, "brcm,gpio-bank-widths", prop, p,
+ bank_width) {
+@@ -689,7 +691,9 @@ static int brcmstb_gpio_probe(struct pla
+ }
+
+ gc->owner = THIS_MODULE;
+- gc->label = devm_kasprintf(dev, GFP_KERNEL, "%pOF", np);
++ gc->label = devm_kasprintf(dev, GFP_KERNEL, "gpio-brcmstb@%zx",
++ (size_t)res->start +
++ GIO_BANK_OFF(bank->id, 0));
+ if (!gc->label) {
+ err = -ENOMEM;
+ goto fail;
+@@ -698,7 +702,7 @@ static int brcmstb_gpio_probe(struct pla
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = brcmstb_gpio_of_xlate;
+ /* not all ngpio lines are valid, will use bank width later */
+- gc->ngpio = MAX_GPIO_PER_BANK;
++ gc->ngpio = bank_width;
+ gc->offset = bank->id * MAX_GPIO_PER_BANK;
+ if (priv->parent_irq > 0)
+ gc->to_irq = brcmstb_gpio_to_irq;
+@@ -707,8 +711,10 @@ static int brcmstb_gpio_probe(struct pla
+ * Mask all interrupts by default, since wakeup interrupts may
+ * be retained from S5 cold boot
+ */
+- need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
+- gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
++ if (priv->parent_irq > 0) {
++ need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
++ gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
++ }
+
+ err = gpiochip_add_data(gc, bank);
+ if (err) {
+--- a/drivers/gpio/gpio-mmio.c
++++ b/drivers/gpio/gpio-mmio.c
+@@ -232,6 +232,25 @@ static void bgpio_set(struct gpio_chip *
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ }
+
++static void bgpio_set_direct(struct gpio_chip *gc, unsigned int gpio, int val)
++{
++ unsigned long mask = bgpio_line2mask(gc, gpio);
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++ gc->bgpio_data = gc->read_reg(gc->reg_dat);
++
++ if (val)
++ gc->bgpio_data |= mask;
++ else
++ gc->bgpio_data &= ~mask;
++
++ gc->write_reg(gc->reg_dat, gc->bgpio_data);
++
++ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
++
+ static void bgpio_set_with_clear(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+ {
+@@ -324,6 +343,27 @@ static void bgpio_set_multiple_with_clea
+ gc->write_reg(gc->reg_clr, clear_mask);
+ }
+
++static void bgpio_set_multiple_direct(struct gpio_chip *gc,
++ unsigned long *mask,
++ unsigned long *bits)
++{
++ unsigned long flags;
++ unsigned long set_mask, clear_mask;
++
++ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++ bgpio_multiple_get_masks(gc, mask, bits, &set_mask, &clear_mask);
++
++ gc->bgpio_data = gc->read_reg(gc->reg_dat);
++
++ gc->bgpio_data |= set_mask;
++ gc->bgpio_data &= ~clear_mask;
++
++ gc->write_reg(gc->reg_dat, gc->bgpio_data);
++
++ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
++
+ static int bgpio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
+ {
+ return 0;
+@@ -361,6 +401,29 @@ static int bgpio_dir_in(struct gpio_chip
+ return 0;
+ }
+
++static int bgpio_dir_in_direct(struct gpio_chip *gc, unsigned int gpio)
++{
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++ if (gc->reg_dir_in)
++ gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in);
++ if (gc->reg_dir_out)
++ gc->bgpio_dir = gc->read_reg(gc->reg_dir_out);
++
++ gc->bgpio_dir &= ~bgpio_line2mask(gc, gpio);
++
++ if (gc->reg_dir_in)
++ gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
++ if (gc->reg_dir_out)
++ gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
++
++ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++
++ return 0;
++}
++
+ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
+ {
+ /* Return 0 if output, 1 if input */
+@@ -399,6 +462,28 @@ static void bgpio_dir_out(struct gpio_ch
+ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
+ }
+
++static void bgpio_dir_out_direct(struct gpio_chip *gc, unsigned int gpio,
++ int val)
++{
++ unsigned long flags;
++
++ raw_spin_lock_irqsave(&gc->bgpio_lock, flags);
++
++ if (gc->reg_dir_in)
++ gc->bgpio_dir = ~gc->read_reg(gc->reg_dir_in);
++ if (gc->reg_dir_out)
++ gc->bgpio_dir = gc->read_reg(gc->reg_dir_out);
++
++ gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
++
++ if (gc->reg_dir_in)
++ gc->write_reg(gc->reg_dir_in, ~gc->bgpio_dir);
++ if (gc->reg_dir_out)
++ gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
++
++ raw_spin_unlock_irqrestore(&gc->bgpio_lock, flags);
++}
++
+ static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
+ int val)
+ {
+@@ -415,6 +500,22 @@ static int bgpio_dir_out_val_first(struc
+ return 0;
+ }
+
++static int bgpio_dir_out_dir_first_direct(struct gpio_chip *gc,
++ unsigned int gpio, int val)
++{
++ bgpio_dir_out_direct(gc, gpio, val);
++ gc->set(gc, gpio, val);
++ return 0;
++}
++
++static int bgpio_dir_out_val_first_direct(struct gpio_chip *gc,
++ unsigned int gpio, int val)
++{
++ gc->set(gc, gpio, val);
++ bgpio_dir_out_direct(gc, gpio, val);
++ return 0;
++}
++
+ static int bgpio_setup_accessors(struct device *dev,
+ struct gpio_chip *gc,
+ bool byte_be)
+@@ -508,6 +609,9 @@ static int bgpio_setup_io(struct gpio_ch
+ } else if (flags & BGPIOF_NO_OUTPUT) {
+ gc->set = bgpio_set_none;
+ gc->set_multiple = NULL;
++ } else if (flags & BGPIOF_REG_DIRECT) {
++ gc->set = bgpio_set_direct;
++ gc->set_multiple = bgpio_set_multiple_direct;
+ } else {
+ gc->set = bgpio_set;
+ gc->set_multiple = bgpio_set_multiple;
+@@ -544,11 +648,21 @@ static int bgpio_setup_direction(struct
+ if (dirout || dirin) {
+ gc->reg_dir_out = dirout;
+ gc->reg_dir_in = dirin;
+- if (flags & BGPIOF_NO_SET_ON_INPUT)
+- gc->direction_output = bgpio_dir_out_dir_first;
+- else
+- gc->direction_output = bgpio_dir_out_val_first;
+- gc->direction_input = bgpio_dir_in;
++ if (flags & BGPIOF_REG_DIRECT) {
++ if (flags & BGPIOF_NO_SET_ON_INPUT)
++ gc->direction_output =
++ bgpio_dir_out_dir_first_direct;
++ else
++ gc->direction_output =
++ bgpio_dir_out_val_first_direct;
++ gc->direction_input = bgpio_dir_in_direct;
++ } else {
++ if (flags & BGPIOF_NO_SET_ON_INPUT)
++ gc->direction_output = bgpio_dir_out_dir_first;
++ else
++ gc->direction_output = bgpio_dir_out_val_first;
++ gc->direction_input = bgpio_dir_in;
++ }
+ gc->get_direction = bgpio_get_dir;
+ } else {
+ if (flags & BGPIOF_NO_OUTPUT)
+--- a/include/linux/gpio/driver.h
++++ b/include/linux/gpio/driver.h
+@@ -690,6 +690,7 @@ int bgpio_init(struct gpio_chip *gc, str
+ #define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */
+ #define BGPIOF_NO_OUTPUT BIT(5) /* only input */
+ #define BGPIOF_NO_SET_ON_INPUT BIT(6)
++#define BGPIOF_REG_DIRECT BIT(7) /* ignore shadow registers */
+
+ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
+ irq_hw_number_t hwirq);
--- /dev/null
+From 22ae3b2ee3293278e647877b269a5aebad3f077d Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 27 May 2021 11:46:30 +0100
+Subject: [PATCH] Allow RESET_BRCMSTB on ARCH_BCM2835
+
+---
+ drivers/reset/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/reset/Kconfig
++++ b/drivers/reset/Kconfig
+@@ -51,7 +51,7 @@ config RESET_BERLIN
+
+ config RESET_BRCMSTB
+ tristate "Broadcom STB reset controller"
+- depends on ARCH_BRCMSTB || COMPILE_TEST
++ depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+ default ARCH_BRCMSTB
+ help
+ This enables the reset controller driver for Broadcom STB SoCs using
--- /dev/null
+From af7e60a33f0b5ce84bffb69ba084ba1edd180195 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 9 Jun 2021 15:48:28 +0100
+Subject: [PATCH] pinctrl: bcm2712 pinctrl/pinconf driver
+
+pinctrl: bcm2712: Reject invalid pulls
+
+Reject attempts to set pulls on aon-sgpios, and fix pull shift
+values.
+
+pinctrl: bcm2712: Add 7712 support, fix 2712 count
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pinctrl-bcm2712: add EMMC pins so pulls can be set
+
+These pins have pad controls but not mux controls. They look enough like
+GPIOs to squeeze in at the end of the list though.
+
+pinctrl: bcm2712: correct BCM2712C0 AON_GPIO pad pull control offset
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+pinctrl: bcm2712: on C0 the regular GPIO pad control register moves too
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+pinctrl: bcm2712: Implement (partially) pinconf_get
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pinctrl: bcm2712: Convert to generic pinconf
+
+Remove the legacy brcm,* pin configuration support and replace it with
+a proper generic pinconf interface, using named functions instead of
+alt function numbers. This is nicer for users, less error-prone, and
+immune to some of the C0->D0 changes.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pinctrl: bcm2712: Remove vestigial pull parameter
+
+Now the legacy brcm, pinconf parameters are no longer supported, this
+custom pin config parameter is not needed.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pinctrl: bcm2712: Guard against bad func numbers
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pinctrl: bcm2712: A better attempt at D0 support
+
+The BCM2712D0 sparse pinctrl maps play havoc with the old GPIO_REGS
+macro, so make the bit positions explicit. And delete the unwanted
+GPIO and pinmux declarations on D0.
+
+Note that a Pi 5 with D0 requires a separate DTS file with "bcm2712d0"
+compatible strings.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pinctrl: bcm2712: Delete base register constants
+
+BCM2712D0 deletes many GPIOs and their associated mux and pad bits,
+so much so that the offsets to the start of the pad control registers
+changes. Remove the constant offsets from the *GPIO_REGS macros,
+compensating by adjusting the per-GPIO values.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/pinctrl/bcm/Kconfig | 9 +
+ drivers/pinctrl/bcm/Makefile | 1 +
+ drivers/pinctrl/bcm/pinctrl-bcm2712.c | 1216 +++++++++++++++++++++++++
+ 3 files changed, 1226 insertions(+)
+ create mode 100644 drivers/pinctrl/bcm/pinctrl-bcm2712.c
+
+--- a/drivers/pinctrl/bcm/Kconfig
++++ b/drivers/pinctrl/bcm/Kconfig
+@@ -3,6 +3,15 @@
+ # Broadcom pinctrl drivers
+ #
+
++config PINCTRL_BCM2712
++ bool "Broadcom BCM2712 PINCONF driver"
++ depends on OF && (ARCH_BCM2835 || ARCH_BRCMSTB || COMPILE_TEST)
++ select PINMUX
++ select PINCONF
++ select GENERIC_PINCONF
++ help
++ Say Y here to enable the Broadcom BCM2835 GPIO driver.
++
+ config PINCTRL_BCM281XX
+ bool "Broadcom BCM281xx pinctrl driver"
+ depends on OF && (ARCH_BCM_MOBILE || COMPILE_TEST)
+--- a/drivers/pinctrl/bcm/Makefile
++++ b/drivers/pinctrl/bcm/Makefile
+@@ -1,6 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ # Broadcom pinctrl support
+
++obj-$(CONFIG_PINCTRL_BCM2712) += pinctrl-bcm2712.o
+ obj-$(CONFIG_PINCTRL_BCM281XX) += pinctrl-bcm281xx.o
+ obj-$(CONFIG_PINCTRL_BCM2835) += pinctrl-bcm2835.o
+ obj-$(CONFIG_PINCTRL_BCM4908) += pinctrl-bcm4908.o
+--- /dev/null
++++ b/drivers/pinctrl/bcm/pinctrl-bcm2712.c
+@@ -0,0 +1,1216 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Driver for Broadcom BCM2712 GPIO units (pinctrl only)
++ *
++ * Copyright (C) 2021-3 Raspberry Pi Ltd.
++ * Copyright (C) 2012 Chris Boot, Simon Arlott, Stephen Warren
++ *
++ * Based heavily on the BCM2835 GPIO & pinctrl driver, which was inspired by:
++ * pinctrl-nomadik.c, please see original file for copyright information
++ * pinctrl-tegra.c, please see original file for copyright information
++ */
++
++#include <linux/bitmap.h>
++#include <linux/bug.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/of_address.h>
++#include <linux/of.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/pinctrl/machine.h>
++#include <linux/pinctrl/pinconf.h>
++#include <linux/pinctrl/pinctrl.h>
++#include <linux/pinctrl/pinmux.h>
++#include <linux/pinctrl/pinconf-generic.h>
++#include <linux/platform_device.h>
++#include <linux/seq_file.h>
++#include <linux/slab.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++
++#define MODULE_NAME "pinctrl-bcm2712"
++
++/* Register offsets */
++
++#define BCM2712_PULL_NONE 0
++#define BCM2712_PULL_DOWN 1
++#define BCM2712_PULL_UP 2
++#define BCM2712_PULL_MASK 0x3
++
++#define BCM2712_FSEL_COUNT 9
++#define BCM2712_FSEL_MASK 0xf
++
++#define FUNC(f) \
++ [func_##f] = #f
++#define PIN(i, f1, f2, f3, f4, f5, f6, f7, f8) \
++ [i] = { \
++ .funcs = { \
++ func_##f1, \
++ func_##f2, \
++ func_##f3, \
++ func_##f4, \
++ func_##f5, \
++ func_##f6, \
++ func_##f7, \
++ func_##f8, \
++ }, \
++ }
++
++#define REG_BIT_INVALID 0xffff
++
++#define BIT_TO_REG(b) (((b) >> 5) << 2)
++#define BIT_TO_SHIFT(b) ((b) & 0x1f)
++
++#define GPIO_REGS(n, mr, mb, pr, pb) \
++ [n] = { ((mr)*4)*8 + (mb)*4, ((pr)*4)*8 + (pb)*2 }
++
++#define EMMC_REGS(n, r, b) \
++ [n] = { 0, ((r)*4)*8 + (b)*2 }
++
++#define AGPIO_REGS(n, mr, mb, pr, pb) \
++ [n] = { ((mr)*4)*8 + (mb)*4, ((pr)*4)*8 + (pb)*2 }
++
++#define SGPIO_REGS(n, mr, mb) \
++ [n+32] = { ((mr)*4)*8 + (mb)*4, REG_BIT_INVALID }
++
++#define GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
++#define AGPIO_PIN(a) PINCTRL_PIN(a, "aon_gpio" #a)
++#define SGPIO_PIN(a) PINCTRL_PIN(a+32, "aon_sgpio" #a)
++
++struct pin_regs {
++ u16 mux_bit;
++ u16 pad_bit;
++};
++
++struct bcm2712_pinctrl {
++ struct device *dev;
++ void __iomem *base;
++ struct pinctrl_dev *pctl_dev;
++ struct pinctrl_desc pctl_desc;
++ const struct pin_regs *pin_regs;
++ const struct bcm2712_pin_funcs *pin_funcs;
++ const char *const *gpio_groups;
++ struct pinctrl_gpio_range gpio_range;
++ spinlock_t lock;
++};
++
++struct bcm_plat_data {
++ const struct pinctrl_desc *pctl_desc;
++ const struct pinctrl_gpio_range *gpio_range;
++ const struct pin_regs *pin_regs;
++ const struct bcm2712_pin_funcs *pin_funcs;
++};
++
++struct bcm2712_pin_funcs {
++ u8 funcs[BCM2712_FSEL_COUNT - 1];
++};
++
++enum bcm2712_funcs {
++ func_gpio,
++ func_alt1,
++ func_alt2,
++ func_alt3,
++ func_alt4,
++ func_alt5,
++ func_alt6,
++ func_alt7,
++ func_alt8,
++ func_aon_cpu_standbyb,
++ func_aon_fp_4sec_resetb,
++ func_aon_gpclk,
++ func_aon_pwm,
++ func_arm_jtag,
++ func_aud_fs_clk0,
++ func_avs_pmu_bsc,
++ func_bsc_m0,
++ func_bsc_m1,
++ func_bsc_m2,
++ func_bsc_m3,
++ func_clk_observe,
++ func_ctl_hdmi_5v,
++ func_enet0,
++ func_enet0_mii,
++ func_enet0_rgmii,
++ func_ext_sc_clk,
++ func_fl0,
++ func_fl1,
++ func_gpclk0,
++ func_gpclk1,
++ func_gpclk2,
++ func_hdmi_tx0_auto_i2c,
++ func_hdmi_tx0_bsc,
++ func_hdmi_tx1_auto_i2c,
++ func_hdmi_tx1_bsc,
++ func_i2s_in,
++ func_i2s_out,
++ func_ir_in,
++ func_mtsif,
++ func_mtsif_alt,
++ func_mtsif_alt1,
++ func_pdm,
++ func_pkt,
++ func_pm_led_out,
++ func_sc0,
++ func_sd0,
++ func_sd2,
++ func_sd_card_a,
++ func_sd_card_b,
++ func_sd_card_c,
++ func_sd_card_d,
++ func_sd_card_e,
++ func_sd_card_f,
++ func_sd_card_g,
++ func_spdif_out,
++ func_spi_m,
++ func_spi_s,
++ func_sr_edm_sense,
++ func_te0,
++ func_te1,
++ func_tsio,
++ func_uart0,
++ func_uart1,
++ func_uart2,
++ func_usb_pwr,
++ func_usb_vbus,
++ func_uui,
++ func_vc_i2c0,
++ func_vc_i2c3,
++ func_vc_i2c4,
++ func_vc_i2c5,
++ func_vc_i2csl,
++ func_vc_pcm,
++ func_vc_pwm0,
++ func_vc_pwm1,
++ func_vc_spi0,
++ func_vc_spi3,
++ func_vc_spi4,
++ func_vc_spi5,
++ func_vc_uart0,
++ func_vc_uart2,
++ func_vc_uart3,
++ func_vc_uart4,
++ func__,
++ func_count = func__
++};
++
++static const struct pin_regs bcm2712_c0_gpio_pin_regs[] = {
++ GPIO_REGS(0, 0, 0, 7, 7),
++ GPIO_REGS(1, 0, 1, 7, 8),
++ GPIO_REGS(2, 0, 2, 7, 9),
++ GPIO_REGS(3, 0, 3, 7, 10),
++ GPIO_REGS(4, 0, 4, 7, 11),
++ GPIO_REGS(5, 0, 5, 7, 12),
++ GPIO_REGS(6, 0, 6, 7, 13),
++ GPIO_REGS(7, 0, 7, 7, 14),
++ GPIO_REGS(8, 1, 0, 8, 0),
++ GPIO_REGS(9, 1, 1, 8, 1),
++ GPIO_REGS(10, 1, 2, 8, 2),
++ GPIO_REGS(11, 1, 3, 8, 3),
++ GPIO_REGS(12, 1, 4, 8, 4),
++ GPIO_REGS(13, 1, 5, 8, 5),
++ GPIO_REGS(14, 1, 6, 8, 6),
++ GPIO_REGS(15, 1, 7, 8, 7),
++ GPIO_REGS(16, 2, 0, 8, 8),
++ GPIO_REGS(17, 2, 1, 8, 9),
++ GPIO_REGS(18, 2, 2, 8, 10),
++ GPIO_REGS(19, 2, 3, 8, 11),
++ GPIO_REGS(20, 2, 4, 8, 12),
++ GPIO_REGS(21, 2, 5, 8, 13),
++ GPIO_REGS(22, 2, 6, 8, 14),
++ GPIO_REGS(23, 2, 7, 9, 0),
++ GPIO_REGS(24, 3, 0, 9, 1),
++ GPIO_REGS(25, 3, 1, 9, 2),
++ GPIO_REGS(26, 3, 2, 9, 3),
++ GPIO_REGS(27, 3, 3, 9, 4),
++ GPIO_REGS(28, 3, 4, 9, 5),
++ GPIO_REGS(29, 3, 5, 9, 6),
++ GPIO_REGS(30, 3, 6, 9, 7),
++ GPIO_REGS(31, 3, 7, 9, 8),
++ GPIO_REGS(32, 4, 0, 9, 9),
++ GPIO_REGS(33, 4, 1, 9, 10),
++ GPIO_REGS(34, 4, 2, 9, 11),
++ GPIO_REGS(35, 4, 3, 9, 12),
++ GPIO_REGS(36, 4, 4, 9, 13),
++ GPIO_REGS(37, 4, 5, 9, 14),
++ GPIO_REGS(38, 4, 6, 10, 0),
++ GPIO_REGS(39, 4, 7, 10, 1),
++ GPIO_REGS(40, 5, 0, 10, 2),
++ GPIO_REGS(41, 5, 1, 10, 3),
++ GPIO_REGS(42, 5, 2, 10, 4),
++ GPIO_REGS(43, 5, 3, 10, 5),
++ GPIO_REGS(44, 5, 4, 10, 6),
++ GPIO_REGS(45, 5, 5, 10, 7),
++ GPIO_REGS(46, 5, 6, 10, 8),
++ GPIO_REGS(47, 5, 7, 10, 9),
++ GPIO_REGS(48, 6, 0, 10, 10),
++ GPIO_REGS(49, 6, 1, 10, 11),
++ GPIO_REGS(50, 6, 2, 10, 12),
++ GPIO_REGS(51, 6, 3, 10, 13),
++ GPIO_REGS(52, 6, 4, 10, 14),
++ GPIO_REGS(53, 6, 5, 11, 0),
++ EMMC_REGS(54, 11, 1), /* EMMC_CMD */
++ EMMC_REGS(55, 11, 2), /* EMMC_DS */
++ EMMC_REGS(56, 11, 3), /* EMMC_CLK */
++ EMMC_REGS(57, 11, 4), /* EMMC_DAT0 */
++ EMMC_REGS(58, 11, 5), /* EMMC_DAT1 */
++ EMMC_REGS(59, 11, 6), /* EMMC_DAT2 */
++ EMMC_REGS(60, 11, 7), /* EMMC_DAT3 */
++ EMMC_REGS(61, 11, 8), /* EMMC_DAT4 */
++ EMMC_REGS(62, 11, 9), /* EMMC_DAT5 */
++ EMMC_REGS(63, 11, 10), /* EMMC_DAT6 */
++ EMMC_REGS(64, 11, 11), /* EMMC_DAT7 */
++};
++
++static struct pin_regs bcm2712_c0_aon_gpio_pin_regs[] = {
++ AGPIO_REGS(0, 3, 0, 6, 10),
++ AGPIO_REGS(1, 3, 1, 6, 11),
++ AGPIO_REGS(2, 3, 2, 6, 12),
++ AGPIO_REGS(3, 3, 3, 6, 13),
++ AGPIO_REGS(4, 3, 4, 6, 14),
++ AGPIO_REGS(5, 3, 5, 7, 0),
++ AGPIO_REGS(6, 3, 6, 7, 1),
++ AGPIO_REGS(7, 3, 7, 7, 2),
++ AGPIO_REGS(8, 4, 0, 7, 3),
++ AGPIO_REGS(9, 4, 1, 7, 4),
++ AGPIO_REGS(10, 4, 2, 7, 5),
++ AGPIO_REGS(11, 4, 3, 7, 6),
++ AGPIO_REGS(12, 4, 4, 7, 7),
++ AGPIO_REGS(13, 4, 5, 7, 8),
++ AGPIO_REGS(14, 4, 6, 7, 9),
++ AGPIO_REGS(15, 4, 7, 7, 10),
++ AGPIO_REGS(16, 5, 0, 7, 11),
++ SGPIO_REGS(0, 0, 0),
++ SGPIO_REGS(1, 0, 1),
++ SGPIO_REGS(2, 0, 2),
++ SGPIO_REGS(3, 0, 3),
++ SGPIO_REGS(4, 1, 0),
++ SGPIO_REGS(5, 2, 0),
++};
++
++static const struct pinctrl_pin_desc bcm2712_c0_gpio_pins[] = {
++ GPIO_PIN(0),
++ GPIO_PIN(1),
++ GPIO_PIN(2),
++ GPIO_PIN(3),
++ GPIO_PIN(4),
++ GPIO_PIN(5),
++ GPIO_PIN(6),
++ GPIO_PIN(7),
++ GPIO_PIN(8),
++ GPIO_PIN(9),
++ GPIO_PIN(10),
++ GPIO_PIN(11),
++ GPIO_PIN(12),
++ GPIO_PIN(13),
++ GPIO_PIN(14),
++ GPIO_PIN(15),
++ GPIO_PIN(16),
++ GPIO_PIN(17),
++ GPIO_PIN(18),
++ GPIO_PIN(19),
++ GPIO_PIN(20),
++ GPIO_PIN(21),
++ GPIO_PIN(22),
++ GPIO_PIN(23),
++ GPIO_PIN(24),
++ GPIO_PIN(25),
++ GPIO_PIN(26),
++ GPIO_PIN(27),
++ GPIO_PIN(28),
++ GPIO_PIN(29),
++ GPIO_PIN(30),
++ GPIO_PIN(31),
++ GPIO_PIN(32),
++ GPIO_PIN(33),
++ GPIO_PIN(34),
++ GPIO_PIN(35),
++ GPIO_PIN(36),
++ GPIO_PIN(37),
++ GPIO_PIN(38),
++ GPIO_PIN(39),
++ GPIO_PIN(40),
++ GPIO_PIN(41),
++ GPIO_PIN(42),
++ GPIO_PIN(43),
++ GPIO_PIN(44),
++ GPIO_PIN(45),
++ GPIO_PIN(46),
++ GPIO_PIN(47),
++ GPIO_PIN(48),
++ GPIO_PIN(49),
++ GPIO_PIN(50),
++ GPIO_PIN(51),
++ GPIO_PIN(52),
++ GPIO_PIN(53),
++ PINCTRL_PIN(54, "emmc_cmd"),
++ PINCTRL_PIN(55, "emmc_ds"),
++ PINCTRL_PIN(56, "emmc_clk"),
++ PINCTRL_PIN(57, "emmc_dat0"),
++ PINCTRL_PIN(58, "emmc_dat1"),
++ PINCTRL_PIN(59, "emmc_dat2"),
++ PINCTRL_PIN(60, "emmc_dat3"),
++ PINCTRL_PIN(61, "emmc_dat4"),
++ PINCTRL_PIN(62, "emmc_dat5"),
++ PINCTRL_PIN(63, "emmc_dat6"),
++ PINCTRL_PIN(64, "emmc_dat7"),
++};
++
++static struct pinctrl_pin_desc bcm2712_c0_aon_gpio_pins[] = {
++ AGPIO_PIN(0),
++ AGPIO_PIN(1),
++ AGPIO_PIN(2),
++ AGPIO_PIN(3),
++ AGPIO_PIN(4),
++ AGPIO_PIN(5),
++ AGPIO_PIN(6),
++ AGPIO_PIN(7),
++ AGPIO_PIN(8),
++ AGPIO_PIN(9),
++ AGPIO_PIN(10),
++ AGPIO_PIN(11),
++ AGPIO_PIN(12),
++ AGPIO_PIN(13),
++ AGPIO_PIN(14),
++ AGPIO_PIN(15),
++ AGPIO_PIN(16),
++ SGPIO_PIN(0),
++ SGPIO_PIN(1),
++ SGPIO_PIN(2),
++ SGPIO_PIN(3),
++ SGPIO_PIN(4),
++ SGPIO_PIN(5),
++};
++
++static const struct pin_regs bcm2712_d0_gpio_pin_regs[] = {
++ GPIO_REGS(1, 0, 0, 4, 5),
++ GPIO_REGS(2, 0, 1, 4, 6),
++ GPIO_REGS(3, 0, 2, 4, 7),
++ GPIO_REGS(4, 0, 3, 4, 8),
++ GPIO_REGS(10, 0, 4, 4, 9),
++ GPIO_REGS(11, 0, 5, 4, 10),
++ GPIO_REGS(12, 0, 6, 4, 11),
++ GPIO_REGS(13, 0, 7, 4, 12),
++ GPIO_REGS(14, 1, 0, 4, 13),
++ GPIO_REGS(15, 1, 1, 4, 14),
++ GPIO_REGS(18, 1, 2, 5, 0),
++ GPIO_REGS(19, 1, 3, 5, 1),
++ GPIO_REGS(20, 1, 4, 5, 2),
++ GPIO_REGS(21, 1, 5, 5, 3),
++ GPIO_REGS(22, 1, 6, 5, 4),
++ GPIO_REGS(23, 1, 7, 5, 5),
++ GPIO_REGS(24, 2, 0, 5, 6),
++ GPIO_REGS(25, 2, 1, 5, 7),
++ GPIO_REGS(26, 2, 2, 5, 8),
++ GPIO_REGS(27, 2, 3, 5, 9),
++ GPIO_REGS(28, 2, 4, 5, 10),
++ GPIO_REGS(29, 2, 5, 5, 11),
++ GPIO_REGS(30, 2, 6, 5, 12),
++ GPIO_REGS(31, 2, 7, 5, 13),
++ GPIO_REGS(32, 3, 0, 5, 14),
++ GPIO_REGS(33, 3, 1, 6, 0),
++ GPIO_REGS(34, 3, 2, 6, 1),
++ GPIO_REGS(35, 3, 3, 6, 2),
++};
++
++static struct pin_regs bcm2712_d0_aon_gpio_pin_regs[] = {
++ AGPIO_REGS(0, 3, 0, 5, 9),
++ AGPIO_REGS(1, 3, 1, 5, 10),
++ AGPIO_REGS(2, 3, 2, 5, 11),
++ AGPIO_REGS(3, 3, 3, 5, 12),
++ AGPIO_REGS(4, 3, 4, 5, 13),
++ AGPIO_REGS(5, 3, 5, 5, 14),
++ AGPIO_REGS(6, 3, 6, 6, 0),
++ AGPIO_REGS(8, 3, 7, 6, 1),
++ AGPIO_REGS(9, 4, 0, 6, 2),
++ AGPIO_REGS(12, 4, 1, 6, 3),
++ AGPIO_REGS(13, 4, 2, 6, 4),
++ AGPIO_REGS(14, 4, 3, 6, 5),
++ SGPIO_REGS(0, 0, 0),
++ SGPIO_REGS(1, 0, 1),
++ SGPIO_REGS(2, 0, 2),
++ SGPIO_REGS(3, 0, 3),
++ SGPIO_REGS(4, 1, 0),
++ SGPIO_REGS(5, 2, 0),
++};
++
++static const struct pinctrl_pin_desc bcm2712_d0_gpio_pins[] = {
++ GPIO_PIN(1),
++ GPIO_PIN(2),
++ GPIO_PIN(3),
++ GPIO_PIN(4),
++ GPIO_PIN(10),
++ GPIO_PIN(11),
++ GPIO_PIN(12),
++ GPIO_PIN(13),
++ GPIO_PIN(14),
++ GPIO_PIN(15),
++ GPIO_PIN(18),
++ GPIO_PIN(19),
++ GPIO_PIN(20),
++ GPIO_PIN(21),
++ GPIO_PIN(22),
++ GPIO_PIN(23),
++ GPIO_PIN(24),
++ GPIO_PIN(25),
++ GPIO_PIN(26),
++ GPIO_PIN(27),
++ GPIO_PIN(28),
++ GPIO_PIN(29),
++ GPIO_PIN(30),
++ GPIO_PIN(31),
++ GPIO_PIN(32),
++ GPIO_PIN(33),
++ GPIO_PIN(34),
++ GPIO_PIN(35),
++};
++
++static struct pinctrl_pin_desc bcm2712_d0_aon_gpio_pins[] = {
++ AGPIO_PIN(0),
++ AGPIO_PIN(1),
++ AGPIO_PIN(2),
++ AGPIO_PIN(3),
++ AGPIO_PIN(4),
++ AGPIO_PIN(5),
++ AGPIO_PIN(6),
++ AGPIO_PIN(8),
++ AGPIO_PIN(9),
++ AGPIO_PIN(12),
++ AGPIO_PIN(13),
++ AGPIO_PIN(14),
++ SGPIO_PIN(0),
++ SGPIO_PIN(1),
++ SGPIO_PIN(2),
++ SGPIO_PIN(3),
++ SGPIO_PIN(4),
++ SGPIO_PIN(5),
++};
++
++static const char * const bcm2712_func_names[] = {
++ FUNC(gpio),
++ FUNC(alt1),
++ FUNC(alt2),
++ FUNC(alt3),
++ FUNC(alt4),
++ FUNC(alt5),
++ FUNC(alt6),
++ FUNC(alt7),
++ FUNC(alt8),
++ FUNC(aon_cpu_standbyb),
++ FUNC(aon_fp_4sec_resetb),
++ FUNC(aon_gpclk),
++ FUNC(aon_pwm),
++ FUNC(arm_jtag),
++ FUNC(aud_fs_clk0),
++ FUNC(avs_pmu_bsc),
++ FUNC(bsc_m0),
++ FUNC(bsc_m1),
++ FUNC(bsc_m2),
++ FUNC(bsc_m3),
++ FUNC(clk_observe),
++ FUNC(ctl_hdmi_5v),
++ FUNC(enet0),
++ FUNC(enet0_mii),
++ FUNC(enet0_rgmii),
++ FUNC(ext_sc_clk),
++ FUNC(fl0),
++ FUNC(fl1),
++ FUNC(gpclk0),
++ FUNC(gpclk1),
++ FUNC(gpclk2),
++ FUNC(hdmi_tx0_auto_i2c),
++ FUNC(hdmi_tx0_bsc),
++ FUNC(hdmi_tx1_auto_i2c),
++ FUNC(hdmi_tx1_bsc),
++ FUNC(i2s_in),
++ FUNC(i2s_out),
++ FUNC(ir_in),
++ FUNC(mtsif),
++ FUNC(mtsif_alt),
++ FUNC(mtsif_alt1),
++ FUNC(pdm),
++ FUNC(pkt),
++ FUNC(pm_led_out),
++ FUNC(sc0),
++ FUNC(sd0),
++ FUNC(sd2),
++ FUNC(sd_card_a),
++ FUNC(sd_card_b),
++ FUNC(sd_card_c),
++ FUNC(sd_card_d),
++ FUNC(sd_card_e),
++ FUNC(sd_card_f),
++ FUNC(sd_card_g),
++ FUNC(spdif_out),
++ FUNC(spi_m),
++ FUNC(spi_s),
++ FUNC(sr_edm_sense),
++ FUNC(te0),
++ FUNC(te1),
++ FUNC(tsio),
++ FUNC(uart0),
++ FUNC(uart1),
++ FUNC(uart2),
++ FUNC(usb_pwr),
++ FUNC(usb_vbus),
++ FUNC(uui),
++ FUNC(vc_i2c0),
++ FUNC(vc_i2c3),
++ FUNC(vc_i2c4),
++ FUNC(vc_i2c5),
++ FUNC(vc_i2csl),
++ FUNC(vc_pcm),
++ FUNC(vc_pwm0),
++ FUNC(vc_pwm1),
++ FUNC(vc_spi0),
++ FUNC(vc_spi3),
++ FUNC(vc_spi4),
++ FUNC(vc_spi5),
++ FUNC(vc_uart0),
++ FUNC(vc_uart2),
++ FUNC(vc_uart3),
++ FUNC(vc_uart4),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_c0_aon_gpio_pin_funcs[] = {
++ PIN(0, ir_in, vc_spi0, vc_uart3, vc_i2c3, te0, vc_i2c0, _, _),
++ PIN(1, vc_pwm0, vc_spi0, vc_uart3, vc_i2c3, te1, aon_pwm, vc_i2c0, vc_pwm1),
++ PIN(2, vc_pwm0, vc_spi0, vc_uart3, ctl_hdmi_5v, fl0, aon_pwm, ir_in, vc_pwm1),
++ PIN(3, ir_in, vc_spi0, vc_uart3, aon_fp_4sec_resetb, fl1, sd_card_g, aon_gpclk, _),
++ PIN(4, gpclk0, vc_spi0, vc_i2csl, aon_gpclk, pm_led_out, aon_pwm, sd_card_g, vc_pwm0),
++ PIN(5, gpclk1, ir_in, vc_i2csl, clk_observe, aon_pwm, sd_card_g, vc_pwm0, _),
++ PIN(6, uart1, vc_uart4, gpclk2, ctl_hdmi_5v, vc_uart0, vc_spi3, _, _),
++ PIN(7, uart1, vc_uart4, gpclk0, aon_pwm, vc_uart0, vc_spi3, _, _),
++ PIN(8, uart1, vc_uart4, vc_i2csl, ctl_hdmi_5v, vc_uart0, vc_spi3, _, _),
++ PIN(9, uart1, vc_uart4, vc_i2csl, aon_pwm, vc_uart0, vc_spi3, _, _),
++ PIN(10, tsio, ctl_hdmi_5v, sc0, spdif_out, vc_spi5, usb_pwr, aon_gpclk, sd_card_f),
++ PIN(11, tsio, uart0, sc0, aud_fs_clk0, vc_spi5, usb_vbus, vc_uart2, sd_card_f),
++ PIN(12, tsio, uart0, vc_uart0, tsio, vc_spi5, usb_pwr, vc_uart2, sd_card_f),
++ PIN(13, bsc_m1, uart0, vc_uart0, uui, vc_spi5, arm_jtag, vc_uart2, vc_i2c3),
++ PIN(14, bsc_m1, uart0, vc_uart0, uui, vc_spi5, arm_jtag, vc_uart2, vc_i2c3),
++ PIN(15, ir_in, aon_fp_4sec_resetb, vc_uart0, pm_led_out, ctl_hdmi_5v, aon_pwm, aon_gpclk, _),
++ PIN(16, aon_cpu_standbyb, gpclk0, pm_led_out, ctl_hdmi_5v, vc_pwm0, usb_pwr, aud_fs_clk0, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_c0_aon_sgpio_pin_funcs[] = {
++ PIN(0, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++ PIN(1, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++ PIN(2, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c4, ctl_hdmi_5v, _, _, _),
++ PIN(3, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c4, _, _, _, _),
++ PIN(4, avs_pmu_bsc, bsc_m2, vc_i2c5, ctl_hdmi_5v, _, _, _, _),
++ PIN(5, avs_pmu_bsc, bsc_m2, vc_i2c5, _, _, _, _, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_c0_gpio_pin_funcs[] = {
++ PIN(0, bsc_m3, vc_i2c0, gpclk0, enet0, vc_pwm1, vc_spi0, ir_in, _),
++ PIN(1, bsc_m3, vc_i2c0, gpclk1, enet0, vc_pwm1, sr_edm_sense, vc_spi0, vc_uart3),
++ PIN(2, pdm, i2s_in, gpclk2, vc_spi4, pkt, vc_spi0, vc_uart3, _),
++ PIN(3, pdm, i2s_in, vc_spi4, pkt, vc_spi0, vc_uart3, _, _),
++ PIN(4, pdm, i2s_in, arm_jtag, vc_spi4, pkt, vc_spi0, vc_uart3, _),
++ PIN(5, pdm, vc_i2c3, arm_jtag, sd_card_e, vc_spi4, pkt, vc_pcm, vc_i2c5),
++ PIN(6, pdm, vc_i2c3, arm_jtag, sd_card_e, vc_spi4, pkt, vc_pcm, vc_i2c5),
++ PIN(7, i2s_out, spdif_out, arm_jtag, sd_card_e, vc_i2c3, enet0_rgmii, vc_pcm, vc_spi4),
++ PIN(8, i2s_out, aud_fs_clk0, arm_jtag, sd_card_e, vc_i2c3, enet0_mii, vc_pcm, vc_spi4),
++ PIN(9, i2s_out, aud_fs_clk0, arm_jtag, sd_card_e, enet0_mii, sd_card_c, vc_spi4, _),
++ PIN(10, bsc_m3, mtsif_alt1, i2s_in, i2s_out, vc_spi5, enet0_mii, sd_card_c, vc_spi4),
++ PIN(11, bsc_m3, mtsif_alt1, i2s_in, i2s_out, vc_spi5, enet0_mii, sd_card_c, vc_spi4),
++ PIN(12, spi_s, mtsif_alt1, i2s_in, i2s_out, vc_spi5, vc_i2csl, sd0, sd_card_d),
++ PIN(13, spi_s, mtsif_alt1, i2s_out, usb_vbus, vc_spi5, vc_i2csl, sd0, sd_card_d),
++ PIN(14, spi_s, vc_i2csl, enet0_rgmii, arm_jtag, vc_spi5, vc_pwm0, vc_i2c4, sd_card_d),
++ PIN(15, spi_s, vc_i2csl, vc_spi3, arm_jtag, vc_pwm0, vc_i2c4, gpclk0, _),
++ PIN(16, sd_card_b, i2s_out, vc_spi3, i2s_in, sd0, enet0_rgmii, gpclk1, _),
++ PIN(17, sd_card_b, i2s_out, vc_spi3, i2s_in, ext_sc_clk, sd0, enet0_rgmii, gpclk2),
++ PIN(18, sd_card_b, i2s_out, vc_spi3, i2s_in, sd0, enet0_rgmii, vc_pwm1, _),
++ PIN(19, sd_card_b, usb_pwr, vc_spi3, pkt, spdif_out, sd0, ir_in, vc_pwm1),
++ PIN(20, sd_card_b, uui, vc_uart0, arm_jtag, uart2, usb_pwr, vc_pcm, vc_uart4),
++ PIN(21, usb_pwr, uui, vc_uart0, arm_jtag, uart2, sd_card_b, vc_pcm, vc_uart4),
++ PIN(22, usb_pwr, enet0, vc_uart0, mtsif, uart2, usb_vbus, vc_pcm, vc_i2c5),
++ PIN(23, usb_vbus, enet0, vc_uart0, mtsif, uart2, i2s_out, vc_pcm, vc_i2c5),
++ PIN(24, mtsif, pkt, uart0, enet0_rgmii, enet0_rgmii, vc_i2c4, vc_uart3, _),
++ PIN(25, mtsif, pkt, sc0, uart0, enet0_rgmii, enet0_rgmii, vc_i2c4, vc_uart3),
++ PIN(26, mtsif, pkt, sc0, uart0, enet0_rgmii, vc_uart4, vc_spi5, _),
++ PIN(27, mtsif, pkt, sc0, uart0, enet0_rgmii, vc_uart4, vc_spi5, _),
++ PIN(28, mtsif, pkt, sc0, enet0_rgmii, vc_uart4, vc_spi5, _, _),
++ PIN(29, mtsif, pkt, sc0, enet0_rgmii, vc_uart4, vc_spi5, _, _),
++ PIN(30, mtsif, pkt, sc0, sd2, enet0_rgmii, gpclk0, vc_pwm0, _),
++ PIN(31, mtsif, pkt, sc0, sd2, enet0_rgmii, vc_spi3, vc_pwm0, _),
++ PIN(32, mtsif, pkt, sc0, sd2, enet0_rgmii, vc_spi3, vc_uart3, _),
++ PIN(33, mtsif, pkt, sd2, enet0_rgmii, vc_spi3, vc_uart3, _, _),
++ PIN(34, mtsif, pkt, ext_sc_clk, sd2, enet0_rgmii, vc_spi3, vc_i2c5, _),
++ PIN(35, mtsif, pkt, sd2, enet0_rgmii, vc_spi3, vc_i2c5, _, _),
++ PIN(36, sd0, mtsif, sc0, i2s_in, vc_uart3, vc_uart2, _, _),
++ PIN(37, sd0, mtsif, sc0, vc_spi0, i2s_in, vc_uart3, vc_uart2, _),
++ PIN(38, sd0, mtsif_alt, sc0, vc_spi0, i2s_in, vc_uart3, vc_uart2, _),
++ PIN(39, sd0, mtsif_alt, sc0, vc_spi0, vc_uart3, vc_uart2, _, _),
++ PIN(40, sd0, mtsif_alt, sc0, vc_spi0, bsc_m3, _, _, _),
++ PIN(41, sd0, mtsif_alt, sc0, vc_spi0, bsc_m3, _, _, _),
++ PIN(42, vc_spi0, mtsif_alt, vc_i2c0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++ PIN(43, vc_spi0, mtsif_alt, vc_i2c0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++ PIN(44, vc_spi0, mtsif_alt, enet0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++ PIN(45, vc_spi0, mtsif_alt, enet0, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m),
++ PIN(46, vc_spi0, mtsif_alt, sd_card_a, mtsif_alt1, arm_jtag, pdm, spi_m, _),
++ PIN(47, enet0, mtsif_alt, i2s_out, mtsif_alt1, arm_jtag, _, _, _),
++ PIN(48, sc0, usb_pwr, spdif_out, mtsif, _, _, _, _),
++ PIN(49, sc0, usb_pwr, aud_fs_clk0, mtsif, _, _, _, _),
++ PIN(50, sc0, usb_vbus, sc0, _, _, _, _, _),
++ PIN(51, sc0, enet0, sc0, sr_edm_sense, _, _, _, _),
++ PIN(52, sc0, enet0, vc_pwm1, _, _, _, _, _),
++ PIN(53, sc0, enet0_rgmii, ext_sc_clk, _, _, _, _, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_d0_aon_gpio_pin_funcs[] = {
++ PIN(0, ir_in, vc_spi0, vc_uart0, vc_i2c3, uart0, vc_i2c0, _, _),
++ PIN(1, vc_pwm0, vc_spi0, vc_uart0, vc_i2c3, uart0, aon_pwm, vc_i2c0, vc_pwm1),
++ PIN(2, vc_pwm0, vc_spi0, vc_uart0, ctl_hdmi_5v, uart0, aon_pwm, ir_in, vc_pwm1),
++ PIN(3, ir_in, vc_spi0, vc_uart0, uart0, sd_card_g, aon_gpclk, _, _),
++ PIN(4, gpclk0, vc_spi0, pm_led_out, aon_pwm, sd_card_g, vc_pwm0, _, _),
++ PIN(5, gpclk1, ir_in, aon_pwm, sd_card_g, vc_pwm0, _, _, _),
++ PIN(6, uart1, vc_uart2, ctl_hdmi_5v, gpclk2, vc_spi3, _, _, _),
++ PIN(7, _, _, _, _, _, _, _, _),
++ PIN(8, uart1, vc_uart2, ctl_hdmi_5v, vc_spi0, vc_spi3, _, _, _),
++ PIN(9, uart1, vc_uart2, vc_uart0, aon_pwm, vc_spi0, vc_uart2, vc_spi3, _),
++ PIN(10, _, _, _, _, _, _, _, _),
++ PIN(11, _, _, _, _, _, _, _, _),
++ PIN(12, uart1, vc_uart2, vc_uart0, vc_spi0, usb_pwr, vc_uart2, vc_spi3, _),
++ PIN(13, bsc_m1, vc_uart0, uui, vc_spi0, arm_jtag, vc_uart2, vc_i2c3, _),
++ PIN(14, bsc_m1, aon_gpclk, vc_uart0, uui, vc_spi0, arm_jtag, vc_uart2, vc_i2c3),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_d0_aon_sgpio_pin_funcs[] = {
++ PIN(0, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++ PIN(1, hdmi_tx0_bsc, hdmi_tx0_auto_i2c, bsc_m0, vc_i2c0, _, _, _, _),
++ PIN(2, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c0, ctl_hdmi_5v, _, _, _),
++ PIN(3, hdmi_tx1_bsc, hdmi_tx1_auto_i2c, bsc_m1, vc_i2c0, _, _, _, _),
++ PIN(4, avs_pmu_bsc, bsc_m2, vc_i2c3, ctl_hdmi_5v, _, _, _, _),
++ PIN(5, avs_pmu_bsc, bsc_m2, vc_i2c3, _, _, _, _, _),
++};
++
++static const struct bcm2712_pin_funcs bcm2712_d0_gpio_pin_funcs[] = {
++ PIN(1, vc_i2c0, usb_pwr, gpclk0, sd_card_e, vc_spi3, sr_edm_sense, vc_spi0, vc_uart0),
++ PIN(2, vc_i2c0, usb_pwr, gpclk1, sd_card_e, vc_spi3, clk_observe, vc_spi0, vc_uart0),
++ PIN(3, vc_i2c3, usb_vbus, gpclk2, sd_card_e, vc_spi3, vc_spi0, vc_uart0, _),
++ PIN(4, vc_i2c3, vc_pwm1, vc_spi3, sd_card_e, vc_spi3, vc_spi0, vc_uart0, _),
++ PIN(10, bsc_m3, vc_pwm1, vc_spi3, sd_card_e, vc_spi3, gpclk0, _, _),
++ PIN(11, bsc_m3, vc_spi3, clk_observe, sd_card_c, gpclk1, _, _, _),
++ PIN(12, spi_s, vc_spi3, sd_card_c, sd_card_d, _, _, _, _),
++ PIN(13, spi_s, vc_spi3, sd_card_c, sd_card_d, _, _, _, _),
++ PIN(14, spi_s, uui, arm_jtag, vc_pwm0, vc_i2c0, sd_card_d, _, _),
++ PIN(15, spi_s, uui, arm_jtag, vc_pwm0, vc_i2c0, gpclk0, _, _),
++ PIN(18, sd_card_f, vc_pwm1, _, _, _, _, _, _),
++ PIN(19, sd_card_f, usb_pwr, vc_pwm1, _, _, _, _, _),
++ PIN(20, vc_i2c3, uui, vc_uart0, arm_jtag, vc_uart2, _, _, _),
++ PIN(21, vc_i2c3, uui, vc_uart0, arm_jtag, vc_uart2, _, _, _),
++ PIN(22, sd_card_f, vc_uart0, vc_i2c3, _, _, _, _, _),
++ PIN(23, vc_uart0, vc_i2c3, _, _, _, _, _, _),
++ PIN(24, sd_card_b, vc_spi0, arm_jtag, uart0, usb_pwr, vc_uart2, vc_uart0, _),
++ PIN(25, sd_card_b, vc_spi0, arm_jtag, uart0, usb_pwr, vc_uart2, vc_uart0, _),
++ PIN(26, sd_card_b, vc_spi0, arm_jtag, uart0, usb_vbus, vc_uart2, vc_spi0, _),
++ PIN(27, sd_card_b, vc_spi0, arm_jtag, uart0, vc_uart2, vc_spi0, _, _),
++ PIN(28, sd_card_b, vc_spi0, arm_jtag, vc_i2c0, vc_spi0, _, _, _),
++ PIN(29, arm_jtag, vc_i2c0, vc_spi0, _, _, _, _, _),
++ PIN(30, sd2, gpclk0, vc_pwm0, _, _, _, _, _),
++ PIN(31, sd2, vc_spi3, vc_pwm0, _, _, _, _, _),
++ PIN(32, sd2, vc_spi3, vc_uart3, _, _, _, _, _),
++ PIN(33, sd2, vc_spi3, vc_uart3, _, _, _, _, _),
++ PIN(34, sd2, vc_spi3, vc_i2c5, _, _, _, _, _),
++ PIN(35, sd2, vc_spi3, vc_i2c5, _, _, _, _, _),
++};
++
++static inline u32 bcm2712_reg_rd(struct bcm2712_pinctrl *pc, unsigned reg)
++{
++ return readl(pc->base + reg);
++}
++
++static inline void bcm2712_reg_wr(struct bcm2712_pinctrl *pc, unsigned reg,
++ u32 val)
++{
++ writel(val, pc->base + reg);
++}
++
++static enum bcm2712_funcs bcm2712_pinctrl_fsel_get(
++ struct bcm2712_pinctrl *pc, unsigned pin)
++{
++ u32 bit = pc->pin_regs[pin].mux_bit;
++ enum bcm2712_funcs func;
++ int fsel;
++ u32 val;
++
++ if (!bit)
++ return func_gpio;
++
++ val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++ fsel = (val >> BIT_TO_SHIFT(bit)) & BCM2712_FSEL_MASK;
++ func = pc->pin_funcs[pin].funcs[fsel];
++ if (func >= func_count)
++ func = (enum bcm2712_funcs)fsel;
++
++ dev_dbg(pc->dev, "get %04x: %08x (%u => %s)\n",
++ BIT_TO_REG(bit), val, pin,
++ bcm2712_func_names[func]);
++
++ return func;
++}
++
++static void bcm2712_pinctrl_fsel_set(
++ struct bcm2712_pinctrl *pc, unsigned pin,
++ enum bcm2712_funcs func)
++{
++ u32 bit = pc->pin_regs[pin].mux_bit, val;
++ const u8 *pin_funcs;
++ unsigned long flags;
++ int fsel;
++ int cur;
++ int i;
++
++ if (!bit || func >= func_count)
++ return;
++
++ fsel = BCM2712_FSEL_COUNT;
++
++ if (func >= BCM2712_FSEL_COUNT) {
++ /* Convert to an fsel number */
++ pin_funcs = pc->pin_funcs[pin].funcs;
++ for (i = 1; i < BCM2712_FSEL_COUNT; i++) {
++ if (pin_funcs[i - 1] == func) {
++ fsel = i;
++ break;
++ }
++ }
++ } else {
++ fsel = (enum bcm2712_funcs)func;
++ }
++ if (fsel >= BCM2712_FSEL_COUNT)
++ return;
++
++ spin_lock_irqsave(&pc->lock, flags);
++
++ val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++ cur = (val >> BIT_TO_SHIFT(bit)) & BCM2712_FSEL_MASK;
++
++ dev_dbg(pc->dev, "read %04x: %08x (%u => %s)\n",
++ BIT_TO_REG(bit), val, pin,
++ bcm2712_func_names[cur]);
++
++ if (cur != fsel) {
++ val &= ~(BCM2712_FSEL_MASK << BIT_TO_SHIFT(bit));
++ val |= fsel << BIT_TO_SHIFT(bit);
++
++ dev_dbg(pc->dev, "write %04x: %08x (%u <= %s)\n",
++ BIT_TO_REG(bit), val, pin,
++ bcm2712_func_names[fsel]);
++ bcm2712_reg_wr(pc, BIT_TO_REG(bit), val);
++ }
++
++ spin_unlock_irqrestore(&pc->lock, flags);
++}
++
++static int bcm2712_pctl_get_groups_count(struct pinctrl_dev *pctldev)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ return pc->pctl_desc.npins;
++}
++
++static const char *bcm2712_pctl_get_group_name(struct pinctrl_dev *pctldev,
++ unsigned selector)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ return pc->gpio_groups[selector];
++}
++
++static int bcm2712_pctl_get_group_pins(struct pinctrl_dev *pctldev,
++ unsigned selector,
++ const unsigned **pins,
++ unsigned *num_pins)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ *pins = &pc->pctl_desc.pins[selector].number;
++ *num_pins = 1;
++
++ return 0;
++}
++
++static void bcm2712_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
++ struct seq_file *s,
++ unsigned offset)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++ enum bcm2712_funcs fsel = bcm2712_pinctrl_fsel_get(pc, offset);
++ const char *fname = bcm2712_func_names[fsel];
++
++ seq_printf(s, "function %s", fname);
++}
++
++static void bcm2712_pctl_dt_free_map(struct pinctrl_dev *pctldev,
++ struct pinctrl_map *maps, unsigned num_maps)
++{
++ int i;
++
++ for (i = 0; i < num_maps; i++)
++ if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
++ kfree(maps[i].data.configs.configs);
++
++ kfree(maps);
++}
++
++static const struct pinctrl_ops bcm2712_pctl_ops = {
++ .get_groups_count = bcm2712_pctl_get_groups_count,
++ .get_group_name = bcm2712_pctl_get_group_name,
++ .get_group_pins = bcm2712_pctl_get_group_pins,
++ .pin_dbg_show = bcm2712_pctl_pin_dbg_show,
++ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
++ .dt_free_map = bcm2712_pctl_dt_free_map,
++};
++
++static int bcm2712_pmx_free(struct pinctrl_dev *pctldev,
++ unsigned offset)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ /* disable by setting to GPIO */
++ bcm2712_pinctrl_fsel_set(pc, offset, func_gpio);
++ return 0;
++}
++
++static int bcm2712_pmx_get_functions_count(struct pinctrl_dev *pctldev)
++{
++ return func_count;
++}
++
++static const char *bcm2712_pmx_get_function_name(struct pinctrl_dev *pctldev,
++ unsigned selector)
++{
++ return (selector < func_count) ? bcm2712_func_names[selector] : NULL;
++}
++
++static int bcm2712_pmx_get_function_groups(struct pinctrl_dev *pctldev,
++ unsigned selector,
++ const char * const **groups,
++ unsigned * const num_groups)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++ /* every pin can do every function */
++ *groups = pc->gpio_groups;
++ *num_groups = pc->pctl_desc.npins;
++
++ return 0;
++}
++
++static int bcm2712_pmx_set(struct pinctrl_dev *pctldev,
++ unsigned func_selector,
++ unsigned group_selector)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ bcm2712_pinctrl_fsel_set(pc, group_selector, func_selector);
++
++ return 0;
++}
++static int bcm2712_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned pin)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ bcm2712_pinctrl_fsel_set(pc, pin, func_gpio);
++
++ return 0;
++}
++
++static void bcm2712_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned offset)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ /* disable by setting to GPIO */
++ bcm2712_pinctrl_fsel_set(pc, offset, func_gpio);
++}
++
++static const struct pinmux_ops bcm2712_pmx_ops = {
++ .free = bcm2712_pmx_free,
++ .get_functions_count = bcm2712_pmx_get_functions_count,
++ .get_function_name = bcm2712_pmx_get_function_name,
++ .get_function_groups = bcm2712_pmx_get_function_groups,
++ .set_mux = bcm2712_pmx_set,
++ .gpio_request_enable = bcm2712_pmx_gpio_request_enable,
++ .gpio_disable_free = bcm2712_pmx_gpio_disable_free,
++};
++
++static unsigned int bcm2712_pull_config_get(struct bcm2712_pinctrl *pc,
++ unsigned int pin)
++{
++ u32 bit = pc->pin_regs[pin].pad_bit, val;
++
++ if (unlikely(bit == REG_BIT_INVALID))
++ return BCM2712_PULL_NONE;
++
++ val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++ return (val >> BIT_TO_SHIFT(bit)) & BCM2712_PULL_MASK;
++}
++
++static void bcm2712_pull_config_set(struct bcm2712_pinctrl *pc,
++ unsigned int pin, unsigned int arg)
++{
++ u32 bit = pc->pin_regs[pin].pad_bit, val;
++ unsigned long flags;
++
++ if (unlikely(bit == REG_BIT_INVALID)) {
++ dev_warn(pc->dev, "can't set pulls for %s\n", pc->gpio_groups[pin]);
++ return;
++ }
++
++ spin_lock_irqsave(&pc->lock, flags);
++
++ val = bcm2712_reg_rd(pc, BIT_TO_REG(bit));
++ val &= ~(BCM2712_PULL_MASK << BIT_TO_SHIFT(bit));
++ val |= (arg << BIT_TO_SHIFT(bit));
++ bcm2712_reg_wr(pc, BIT_TO_REG(bit), val);
++
++ spin_unlock_irqrestore(&pc->lock, flags);
++}
++
++static int bcm2712_pinconf_get(struct pinctrl_dev *pctldev,
++ unsigned pin, unsigned long *config)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++ enum pin_config_param param = pinconf_to_config_param(*config);
++ u32 arg;
++
++ switch (param) {
++ case PIN_CONFIG_BIAS_DISABLE:
++ arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_NONE);
++ break;
++ case PIN_CONFIG_BIAS_PULL_DOWN:
++ arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_DOWN);
++ break;
++ case PIN_CONFIG_BIAS_PULL_UP:
++ arg = (bcm2712_pull_config_get(pc, pin) == BCM2712_PULL_UP);
++ break;
++ default:
++ return -ENOTSUPP;
++ }
++
++ *config = pinconf_to_config_packed(param, arg);
++
++ return -ENOTSUPP;
++}
++
++static int bcm2712_pinconf_set(struct pinctrl_dev *pctldev,
++ unsigned int pin, unsigned long *configs,
++ unsigned int num_configs)
++{
++ struct bcm2712_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++ u32 param, arg;
++ int i;
++
++ for (i = 0; i < num_configs; i++) {
++ param = pinconf_to_config_param(configs[i]);
++ arg = pinconf_to_config_argument(configs[i]);
++
++ switch (param) {
++ case PIN_CONFIG_BIAS_DISABLE:
++ bcm2712_pull_config_set(pc, pin, BCM2712_PULL_NONE);
++ break;
++ case PIN_CONFIG_BIAS_PULL_DOWN:
++ bcm2712_pull_config_set(pc, pin, BCM2712_PULL_DOWN);
++ break;
++ case PIN_CONFIG_BIAS_PULL_UP:
++ bcm2712_pull_config_set(pc, pin, BCM2712_PULL_UP);
++ break;
++ default:
++ return -ENOTSUPP;
++ }
++ } /* for each config */
++
++ return 0;
++}
++
++static const struct pinconf_ops bcm2712_pinconf_ops = {
++ .is_generic = true,
++ .pin_config_get = bcm2712_pinconf_get,
++ .pin_config_set = bcm2712_pinconf_set,
++};
++
++static const struct pinctrl_desc bcm2712_c0_pinctrl_desc = {
++ .name = "pinctrl-bcm2712",
++ .pins = bcm2712_c0_gpio_pins,
++ .npins = ARRAY_SIZE(bcm2712_c0_gpio_pins),
++ .pctlops = &bcm2712_pctl_ops,
++ .pmxops = &bcm2712_pmx_ops,
++ .confops = &bcm2712_pinconf_ops,
++ .owner = THIS_MODULE,
++};
++
++static const struct pinctrl_desc bcm2712_c0_aon_pinctrl_desc = {
++ .name = "aon-pinctrl-bcm2712",
++ .pins = bcm2712_c0_aon_gpio_pins,
++ .npins = ARRAY_SIZE(bcm2712_c0_aon_gpio_pins),
++ .pctlops = &bcm2712_pctl_ops,
++ .pmxops = &bcm2712_pmx_ops,
++ .confops = &bcm2712_pinconf_ops,
++ .owner = THIS_MODULE,
++};
++
++static const struct pinctrl_desc bcm2712_d0_pinctrl_desc = {
++ .name = "pinctrl-bcm2712",
++ .pins = bcm2712_d0_gpio_pins,
++ .npins = ARRAY_SIZE(bcm2712_d0_gpio_pins),
++ .pctlops = &bcm2712_pctl_ops,
++ .pmxops = &bcm2712_pmx_ops,
++ .confops = &bcm2712_pinconf_ops,
++ .owner = THIS_MODULE,
++};
++
++static const struct pinctrl_desc bcm2712_d0_aon_pinctrl_desc = {
++ .name = "aon-pinctrl-bcm2712",
++ .pins = bcm2712_d0_aon_gpio_pins,
++ .npins = ARRAY_SIZE(bcm2712_d0_aon_gpio_pins),
++ .pctlops = &bcm2712_pctl_ops,
++ .pmxops = &bcm2712_pmx_ops,
++ .confops = &bcm2712_pinconf_ops,
++ .owner = THIS_MODULE,
++};
++
++static const struct pinctrl_gpio_range bcm2712_c0_pinctrl_gpio_range = {
++ .name = "pinctrl-bcm2712",
++ .npins = ARRAY_SIZE(bcm2712_c0_gpio_pins),
++};
++
++static const struct pinctrl_gpio_range bcm2712_c0_aon_pinctrl_gpio_range = {
++ .name = "aon-pinctrl-bcm2712",
++ .npins = ARRAY_SIZE(bcm2712_c0_aon_gpio_pins),
++};
++
++static const struct pinctrl_gpio_range bcm2712_d0_pinctrl_gpio_range = {
++ .name = "pinctrl-bcm2712",
++ .npins = ARRAY_SIZE(bcm2712_d0_gpio_pins),
++};
++
++static const struct pinctrl_gpio_range bcm2712_d0_aon_pinctrl_gpio_range = {
++ .name = "aon-pinctrl-bcm2712",
++ .npins = ARRAY_SIZE(bcm2712_d0_aon_gpio_pins),
++};
++
++static const struct bcm_plat_data bcm2712_c0_plat_data = {
++ .pctl_desc = &bcm2712_c0_pinctrl_desc,
++ .gpio_range = &bcm2712_c0_pinctrl_gpio_range,
++ .pin_regs = bcm2712_c0_gpio_pin_regs,
++ .pin_funcs = bcm2712_c0_gpio_pin_funcs,
++};
++
++static const struct bcm_plat_data bcm2712_c0_aon_plat_data = {
++ .pctl_desc = &bcm2712_c0_aon_pinctrl_desc,
++ .gpio_range = &bcm2712_c0_aon_pinctrl_gpio_range,
++ .pin_regs = bcm2712_c0_aon_gpio_pin_regs,
++ .pin_funcs = bcm2712_c0_aon_gpio_pin_funcs,
++};
++
++static const struct bcm_plat_data bcm2712_d0_plat_data = {
++ .pctl_desc = &bcm2712_d0_pinctrl_desc,
++ .gpio_range = &bcm2712_d0_pinctrl_gpio_range,
++ .pin_regs = bcm2712_d0_gpio_pin_regs,
++ .pin_funcs = bcm2712_d0_gpio_pin_funcs,
++};
++
++static const struct bcm_plat_data bcm2712_d0_aon_plat_data = {
++ .pctl_desc = &bcm2712_d0_aon_pinctrl_desc,
++ .gpio_range = &bcm2712_d0_aon_pinctrl_gpio_range,
++ .pin_regs = bcm2712_d0_aon_gpio_pin_regs,
++ .pin_funcs = bcm2712_d0_aon_gpio_pin_funcs,
++};
++
++static const struct of_device_id bcm2712_pinctrl_match[] = {
++ {
++ .compatible = "brcm,bcm2712-pinctrl",
++ .data = &bcm2712_c0_plat_data,
++ },
++ {
++ .compatible = "brcm,bcm2712-aon-pinctrl",
++ .data = &bcm2712_c0_aon_plat_data,
++ },
++
++ {
++ .compatible = "brcm,bcm2712c0-pinctrl",
++ .data = &bcm2712_c0_plat_data,
++ },
++ {
++ .compatible = "brcm,bcm2712c0-aon-pinctrl",
++ .data = &bcm2712_c0_aon_plat_data,
++ },
++
++ {
++ .compatible = "brcm,bcm2712d0-pinctrl",
++ .data = &bcm2712_d0_plat_data,
++ },
++ {
++ .compatible = "brcm,bcm2712d0-aon-pinctrl",
++ .data = &bcm2712_d0_aon_plat_data,
++ },
++ {}
++};
++
++static int bcm2712_pinctrl_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ const struct bcm_plat_data *pdata;
++ const struct of_device_id *match;
++ struct bcm2712_pinctrl *pc;
++ const char **names;
++ int num_pins, i;
++
++ match = of_match_node(bcm2712_pinctrl_match, np);
++ if (!match)
++ return -EINVAL;
++ pdata = match->data;
++
++ pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
++ if (!pc)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, pc);
++ pc->dev = dev;
++ spin_lock_init(&pc->lock);
++
++ pc->base = devm_of_iomap(dev, np, 0, NULL);
++ if (IS_ERR(pc->base)) {
++ dev_err(dev, "could not get IO memory\n");
++ return PTR_ERR(pc->base);
++ }
++
++ pc->pctl_desc = *pdata->pctl_desc;
++ num_pins = pc->pctl_desc.npins;
++ names = devm_kmalloc_array(dev, num_pins, sizeof(const char *),
++ GFP_KERNEL);
++ if (!names)
++ return -ENOMEM;
++ for (i = 0; i < num_pins; i++)
++ names[i] = pc->pctl_desc.pins[i].name;
++ pc->gpio_groups = names;
++ pc->pin_regs = pdata->pin_regs;
++ pc->pin_funcs = pdata->pin_funcs;
++ pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
++ if (IS_ERR(pc->pctl_dev))
++ return PTR_ERR(pc->pctl_dev);
++
++ pc->gpio_range = *pdata->gpio_range;
++ pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
++
++ return 0;
++}
++
++static struct platform_driver bcm2712_pinctrl_driver = {
++ .probe = bcm2712_pinctrl_probe,
++ .driver = {
++ .name = MODULE_NAME,
++ .of_match_table = bcm2712_pinctrl_match,
++ .suppress_bind_attrs = true,
++ },
++};
++builtin_platform_driver(bcm2712_pinctrl_driver);
--- /dev/null
+From b627647c4500d39cb026924b608841fdf4d4d7e9 Mon Sep 17 00:00:00 2001
+From: Ulf Hansson <ulf.hansson@linaro.org>
+Date: Thu, 29 Oct 2020 09:57:16 +0800
+Subject: [PATCH] mmc: brcmstb: add support for BCM2712
+
+BCM2712 has an SD Express capable SDHCI implementation and uses
+the SDIO CFG register block present on other STB chips.
+
+Add plumbing for SD Express handover and BCM2712-specific functions.
+
+Due to the common bus infrastructure between BCM2711 and BCM2712,
+the driver also needs to implement 32-bit IO accessors.
+
+mmc: brcmstb: override card presence if broken-cd is set
+
+Not just if the card is declared as nonremovable.
+
+sdhci: brcmstb: align SD express switchover with SD spec v8.00
+
+Part 1 of the Physical specification, figure 3-24, details the switch
+sequence for cards initially probed as SD. Add a missing check for DAT2
+level after switching VDD2 on.
+
+sdhci: brcmstb: clean up SD Express probe and error handling
+
+Refactor to avoid spurious error messages in dmesg if the requisite SD
+Express DT nodes aren't present.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+mmc: sdhci-brcmstb: only use the delay line PHY for tuneable speeds
+
+The MMC core has a 200MHz core clock which allows the use of DDR50 and
+below without incremental phase tuning. SDR50/SDR104 and the EMMC HS200
+speeds require tuning.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/host/Kconfig | 2 +
+ drivers/mmc/host/sdhci-brcmstb.c | 356 +++++++++++++++++++++++++++++++
+ 2 files changed, 358 insertions(+)
+
+--- a/drivers/mmc/host/Kconfig
++++ b/drivers/mmc/host/Kconfig
+@@ -1082,7 +1082,9 @@ config MMC_SDHCI_BRCMSTB
+ tristate "Broadcom SDIO/SD/MMC support"
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC
+ depends on MMC_SDHCI_PLTFM
++ select MMC_SDHCI_IO_ACCESSORS
+ select MMC_CQHCI
++ select OF_DYNAMIC
+ default y
+ help
+ This selects support for the SDIO/SD/MMC Host Controller on
+--- a/drivers/mmc/host/sdhci-brcmstb.c
++++ b/drivers/mmc/host/sdhci-brcmstb.c
+@@ -11,6 +11,8 @@
+ #include <linux/of.h>
+ #include <linux/bitops.h>
+ #include <linux/delay.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/regulator/consumer.h>
+
+ #include "sdhci-cqhci.h"
+ #include "sdhci-pltfm.h"
+@@ -26,18 +28,43 @@
+
+ #define BRCMSTB_PRIV_FLAGS_HAS_CQE BIT(0)
+ #define BRCMSTB_PRIV_FLAGS_GATE_CLOCK BIT(1)
++#define BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS BIT(2)
+
+ #define SDHCI_ARASAN_CQE_BASE_ADDR 0x200
+
++#define SDIO_CFG_CTRL 0x0
++#define SDIO_CFG_CTRL_SDCD_N_TEST_EN BIT(31)
++#define SDIO_CFG_CTRL_SDCD_N_TEST_LEV BIT(30)
++
++#define SDIO_CFG_SD_PIN_SEL 0x44
++#define SDIO_CFG_SD_PIN_SEL_MASK 0x3
++#define SDIO_CFG_SD_PIN_SEL_CARD BIT(1)
++
++#define SDIO_CFG_MAX_50MHZ_MODE 0x1ac
++#define SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE BIT(31)
++#define SDIO_CFG_MAX_50MHZ_MODE_ENABLE BIT(0)
++
+ struct sdhci_brcmstb_priv {
+ void __iomem *cfg_regs;
+ unsigned int flags;
+ struct clk *base_clk;
+ u32 base_freq_hz;
++ u32 shadow_cmd;
++ u32 shadow_blk;
++ bool is_cmd_shadowed;
++ bool is_blk_shadowed;
++ struct regulator *sde_1v8;
++ struct device_node *sde_pcie;
++ void *__iomem sde_ioaddr;
++ void *__iomem sde_ioaddr2;
++ struct pinctrl *pinctrl;
++ struct pinctrl_state *pins_default;
++ struct pinctrl_state *pins_sdex;
+ };
+
+ struct brcmstb_match_priv {
+ void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
++ void (*cfginit)(struct sdhci_host *host);
+ struct sdhci_ops *ops;
+ const unsigned int flags;
+ };
+@@ -94,6 +121,124 @@ static void sdhci_brcmstb_set_clock(stru
+ sdhci_enable_clk(host, clk);
+ }
+
++#define REG_OFFSET_IN_BITS(reg) ((reg) << 3 & 0x18)
++
++static inline u32 sdhci_brcmstb_32only_readl(struct sdhci_host *host, int reg)
++{
++ u32 val = readl(host->ioaddr + reg);
++
++ pr_debug("%s: readl [0x%02x] 0x%08x\n",
++ mmc_hostname(host->mmc), reg, val);
++ return val;
++}
++
++static u16 sdhci_brcmstb_32only_readw(struct sdhci_host *host, int reg)
++{
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++ u32 val;
++ u16 word;
++
++ if ((reg == SDHCI_TRANSFER_MODE) && brcmstb_priv->is_cmd_shadowed) {
++ /* Get the saved transfer mode */
++ val = brcmstb_priv->shadow_cmd;
++ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
++ brcmstb_priv->is_blk_shadowed) {
++ /* Get the saved block info */
++ val = brcmstb_priv->shadow_blk;
++ } else {
++ val = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++ }
++ word = val >> REG_OFFSET_IN_BITS(reg) & 0xffff;
++ return word;
++}
++
++static u8 sdhci_brcmstb_32only_readb(struct sdhci_host *host, int reg)
++{
++ u32 val = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++ u8 byte = val >> REG_OFFSET_IN_BITS(reg) & 0xff;
++ return byte;
++}
++
++static inline void sdhci_brcmstb_32only_writel(struct sdhci_host *host, u32 val, int reg)
++{
++ pr_debug("%s: writel [0x%02x] 0x%08x\n",
++ mmc_hostname(host->mmc), reg, val);
++
++ writel(val, host->ioaddr + reg);
++}
++
++/*
++ * BCM2712 unfortunately carries with it a perennial bug with the SD controller
++ * register interface present on previous chips (2711/2709/2708). Accesses must
++ * be dword-sized and a read-modify-write cycle to the 32-bit registers
++ * containing the COMMAND, TRANSFER_MODE, BLOCK_SIZE and BLOCK_COUNT registers
++ * tramples the upper/lower 16 bits of data written. BCM2712 does not seem to
++ * need the extreme delay between each write as on previous chips, just the
++ * serialisation of writes to these registers in a single 32-bit operation.
++ */
++static void sdhci_brcmstb_32only_writew(struct sdhci_host *host, u16 val, int reg)
++{
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++ u32 word_shift = REG_OFFSET_IN_BITS(reg);
++ u32 mask = 0xffff << word_shift;
++ u32 oldval, newval;
++
++ if (reg == SDHCI_COMMAND) {
++ /* Write the block now as we are issuing a command */
++ if (brcmstb_priv->is_blk_shadowed) {
++ sdhci_brcmstb_32only_writel(host, brcmstb_priv->shadow_blk,
++ SDHCI_BLOCK_SIZE);
++ brcmstb_priv->is_blk_shadowed = false;
++ }
++ oldval = brcmstb_priv->shadow_cmd;
++ brcmstb_priv->is_cmd_shadowed = false;
++ } else if ((reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) &&
++ brcmstb_priv->is_blk_shadowed) {
++ /* Block size and count are stored in shadow reg */
++ oldval = brcmstb_priv->shadow_blk;
++ } else {
++ /* Read reg, all other registers are not shadowed */
++ oldval = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++ }
++ newval = (oldval & ~mask) | (val << word_shift);
++
++ if (reg == SDHCI_TRANSFER_MODE) {
++ /* Save the transfer mode until the command is issued */
++ brcmstb_priv->shadow_cmd = newval;
++ brcmstb_priv->is_cmd_shadowed = true;
++ } else if (reg == SDHCI_BLOCK_SIZE || reg == SDHCI_BLOCK_COUNT) {
++ /* Save the block info until the command is issued */
++ brcmstb_priv->shadow_blk = newval;
++ brcmstb_priv->is_blk_shadowed = true;
++ } else {
++ /* Command or other regular 32-bit write */
++ sdhci_brcmstb_32only_writel(host, newval, reg & ~3);
++ }
++}
++
++static void sdhci_brcmstb_32only_writeb(struct sdhci_host *host, u8 val, int reg)
++{
++ u32 oldval = sdhci_brcmstb_32only_readl(host, (reg & ~3));
++ u32 byte_shift = REG_OFFSET_IN_BITS(reg);
++ u32 mask = 0xff << byte_shift;
++ u32 newval = (oldval & ~mask) | (val << byte_shift);
++
++ sdhci_brcmstb_32only_writel(host, newval, reg & ~3);
++}
++
++static void sdhci_brcmstb_set_power(struct sdhci_host *host, unsigned char mode,
++ unsigned short vdd)
++{
++ if (!IS_ERR(host->mmc->supply.vmmc)) {
++ struct mmc_host *mmc = host->mmc;
++
++ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd);
++ }
++ sdhci_set_power_noreg(host, mode, vdd);
++}
++
+ static void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+ {
+@@ -123,6 +268,146 @@ static void sdhci_brcmstb_set_uhs_signal
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+ }
+
++static void sdhci_brcmstb_cfginit_2712(struct sdhci_host *host)
++{
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++ bool want_dll = false;
++ u32 uhs_mask = (MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104);
++ u32 hsemmc_mask = (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS200_1_2V_SDR |
++ MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V);
++ u32 reg;
++
++ if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V)) {
++ if((host->mmc->caps & uhs_mask) || (host->mmc->caps2 & hsemmc_mask))
++ want_dll = true;
++ }
++
++ /*
++ * If we want a speed that requires tuning,
++ * then select the delay line PHY as the clock source.
++ */
++ if (want_dll) {
++ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
++ reg &= ~SDIO_CFG_MAX_50MHZ_MODE_ENABLE;
++ reg |= SDIO_CFG_MAX_50MHZ_MODE_STRAP_OVERRIDE;
++ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_MAX_50MHZ_MODE);
++ }
++
++ if ((host->mmc->caps & MMC_CAP_NONREMOVABLE) ||
++ (host->mmc->caps & MMC_CAP_NEEDS_POLL)) {
++ /* Force presence */
++ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
++ reg &= ~SDIO_CFG_CTRL_SDCD_N_TEST_LEV;
++ reg |= SDIO_CFG_CTRL_SDCD_N_TEST_EN;
++ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_CTRL);
++ } else {
++ /* Enable card detection line */
++ reg = readl(brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL);
++ reg &= ~SDIO_CFG_SD_PIN_SEL_MASK;
++ reg |= SDIO_CFG_SD_PIN_SEL_CARD;
++ writel(reg, brcmstb_priv->cfg_regs + SDIO_CFG_SD_PIN_SEL);
++ }
++}
++
++static int bcm2712_init_sd_express(struct sdhci_host *host, struct mmc_ios *ios)
++{
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct sdhci_brcmstb_priv *brcmstb_priv = sdhci_pltfm_priv(pltfm_host);
++ struct device *dev = host->mmc->parent;
++ u32 ctrl_val;
++ u32 present_state;
++ int ret;
++
++ if (!brcmstb_priv->sde_ioaddr || !brcmstb_priv->sde_ioaddr2)
++ return -EINVAL;
++
++ if (!brcmstb_priv->pinctrl)
++ return -EINVAL;
++
++ /* Turn off the SD clock first */
++ sdhci_set_clock(host, 0);
++
++ /* Disable SD DAT0-3 pulls */
++ pinctrl_select_state(brcmstb_priv->pinctrl, brcmstb_priv->pins_sdex);
++
++ ctrl_val = readl(brcmstb_priv->sde_ioaddr);
++ dev_dbg(dev, "ctrl_val 1 %08x\n", ctrl_val);
++
++ /* Tri-state the SD pins */
++ ctrl_val |= 0x1ff8;
++ writel(ctrl_val, brcmstb_priv->sde_ioaddr);
++ dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr));
++ /* Let voltages settle */
++ udelay(100);
++
++ /* Enable the PCIe sideband pins */
++ ctrl_val &= ~0x6000;
++ writel(ctrl_val, brcmstb_priv->sde_ioaddr);
++ dev_dbg(dev, "ctrl_val 1->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr));
++ /* Let voltages settle */
++ udelay(100);
++
++ /* Turn on the 1v8 VDD2 regulator */
++ ret = regulator_enable(brcmstb_priv->sde_1v8);
++ if (ret)
++ return ret;
++
++ /* Wait for Tpvcrl */
++ msleep(1);
++
++ /* Sample DAT2 (CLKREQ#) - if low, card is in PCIe mode */
++ present_state = sdhci_readl(host, SDHCI_PRESENT_STATE);
++ present_state = (present_state & SDHCI_DATA_LVL_MASK) >> SDHCI_DATA_LVL_SHIFT;
++ dev_dbg(dev, "state = 0x%08x\n", present_state);
++
++ if (present_state & BIT(2)) {
++ dev_err(dev, "DAT2 still high, abandoning SDex switch\n");
++ return -ENODEV;
++ }
++
++ /* Turn on the LCPLL PTEST mux */
++ ctrl_val = readl(brcmstb_priv->sde_ioaddr2 + 20); // misc5
++ ctrl_val &= ~(0x7 << 7);
++ ctrl_val |= 3 << 7;
++ writel(ctrl_val, brcmstb_priv->sde_ioaddr2 + 20);
++ dev_dbg(dev, "misc 5->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2 + 20));
++
++ /* PTEST diff driver enable */
++ ctrl_val = readl(brcmstb_priv->sde_ioaddr2);
++ ctrl_val |= BIT(21);
++ writel(ctrl_val, brcmstb_priv->sde_ioaddr2);
++
++ dev_dbg(dev, "misc 0->%08x (%08x)\n", ctrl_val, readl(brcmstb_priv->sde_ioaddr2));
++
++ /* Wait for more than the minimum Tpvpgl time */
++ msleep(100);
++
++ if (brcmstb_priv->sde_pcie) {
++ struct of_changeset changeset;
++ static struct property okay_property = {
++ .name = "status",
++ .value = "okay",
++ .length = 5,
++ };
++
++ /* Enable the pcie controller */
++ of_changeset_init(&changeset);
++ ret = of_changeset_update_property(&changeset,
++ brcmstb_priv->sde_pcie,
++ &okay_property);
++ if (ret) {
++ dev_err(dev, "%s: failed to update property - %d\n", __func__,
++ ret);
++ return -ENODEV;
++ }
++ ret = of_changeset_apply(&changeset);
++ }
++
++ dev_dbg(dev, "%s -> %d\n", __func__, ret);
++ return ret;
++}
++
+ static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
+ {
+ sdhci_dumpregs(mmc_priv(mmc));
+@@ -155,6 +440,21 @@ static struct sdhci_ops sdhci_brcmstb_op
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ };
+
++static struct sdhci_ops sdhci_brcmstb_ops_2712 = {
++ .read_l = sdhci_brcmstb_32only_readl,
++ .read_w = sdhci_brcmstb_32only_readw,
++ .read_b = sdhci_brcmstb_32only_readb,
++ .write_l = sdhci_brcmstb_32only_writel,
++ .write_w = sdhci_brcmstb_32only_writew,
++ .write_b = sdhci_brcmstb_32only_writeb,
++ .set_clock = sdhci_set_clock,
++ .set_power = sdhci_brcmstb_set_power,
++ .set_bus_width = sdhci_set_bus_width,
++ .reset = sdhci_reset,
++ .set_uhs_signaling = sdhci_set_uhs_signaling,
++ .init_sd_express = bcm2712_init_sd_express,
++};
++
+ static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
+ .set_clock = sdhci_brcmstb_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+@@ -179,10 +479,16 @@ static const struct brcmstb_match_priv m
+ .ops = &sdhci_brcmstb_ops_7216,
+ };
+
++static const struct brcmstb_match_priv match_priv_2712 = {
++ .cfginit = sdhci_brcmstb_cfginit_2712,
++ .ops = &sdhci_brcmstb_ops_2712,
++};
++
+ static const struct of_device_id sdhci_brcm_of_match[] = {
+ { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 },
+ { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 },
+ { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 },
++ { .compatible = "brcm,bcm2712-sdhci", .data = &match_priv_2712 },
+ {},
+ };
+
+@@ -256,6 +562,7 @@ static int sdhci_brcmstb_probe(struct pl
+ u32 actual_clock_mhz;
+ struct sdhci_host *host;
+ struct resource *iomem;
++ bool no_pinctrl = false;
+ struct clk *clk;
+ struct clk *base_clk = NULL;
+ int res;
+@@ -290,6 +597,11 @@ static int sdhci_brcmstb_probe(struct pl
+ match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
+ }
+
++ priv->sde_pcie = of_parse_phandle(pdev->dev.of_node,
++ "sde-pcie", 0);
++ if (priv->sde_pcie)
++ priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
++
+ /* Map in the non-standard CFG registers */
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem);
+@@ -303,6 +615,43 @@ static int sdhci_brcmstb_probe(struct pl
+ if (res)
+ goto err;
+
++ priv->sde_1v8 = devm_regulator_get_optional(&pdev->dev, "sde-1v8");
++ if (IS_ERR(priv->sde_1v8))
++ priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
++
++ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 2);
++ if (iomem) {
++ priv->sde_ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
++ if (IS_ERR(priv->sde_ioaddr))
++ priv->sde_ioaddr = NULL;
++ }
++
++ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 3);
++ if (iomem) {
++ priv->sde_ioaddr2 = devm_ioremap_resource(&pdev->dev, iomem);
++ if (IS_ERR(priv->sde_ioaddr2))
++ priv->sde_ioaddr = NULL;
++ }
++
++ priv->pinctrl = devm_pinctrl_get(&pdev->dev);
++ if (IS_ERR(priv->pinctrl)) {
++ no_pinctrl = true;
++ }
++ priv->pins_default = pinctrl_lookup_state(priv->pinctrl, "default");
++ if (IS_ERR(priv->pins_default)) {
++ dev_dbg(&pdev->dev, "No pinctrl default state\n");
++ no_pinctrl = true;
++ }
++ priv->pins_sdex = pinctrl_lookup_state(priv->pinctrl, "sd-express");
++ if (IS_ERR(priv->pins_sdex)) {
++ dev_dbg(&pdev->dev, "No pinctrl sd-express state\n");
++ no_pinctrl = true;
++ }
++ if (no_pinctrl || !priv->sde_ioaddr || !priv->sde_ioaddr2) {
++ priv->pinctrl = NULL;
++ priv->flags &= ~BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS;
++ }
++
+ /*
+ * Automatic clock gating does not work for SD cards that may
+ * voltage switch so only enable it for non-removable devices.
+@@ -319,6 +668,13 @@ static int sdhci_brcmstb_probe(struct pl
+ (host->mmc->caps2 & MMC_CAP2_HS400_ES))
+ host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es;
+
++ if (host->ops->init_sd_express &&
++ (priv->flags & BRCMSTB_PRIV_FLAGS_HAS_SD_EXPRESS))
++ host->mmc->caps2 |= MMC_CAP2_SD_EXP;
++
++ if(match_priv->cfginit)
++ match_priv->cfginit(host);
++
+ /*
+ * Supply the existing CAPS, but clear the UHS modes. This
+ * will allow these modes to be specified by device tree
--- /dev/null
+From 9564939f1a92e5f9807461539de28c50e5bee440 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 6 Jul 2021 09:45:36 +0100
+Subject: [PATCH] sdhci: Add SD Express hook
+
+sdhci: remove PYA0_INTR_BUG quirk. Add quirks to disable some of the higher SDR speeds at 1.8v.
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 5 ++++-
+ drivers/mmc/host/sdhci.c | 19 +++++++++++++++++++
+ drivers/mmc/host/sdhci.h | 6 ++++++
+ 3 files changed, 29 insertions(+), 1 deletion(-)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -363,7 +363,10 @@ static const struct sdhci_pltfm_data sdh
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL,
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+- SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN,
++ SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN |
++ SDHCI_QUIRK2_NO_SDR50 |
++ SDHCI_QUIRK2_NO_SDR104 |
++ SDHCI_QUIRK2_NO_SDR25,
+ };
+
+ static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -3071,6 +3071,15 @@ static void sdhci_card_event(struct mmc_
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+
++static int sdhci_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++ struct sdhci_host *host = mmc_priv(mmc);
++
++ if (!host->ops->init_sd_express)
++ return -EOPNOTSUPP;
++ return host->ops->init_sd_express(host, ios);
++}
++
+ static const struct mmc_host_ops sdhci_ops = {
+ .request = sdhci_request,
+ .post_req = sdhci_post_req,
+@@ -3086,6 +3095,7 @@ static const struct mmc_host_ops sdhci_o
+ .execute_tuning = sdhci_execute_tuning,
+ .card_event = sdhci_card_event,
+ .card_busy = sdhci_card_busy,
++ .init_sd_express = sdhci_init_sd_express,
+ };
+
+ /*****************************************************************************\
+@@ -4605,6 +4615,15 @@ int sdhci_setup_host(struct sdhci_host *
+ !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
+ mmc->caps |= MMC_CAP_UHS_DDR50;
+
++ if (host->quirks2 & SDHCI_QUIRK2_NO_SDR25)
++ mmc->caps &= ~MMC_CAP_UHS_SDR25;
++
++ if (host->quirks2 & SDHCI_QUIRK2_NO_SDR50)
++ mmc->caps &= ~MMC_CAP_UHS_SDR50;
++
++ if (host->quirks2 & SDHCI_QUIRK2_NO_SDR104)
++ mmc->caps &= ~MMC_CAP_UHS_SDR104;
++
+ /* Does the host need tuning for SDR50? */
+ if (host->caps1 & SDHCI_USE_SDR50_TUNING)
+ host->flags |= SDHCI_SDR50_NEEDS_TUNING;
+--- a/drivers/mmc/host/sdhci.h
++++ b/drivers/mmc/host/sdhci.h
+@@ -481,6 +481,11 @@ struct sdhci_host {
+ /* Issue CMD and DATA reset together */
+ #define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER (1<<19)
+
++/* Quirks to ignore a speed if a that speed is unreliable */
++#define SDHCI_QUIRK2_NO_SDR25 (1<<19)
++#define SDHCI_QUIRK2_NO_SDR50 (1<<20)
++#define SDHCI_QUIRK2_NO_SDR104 (1<<21)
++
+ int irq; /* Device IRQ */
+ void __iomem *ioaddr; /* Mapped address */
+ phys_addr_t mapbase; /* physical address base */
+@@ -663,6 +668,7 @@ struct sdhci_ops {
+ void (*request_done)(struct sdhci_host *host,
+ struct mmc_request *mrq);
+ void (*dump_vendor_regs)(struct sdhci_host *host);
++ int (*init_sd_express)(struct sdhci_host *host, struct mmc_ios *ios);
+ };
+
+ #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
--- /dev/null
+From ce14be51d71bf39893786d380cbb82e81d2a10d5 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.org>
+Date: Wed, 14 Jul 2021 09:32:49 +0100
+Subject: [PATCH] Add new "pispbe" driver (though not yet the Makesfiles or DT
+ required to use it)
+
+media: bcm2712: Initial commit of the PiSP BE driver
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: bcm2712_pisp_be: PiSP driver updates.
+
+- Start registering video nodes from /dev/video20
+- Formatting fixes
+- Define MODULE_DEVICE_TABLE() to probe correctly
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: pisp_be: Improve image format support
+
+Add a new format table that lists the V4L2 format enums and their properties.
+Keep the exising 'RPBP' format to support the userland verification tools.
+This format requires userland to fill all plane properties. Standard V4L2
+formats will derive these properties from the format table.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: pisp_be: Advertise the meta output format explictily.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: pisp_be: Various updates and cleanups
+
+- Switch to a single node group for now.
+- Add a node description table to simplify node handling.
+- Switch HoG output to V4L2_CAP_META_CAPTURE type.
+- Use string descriptions for node names in logging messages.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+pisp_be: Updates for libcamera usage:
+
+- Remove indexes from device entity names
+- Add enumframesize and enumfmts ioctls
+- Add default format to all nodes.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+v4l2: pisp_be: Move format definitions into v4l2 core
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: raspberrypi: Move PiSP common headers to a single location
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: raspberrypi: Remove old pispbe driver.
+
+This is now supersede by the driver in drivers/media/platform/raspberrypi/
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+PISP-BE Driver: Automate buffer-cycling for TDN and Stitch state.
+Remove "tdn-input" and "stitch-input" nodes altogether (the output
+nodes must still be opened and REQBUFS called with 1 or 2 buffers).
+Also, a bit of tidying of buffer address handling and locking.
+
+PISP-BE driver: Turn debug level right down to reduce overly-chatty messages
+
+media: bcm2712: Depend on CONFIG_PM
+
+Depend on CONFIG_PM as the driver uses the runtime_pm infrastructure.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+drivers: media: pisp_be: Move BE driver to a raspberrypi directory
+
+Move the pisp_be driver from drivers/media/platform/raspberrypi/ to
+drivers/media/platform/raspberrypi/pisp_be/. This seems the accepted
+convention in the drivers/media/platform/ directory structure.
+
+Also rename the driver module from bcm2712_pisp_be to pisp_be.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+pisp_be: Updates for libcamera streaming:
+
+- Add some required v4l2 formats
+- Add buf_prepare ioctl
+- Set plane offsets correctly before reprogramming
+
+pisp_be: Reduce logging verbosity
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+pisp_be: Add buffer timestamps
+
+While at it, remove duplicate code when checking if the HW has completed
+multiple jobs.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+pisp_be: Remove queue size allocation constraint
+
+PISP-BE driver: Fix ISR to handle multiple done/start events.
+
+PISP-BE: Fix variable-name shadowing bugette
+
+PISP-BE: Support for two node groups. Reorganize the driver.
+
+To support 2 concurrent libcamera applications, we need 2 node groups,
+need to allow multiple opens of each node (because libcamera does this)
+and create a separate media device per group (to support file-locking).
+
+This triggered significant rearrangement of the driver. Some calls
+that we formerly intercepted have been delegated back to v4l2/vb2.
+Logging changes arising from multiple v4l2_dev. Refactored probe()
+and initialization. Avoid dynamically-allocated entity name strings.
+
+drivers: media: pisp_be: Add vidioc_enum_fmt_meta_out
+
+This was missing in the struct v4l2_ioctl_ops definition.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: media: pispe_be: Add Bayer compressed formats
+
+Add PiSP Bayer compressed formats to the list of supported pixel formats
+for the PiSP backend driver.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: meida: pisp_be: Fix overflow in plane size calculations
+
+The calculations for buffer plane sizes can overflow because of the
+plane factor shift. Fix this by using u64 integers for the calculations.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: media: pisp_be: Use 0P3 for plane factors
+
+Use less precision for the plane factors to avoid any nasty overflows.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: pisp: Checkpatch and coding style fixups
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+
+media: pisp_be: More coding style fixups
+
+media: platform: bcm2712: pisp_be: Fix crash when buffer format not set
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+
+media: platform: bcm2712: pisp_be: Allow non-SRGB colour spaces on RGB outputs
+
+Allow colour spaces other than SRGB when the output format in question
+is an RGB output. This commit merely ports over existing changes from
+the vc4 ISP driver.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+
+media: platform: bcm2712: Tweak list of BE supported image formats
+
+Remove RGB565 and 10- and 12-bit packed raw formats, which ISP-BE
+can't support for input or output. Add NV12M and NV21M which it can.
+(I didn't bother adding YUV422P, which apparently is not widely used.)
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+
+pisp_be: Fill the hardware revision in the media entity struct
+
+This can be used by userland to determine the hardware capabilities.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+bcm2712: Use BIT() macro
+
+Use the BIT() macro instead of plain bit shifting.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Invert condition in pispbe_schedule_internal()
+
+Return earlier and save one indentation level
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Invert condition in for loop
+
+Save one indentation level by continuing if the node is not streaming.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Do not declare a local variable
+
+There already is a truct pispbe_node *node in the function scope.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm21712: Siplify pispbe_schedule_one()
+
+A little more verbose but easier to follow ?
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+bcm2712: Rename pispbe_schedule_all() to pispbe_schedule_any()
+
+The pispbe_schedule_all() function name is misleading, as the function
+schedule a single job from any of the node groups. Rename it.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: platform: bcm2712: Remove buffer auto-cycling from ISP-BE
+
+Previously, the ISP-BE driver tried to automate "ping pong" buffers
+for TDN and HDR state, but did not fully conceal them from users.
+
+The automation has been removed: there are now separate output and
+capture queues for each of TDN and Stitch, which must be managed by
+user code (DMABUFs may be used to circulate buffers between queues).
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+
+drivers: media: pisp_be: Cache BE config buffer vaddr
+
+When programming a new job, we access at the config buffer, possibly
+from ISR context. So fetch and the virtual address when queuing the
+buffer.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: media: pisp_be: Remove all traces of ctrls and request API
+
+These APIs are not (and will not) be used by the driver, so remove them.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: bcm2712: Replace v4l2_dbg with dev_dbg
+
+Replace the v4l2 debug helpers with the device debug once, which are
+preferred.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Remove of_match_ptr()
+
+The of_match_ptr() usage could cause a compiler warning if
+CONFIG_OF is not enabled, as the pispbe_of_match variable would
+result unused.
+
+As the of_match_table field of struct platform_driver exists
+unconditionally, drop of_match_ptr() to avoid a warning.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+drivers: media: pispbe: Add local config buffer DMA allocation
+
+When initialiasing the driver, allocate a number of tiles + config
+structures used for storing hardware config internally in the driver.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: media: pispbe: Use local config buffers
+
+Store a copy of the config + tiles buffer locally when the buffer gets
+queued. This resolves the security issue where a userland process may
+modify the config buffer after it has been queued.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+drivers: media: pispbe: Validate config buffers
+
+Perform a basic config validation on the device output nodes to ensure
+the buffer size and stride values do not result in a buffer overrun.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: bcm2712: Rework probe sequence order
+
+Rework the probe sequence to:
+1) Use dev_err_probe() when failing to get clocks
+2) Disable clock on error path
+3) Disable the node groups if they have been enabled and
+ propagate the error up
+
+Also disable clocks in the remove() function.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Use pm_runtime_ops
+
+Introduce usage of runtime resume and suspend operations.
+
+The diver only uses a single clock source which is enable/disabled
+at resume and suspend time.
+
+Implement file open and release operations to control enablement of
+the clock provider.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Demote info message
+
+Demote info message about clock enablement to dev_dbg()
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Move pm_runtime calls to streamon/streamoff
+
+Move the calls to pm_runtime_resume_and_get() and pm_runtime_put()
+to the streamon and streamoff ioctl handlers.
+
+Remove custom handlers for the open and close file operations and use
+the framework provided helpers.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Use pm_runtime_autosuspend()
+
+Use the _autosuspend() version of runtime_pm_put() in order to avoid
+resuming and suspending the peripheral in between streaming sessions
+closely apart one from the other.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+drivers: media: pisp_be: Conditionally check buffers when preparing jobs
+
+When preparing a job, check the global enables in the config structure
+to see if the Output0/1, Tdn and Stitch blocks are enabled, and only
+test for a buffer queued if they are.
+
+This will allow userland to control the outputs selectively without
+disabling/re-enabling the respective device nodes.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: bcm2712: Rework media controller registration
+
+The current implementation register the v4l2_device and the video
+devices first, then creates the media controller and manually registers
+entities there.
+
+Rework the registration procedure to first create the v4l2_device and
+register the media_device with it. Then create the video nodes which
+gets automatically registered in the media graph by the core.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Create v4l2_subdev for ISP entity
+
+Create a v4l2 subdevice to represent the PISPBE ISP entity.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Fix v4l2-compliance warn on QUERYCAP
+
+Fix:
+
+warn: v4l2-compliance.cpp(669): media bus_info
+'platform:1000880000.pisp_be' differs from V4L2 bus_info
+'platform:pispbe'
+
+by populating the driver caps bus_info by using dev_name().
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Fix v4l2-compliance warn on invalid pixfmt
+
+The V4L2 API for the TRY_FMT/S_FMT ioctl allows the ioctl handler to
+return an error code only in specific conditions. If an invalid pixel
+format is supplied it should be adjusted instead of an error being
+returned.
+
+Albeit, v4l2-compliance treats this situation as a warning and not as
+an error because the behaviour has been discussed in length in the past.
+
+warn: v4l2-test-formats.cpp(794): TRY_FMT cannot handle an invalid pixelformat.
+warn: v4l2-test-formats.cpp(795): This may or may not be a problem. For more information see:
+warn: v4l2-test-formats.cpp(796): http://www.mail-archive.com/linux-media@vger.kernel.org/msg56550.html
+VIDIOC_TRY_FMT returned -1 (Invalid argument)
+
+Regardless of the warning vs failure decision, adjust the try_format()
+function implementation to use V4L2_PIX_FMT_YUV420M as default pixel
+format if the supplied one is invalid.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Fix v4l2-compliance warn on HOG pix format
+
+The try_format() implementation for the HOG video device node returns
+an error if the supplied pixel format is not correct.
+
+As per the video device output and capture video nodes, this contradicts
+the V4L2 specification even if it is treated as a warning by
+v4l2-compliance.
+
+Fix this by forcing the buffer pixel format and size to the default
+supported one. While at here, use the BIT() macro in the format
+initialization function.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Fix formats enumeration
+
+Right now a single implementation of enum_fmt() is used for all nodes
+in a group. This means that all the BE supported formats are listed for
+all the nodes. This is incorrect as the meta capture and output node
+formats should be restricted, and the meta formats should not be
+enumerated for video output and capture devices.
+
+Fix this by restricting the enumeration of META formats to the config
+and hog nodes. Split out from the list of supported_formats the
+V4L2_META_FMT_RPI_BE_CFG which is only used for the meta_out node, while
+V4L2_PIX_FMT_RPI_BE is kept in the list of supported_formats as it can
+be used as an opaque format for both meta_cap, video_cap and video_out
+nodes.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+
+media: bcm2712: Minor fixes to support PiSP regression tests
+
+Allow RGB input, not just Bayer (but only of those at once);
+Allow Wallpaper image formats. XXX They are not yet size-checked;
+Set "chicken bits" to test BURST_TRIM and AXI AWID/BID variation.
+Convert some v4l2_err() to dev_err()
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+
+drivers: media: pisp_be: Use the maximum number of config buffers
+
+Set PISP_BE_NUM_CONFIG_BUFFERS the the maximum number of possible
+buffers. In the worst case, this overallocates config buffers, but
+given their size, it's not too much of a problem.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+
+media: pisp_be: Fix extra PM runtime put
+
+vidioc_streamoff callback can be called even if vidioc_streamon was
+never called. The driver currently does PM runtime get/put in these
+callbacks, which may lead to a put without a matching get.
+
+Fix this by moving the PM runtime get/put to vb2_ops's start_streaming &
+stop_streaming, which the framework makes sure won't get extra calls.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+
+drivers: media: pisp_be: Don't report V4L2_PIX_FMT_RPI_BE format
+
+This is an internal opaque format, not to be reported in enum_fmt.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/Kconfig | 1 +
+ drivers/media/platform/Makefile | 1 +
+ drivers/media/platform/raspberrypi/Kconfig | 5 +
+ drivers/media/platform/raspberrypi/Makefile | 3 +
+ .../platform/raspberrypi/pisp_be/Kconfig | 12 +
+ .../platform/raspberrypi/pisp_be/Makefile | 6 +
+ .../platform/raspberrypi/pisp_be/pisp_be.c | 1985 +++++++++++++++++
+ .../raspberrypi/pisp_be/pisp_be_config.h | 533 +++++
+ .../raspberrypi/pisp_be/pisp_be_formats.h | 469 ++++
+ drivers/media/v4l2-core/v4l2-ioctl.c | 2 +
+ include/media/raspberrypi/pisp_common.h | 65 +
+ include/media/raspberrypi/pisp_types.h | 144 ++
+ include/uapi/linux/videodev2.h | 6 +
+ 13 files changed, 3232 insertions(+)
+ create mode 100644 drivers/media/platform/raspberrypi/Kconfig
+ create mode 100644 drivers/media/platform/raspberrypi/Makefile
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/Kconfig
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/Makefile
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
+ create mode 100644 drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+ create mode 100644 include/media/raspberrypi/pisp_common.h
+ create mode 100644 include/media/raspberrypi/pisp_types.h
+
+--- a/drivers/media/platform/Kconfig
++++ b/drivers/media/platform/Kconfig
+@@ -76,6 +76,7 @@ source "drivers/media/platform/mediatek/
+ source "drivers/media/platform/nvidia/Kconfig"
+ source "drivers/media/platform/nxp/Kconfig"
+ source "drivers/media/platform/qcom/Kconfig"
++source "drivers/media/platform/raspberrypi/Kconfig"
+ source "drivers/media/platform/renesas/Kconfig"
+ source "drivers/media/platform/rockchip/Kconfig"
+ source "drivers/media/platform/samsung/Kconfig"
+--- a/drivers/media/platform/Makefile
++++ b/drivers/media/platform/Makefile
+@@ -19,6 +19,7 @@ obj-y += mediatek/
+ obj-y += nvidia/
+ obj-y += nxp/
+ obj-y += qcom/
++obj-y += raspberrypi/
+ obj-y += renesas/
+ obj-y += rockchip/
+ obj-y += samsung/
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/Kconfig
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++comment "Raspberry Pi media platform drivers"
++
++source "drivers/media/platform/raspberrypi/pisp_be/Kconfig"
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/Makefile
+@@ -0,0 +1,3 @@
++# SPDX-License-Identifier: GPL-2.0
++
++obj-y += pisp_be/
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/Kconfig
+@@ -0,0 +1,12 @@
++config VIDEO_RASPBERRYPI_PISP_BE
++ tristate "Raspberry Pi PiSP Backend (BE) ISP driver"
++ depends on VIDEO_DEV && PM
++ select VIDEO_V4L2_SUBDEV_API
++ select MEDIA_CONTROLLER
++ select VIDEOBUF2_DMA_CONTIG
++ select V4L2_FWNODE
++ help
++ Say Y here to enable support for the PiSP Backend (BE) ISP driver.
++
++ To compile this driver as a module, choose M here. The module will be
++ called pisp-be.
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for Raspberry Pi PiSP Backend driver
++#
++pisp-be-objs := pisp_be.o
++obj-$(CONFIG_VIDEO_RASPBERRYPI_PISP_BE) += pisp-be.o
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+@@ -0,0 +1,1985 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PiSP Back End driver.
++ * Copyright (c) 2021-2022 Raspberry Pi Limited.
++ *
++ */
++#include <linux/clk.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include <media/videobuf2-dma-contig.h>
++#include <media/videobuf2-vmalloc.h>
++
++#include "pisp_be_config.h"
++#include "pisp_be_formats.h"
++
++MODULE_DESCRIPTION("PiSP Back End driver");
++MODULE_AUTHOR("David Plowman <david.plowman@raspberrypi.com>");
++MODULE_AUTHOR("Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>");
++MODULE_LICENSE("GPL v2");
++
++/* Offset to use when registering the /dev/videoX node */
++#define PISPBE_VIDEO_NODE_OFFSET 20
++
++/* Maximum number of config buffers possible */
++#define PISP_BE_NUM_CONFIG_BUFFERS VB2_MAX_FRAME
++
++/*
++ * We want to support 2 independent instances allowing 2 simultaneous users
++ * of the ISP-BE (of course they share hardware, platform resources and mutex).
++ * Each such instance comprises a group of device nodes representing input
++ * and output queues, and a media controller device node to describe them.
++ */
++#define PISPBE_NUM_NODE_GROUPS 2
++
++#define PISPBE_NAME "pispbe"
++
++/* Some ISP-BE registers */
++#define PISP_BE_VERSION_OFFSET (0x0)
++#define PISP_BE_CONTROL_OFFSET (0x4)
++#define PISP_BE_TILE_ADDR_LO_OFFSET (0x8)
++#define PISP_BE_TILE_ADDR_HI_OFFSET (0xc)
++#define PISP_BE_STATUS_OFFSET (0x10)
++#define PISP_BE_BATCH_STATUS_OFFSET (0x14)
++#define PISP_BE_INTERRUPT_EN_OFFSET (0x18)
++#define PISP_BE_INTERRUPT_STATUS_OFFSET (0x1c)
++#define PISP_BE_AXI_OFFSET (0x20)
++#define PISP_BE_CONFIG_BASE_OFFSET (0x40)
++#define PISP_BE_IO_INPUT_ADDR0_LO_OFFSET (PISP_BE_CONFIG_BASE_OFFSET)
++#define PISP_BE_GLOBAL_BAYER_ENABLE_OFFSET (PISP_BE_CONFIG_BASE_OFFSET + 0x70)
++#define PISP_BE_GLOBAL_RGB_ENABLE_OFFSET (PISP_BE_CONFIG_BASE_OFFSET + 0x74)
++#define N_HW_ADDRESSES 14
++#define N_HW_ENABLES 2
++
++#define PISP_BE_VERSION_2712C1 0x02252700
++#define PISP_BE_VERSION_MINOR_BITS 0xF
++
++/*
++ * This maps our nodes onto the inputs/outputs of the actual PiSP Back End.
++ * Be wary of the word "OUTPUT" which is used ambiguously here. In a V4L2
++ * context it means an input to the hardware (source image or metadata).
++ * Elsewhere it means an output from the hardware.
++ */
++enum node_ids {
++ MAIN_INPUT_NODE,
++ TDN_INPUT_NODE,
++ STITCH_INPUT_NODE,
++ HOG_OUTPUT_NODE,
++ OUTPUT0_NODE,
++ OUTPUT1_NODE,
++ TDN_OUTPUT_NODE,
++ STITCH_OUTPUT_NODE,
++ CONFIG_NODE,
++ PISPBE_NUM_NODES
++};
++
++struct node_description {
++ const char *ent_name;
++ enum v4l2_buf_type buf_type;
++ unsigned int caps;
++};
++
++static const struct node_description node_desc[PISPBE_NUM_NODES] = {
++ /* MAIN_INPUT_NODE */
++ {
++ .ent_name = PISPBE_NAME "-input",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
++ },
++ /* TDN_INPUT_NODE */
++ {
++ .ent_name = PISPBE_NAME "-tdn_input",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
++ },
++ /* STITCH_INPUT_NODE */
++ {
++ .ent_name = PISPBE_NAME "-stitch_input",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
++ .caps = V4L2_CAP_VIDEO_OUTPUT_MPLANE,
++ },
++ /* HOG_OUTPUT_NODE */
++ {
++ .ent_name = PISPBE_NAME "-hog_output",
++ .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
++ .caps = V4L2_CAP_META_CAPTURE,
++ },
++ /* OUTPUT0_NODE */
++ {
++ .ent_name = PISPBE_NAME "-output0",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++ },
++ /* OUTPUT1_NODE */
++ {
++ .ent_name = PISPBE_NAME "-output1",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++ },
++ /* TDN_OUTPUT_NODE */
++ {
++ .ent_name = PISPBE_NAME "-tdn_output",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++ },
++ /* STITCH_OUTPUT_NODE */
++ {
++ .ent_name = PISPBE_NAME "-stitch_output",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE,
++ },
++ /* CONFIG_NODE */
++ {
++ .ent_name = PISPBE_NAME "-config",
++ .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
++ .caps = V4L2_CAP_META_OUTPUT,
++ }
++};
++
++#define NODE_DESC_IS_OUTPUT(desc) ( \
++ ((desc)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
++ ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \
++ ((desc)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
++
++#define NODE_IS_META(node) ( \
++ ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
++ ((node)->buf_type == V4L2_BUF_TYPE_META_CAPTURE))
++#define NODE_IS_OUTPUT(node) ( \
++ ((node)->buf_type == V4L2_BUF_TYPE_META_OUTPUT) || \
++ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT) || \
++ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE))
++#define NODE_IS_CAPTURE(node) ( \
++ ((node)->buf_type == V4L2_BUF_TYPE_META_CAPTURE) || \
++ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || \
++ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
++#define NODE_IS_MPLANE(node) ( \
++ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) || \
++ ((node)->buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE))
++
++/*
++ * Structure to describe a single node /dev/video<N> which represents a single
++ * input or output queue to the PiSP Back End device.
++ */
++struct pispbe_node {
++ unsigned int id;
++ int vfl_dir;
++ enum v4l2_buf_type buf_type;
++ struct video_device vfd;
++ struct media_pad pad;
++ struct media_intf_devnode *intf_devnode;
++ struct media_link *intf_link;
++ struct pispbe_node_group *node_group;
++ struct mutex node_lock;
++ struct mutex queue_lock;
++ spinlock_t ready_lock;
++ struct list_head ready_queue;
++ struct vb2_queue queue;
++ struct v4l2_format format;
++ const struct pisp_be_format *pisp_format;
++};
++
++/* For logging only, use the entity name with "pispbe" and separator removed */
++#define NODE_NAME(node) \
++ (node_desc[(node)->id].ent_name + sizeof(PISPBE_NAME))
++#define NODE_GET_V4L2(node) ((node)->node_group->v4l2_dev)
++
++/*
++ * Node group structure, which comprises all the input and output nodes that a
++ * single PiSP client will need, along with its own v4l2 and media devices.
++ */
++struct pispbe_node_group {
++ unsigned int id;
++ struct v4l2_device v4l2_dev;
++ struct v4l2_subdev sd;
++ struct pispbe_dev *pispbe;
++ struct media_device mdev;
++ struct pispbe_node node[PISPBE_NUM_NODES];
++ u32 streaming_map; /* bitmap of which nodes are streaming */
++ struct media_pad pad[PISPBE_NUM_NODES]; /* output pads first */
++ struct pisp_be_tiles_config *config;
++ dma_addr_t config_dma_addr;
++};
++
++/* Records details of the jobs currently running or queued on the h/w. */
++struct pispbe_job {
++ struct pispbe_node_group *node_group;
++ /*
++ * An array of buffer pointers - remember it's source buffers first,
++ * then captures, then metadata last.
++ */
++ struct pispbe_buffer *buf[PISPBE_NUM_NODES];
++};
++
++/*
++ * Structure representing the entire PiSP Back End device, comprising several
++ * node groups which share platform resources and a mutex for the actual HW.
++ */
++struct pispbe_dev {
++ struct device *dev;
++ struct pispbe_node_group node_group[PISPBE_NUM_NODE_GROUPS];
++ int hw_busy; /* non-zero if a job is queued or is being started */
++ struct pispbe_job queued_job, running_job;
++ void __iomem *be_reg_base;
++ struct clk *clk;
++ int irq;
++ u32 hw_version;
++ u8 done, started;
++ spinlock_t hw_lock; /* protects "hw_busy" flag and streaming_map */
++};
++
++static inline u32 read_reg(struct pispbe_dev *pispbe, unsigned int offset)
++{
++ return readl(pispbe->be_reg_base + offset);
++}
++
++static inline void write_reg(struct pispbe_dev *pispbe, unsigned int offset,
++ u32 val)
++{
++ writel(val, pispbe->be_reg_base + offset);
++}
++
++/* Check and initialize hardware. */
++static int hw_init(struct pispbe_dev *pispbe)
++{
++ u32 u;
++
++ /* Check the HW is present and has a known version */
++ u = read_reg(pispbe, PISP_BE_VERSION_OFFSET);
++ dev_info(pispbe->dev, "pispbe_probe: HW version: 0x%08x", u);
++ pispbe->hw_version = u;
++ if ((u & ~PISP_BE_VERSION_MINOR_BITS) != PISP_BE_VERSION_2712C1)
++ return -ENODEV;
++
++ /* Clear leftover interrupts */
++ write_reg(pispbe, PISP_BE_INTERRUPT_STATUS_OFFSET, 0xFFFFFFFFu);
++ u = read_reg(pispbe, PISP_BE_BATCH_STATUS_OFFSET);
++ dev_info(pispbe->dev, "pispbe_probe: BatchStatus: 0x%08x", u);
++ pispbe->done = (uint8_t)u;
++ pispbe->started = (uint8_t)(u >> 8);
++ u = read_reg(pispbe, PISP_BE_STATUS_OFFSET);
++ dev_info(pispbe->dev, "pispbe_probe: Status: 0x%08x", u);
++ if (u != 0 || pispbe->done != pispbe->started) {
++ dev_err(pispbe->dev, "pispbe_probe: HW is stuck or busy\n");
++ return -EBUSY;
++ }
++ /*
++ * AXI QOS=0, CACHE=4'b0010, PROT=3'b011
++ * Also set "chicken bits" 22:20 which enable sub-64-byte bursts
++ * and AXI AWID/BID variability (on versions which support this).
++ */
++ write_reg(pispbe, PISP_BE_AXI_OFFSET, 0x32703200u);
++
++ /* Enable both interrupt flags */
++ write_reg(pispbe, PISP_BE_INTERRUPT_EN_OFFSET, 0x00000003u);
++ return 0;
++}
++
++/*
++ * Queue a job to the h/w. If the h/w is idle it will begin immediately.
++ * Caller must ensure it is "safe to queue", i.e. we don't already have a
++ * queued, unstarted job.
++ */
++static void hw_queue_job(struct pispbe_dev *pispbe,
++ dma_addr_t hw_dma_addrs[N_HW_ADDRESSES],
++ u32 hw_enables[N_HW_ENABLES],
++ struct pisp_be_config *config, dma_addr_t tiles,
++ unsigned int num_tiles)
++{
++ unsigned int begin, end;
++ unsigned int u;
++
++ if (read_reg(pispbe, PISP_BE_STATUS_OFFSET) & 1)
++ dev_err(pispbe->dev, "ERROR: not safe to queue new job!\n");
++
++ /*
++ * Write configuration to hardware. DMA addresses and enable flags
++ * are passed separately, because the driver needs to sanitize them,
++ * and we don't want to modify (or be vulnerable to modifications of)
++ * the mmap'd buffer.
++ */
++ for (u = 0; u < N_HW_ADDRESSES; ++u) {
++ write_reg(pispbe, PISP_BE_IO_INPUT_ADDR0_LO_OFFSET + 8 * u,
++ (u32)(hw_dma_addrs[u]));
++ write_reg(pispbe, PISP_BE_IO_INPUT_ADDR0_LO_OFFSET + 8 * u + 4,
++ (u32)(hw_dma_addrs[u] >> 32));
++ }
++ write_reg(pispbe, PISP_BE_GLOBAL_BAYER_ENABLE_OFFSET, hw_enables[0]);
++ write_reg(pispbe, PISP_BE_GLOBAL_RGB_ENABLE_OFFSET, hw_enables[1]);
++
++ /*
++ * Everything else is as supplied by the user. XXX Buffer sizes not
++ * checked!
++ */
++ begin = offsetof(struct pisp_be_config, global.bayer_order) /
++ sizeof(u32);
++ end = offsetof(struct pisp_be_config, axi) / sizeof(u32);
++ for (u = begin; u < end; u++) {
++ unsigned int val = ((u32 *)config)[u];
++
++ write_reg(pispbe, PISP_BE_CONFIG_BASE_OFFSET + 4 * u, val);
++ }
++
++ /* Read back the addresses -- an error here could be fatal */
++ for (u = 0; u < N_HW_ADDRESSES; ++u) {
++ unsigned int offset = PISP_BE_IO_INPUT_ADDR0_LO_OFFSET + 8 * u;
++ u64 along = read_reg(pispbe, offset);
++
++ along += ((u64)read_reg(pispbe, offset + 4)) << 32;
++ if (along != (u64)(hw_dma_addrs[u])) {
++ dev_err(pispbe->dev,
++ "ISP BE config error: check if ISP RAMs enabled?\n");
++ return;
++ }
++ }
++
++ /*
++ * Write tile pointer to hardware. XXX Tile offsets and sizes not
++ * checked (and even if checked, the user could subsequently modify
++ * them)!
++ */
++ write_reg(pispbe, PISP_BE_TILE_ADDR_LO_OFFSET, (u32)tiles);
++ write_reg(pispbe, PISP_BE_TILE_ADDR_HI_OFFSET, (u32)(tiles >> 32));
++
++ /* Enqueue the job */
++ write_reg(pispbe, PISP_BE_CONTROL_OFFSET, 3 + 65536 * num_tiles);
++}
++
++struct pispbe_buffer {
++ struct vb2_v4l2_buffer vb;
++ struct list_head ready_list;
++ unsigned int config_index;
++};
++
++static int get_addr_3(dma_addr_t addr[3], struct pispbe_buffer *buf,
++ struct pispbe_node *node)
++{
++ unsigned int num_planes = node->format.fmt.pix_mp.num_planes;
++ unsigned int plane_factor = 0;
++ unsigned int size;
++ unsigned int p;
++
++ if (!buf || !node->pisp_format)
++ return 0;
++
++ WARN_ON(!NODE_IS_MPLANE(node));
++
++ /*
++ * Determine the base plane size. This will not be the same
++ * as node->format.fmt.pix_mp.plane_fmt[0].sizeimage for a single
++ * plane buffer in an mplane format.
++ */
++ size = node->format.fmt.pix_mp.plane_fmt[0].bytesperline *
++ node->format.fmt.pix_mp.height;
++
++ for (p = 0; p < num_planes && p < 3; p++) {
++ addr[p] = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, p);
++ plane_factor += node->pisp_format->plane_factor[p];
++ }
++
++ for (; p < MAX_PLANES && node->pisp_format->plane_factor[p]; p++) {
++ /*
++ * Calculate the address offset of this plane as needed
++ * by the hardware. This is specifically for non-mplane
++ * buffer formats, where there are 3 image planes, e.g.
++ * for the V4L2_PIX_FMT_YUV420 format.
++ */
++ addr[p] = addr[0] + ((size * plane_factor) >> 3);
++ plane_factor += node->pisp_format->plane_factor[p];
++ }
++
++ return num_planes;
++}
++
++static dma_addr_t get_addr(struct pispbe_buffer *buf)
++{
++ if (buf)
++ return vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ return 0;
++}
++
++static void
++fixup_addrs_enables(dma_addr_t addrs[N_HW_ADDRESSES],
++ u32 hw_enables[N_HW_ENABLES],
++ struct pisp_be_tiles_config *config,
++ struct pispbe_buffer *buf[PISPBE_NUM_NODES],
++ struct pispbe_node_group *node_group)
++{
++ int ret, i;
++
++ /* Take a copy of the "enable" bitmaps so we can modify them. */
++ hw_enables[0] = config->config.global.bayer_enables;
++ hw_enables[1] = config->config.global.rgb_enables;
++
++ /*
++ * Main input first. There are 3 address pointers, corresponding to up
++ * to 3 planes.
++ */
++ ret = get_addr_3(addrs, buf[MAIN_INPUT_NODE],
++ &node_group->node[MAIN_INPUT_NODE]);
++ if (ret <= 0) {
++ /*
++ * This shouldn't happen; pispbe_schedule_internal should insist
++ * on an input.
++ */
++ dev_warn(node_group->pispbe->dev,
++ "ISP-BE missing input\n");
++ hw_enables[0] = 0;
++ hw_enables[1] = 0;
++ return;
++ }
++
++ /*
++ * Now TDN/Stitch inputs and outputs. These are single-plane and only
++ * used with Bayer input. Input enables must match the requirements
++ * of the processing stages, otherwise the hardware can lock up!
++ */
++ if (hw_enables[0] & PISP_BE_BAYER_ENABLE_INPUT) {
++ addrs[3] = get_addr(buf[TDN_INPUT_NODE]);
++ if (addrs[3] == 0 ||
++ !(hw_enables[0] & PISP_BE_BAYER_ENABLE_TDN_INPUT) ||
++ !(hw_enables[0] & PISP_BE_BAYER_ENABLE_TDN) ||
++ (config->config.tdn.reset & 1)) {
++ hw_enables[0] &= ~(PISP_BE_BAYER_ENABLE_TDN_INPUT |
++ PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS);
++ if (!(config->config.tdn.reset & 1))
++ hw_enables[0] &= ~PISP_BE_BAYER_ENABLE_TDN;
++ }
++
++ addrs[4] = get_addr(buf[STITCH_INPUT_NODE]);
++ if (addrs[4] == 0 ||
++ !(hw_enables[0] & PISP_BE_BAYER_ENABLE_STITCH_INPUT) ||
++ !(hw_enables[0] & PISP_BE_BAYER_ENABLE_STITCH)) {
++ hw_enables[0] &=
++ ~(PISP_BE_BAYER_ENABLE_STITCH_INPUT |
++ PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS |
++ PISP_BE_BAYER_ENABLE_STITCH);
++ }
++
++ addrs[5] = get_addr(buf[TDN_OUTPUT_NODE]);
++ if (addrs[5] == 0)
++ hw_enables[0] &= ~(PISP_BE_BAYER_ENABLE_TDN_COMPRESS |
++ PISP_BE_BAYER_ENABLE_TDN_OUTPUT);
++
++ addrs[6] = get_addr(buf[STITCH_OUTPUT_NODE]);
++ if (addrs[6] == 0)
++ hw_enables[0] &=
++ ~(PISP_BE_BAYER_ENABLE_STITCH_COMPRESS |
++ PISP_BE_BAYER_ENABLE_STITCH_OUTPUT);
++ } else {
++ /* No Bayer input? Disable entire Bayer pipe (else lockup) */
++ hw_enables[0] = 0;
++ }
++
++ /* Main image output channels. */
++ for (i = 0; i < PISP_BACK_END_NUM_OUTPUTS; i++) {
++ ret = get_addr_3(addrs + 7 + 3 * i, buf[OUTPUT0_NODE + i],
++ &node_group->node[OUTPUT0_NODE + i]);
++ if (ret <= 0)
++ hw_enables[1] &= ~(PISP_BE_RGB_ENABLE_OUTPUT0 << i);
++ }
++
++ /* HoG output (always single plane). */
++ addrs[13] = get_addr(buf[HOG_OUTPUT_NODE]);
++ if (addrs[13] == 0)
++ hw_enables[1] &= ~PISP_BE_RGB_ENABLE_HOG;
++}
++
++/*
++ * Internal function. Called from pispbe_schedule_one/all. Returns non-zero if
++ * we started a job.
++ *
++ * Warning: needs to be called with hw_lock taken, and releases it if it
++ * schedules a job.
++ */
++static int pispbe_schedule_internal(struct pispbe_node_group *node_group,
++ unsigned long flags)
++{
++ struct pisp_be_tiles_config *config_tiles_buffer;
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ struct pispbe_buffer *buf[PISPBE_NUM_NODES];
++ dma_addr_t hw_dma_addrs[N_HW_ADDRESSES];
++ dma_addr_t tiles;
++ u32 hw_enables[N_HW_ENABLES];
++ struct pispbe_node *node;
++ unsigned long flags1;
++ unsigned int config_index;
++ int i;
++
++ /*
++ * To schedule a job, we need all streaming nodes (apart from Output0,
++ * Output1, Tdn and Stitch) to have a buffer ready, which must
++ * include at least a config buffer and a main input image.
++ *
++ * For Output0, Output1, Tdn and Stitch, a buffer only needs to be
++ * available if the blocks are enabled in the config.
++ *
++ * (Note that streaming_map is protected by hw_lock, which is held.)
++ */
++ if (((BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE)) &
++ node_group->streaming_map) !=
++ (BIT(CONFIG_NODE) | BIT(MAIN_INPUT_NODE))) {
++ dev_dbg(pispbe->dev, "Nothing to do\n");
++ return 0;
++ }
++
++ node = &node_group->node[CONFIG_NODE];
++ spin_lock_irqsave(&node->ready_lock, flags1);
++ buf[CONFIG_NODE] =
++ list_first_entry_or_null(&node->ready_queue, struct pispbe_buffer,
++ ready_list);
++ spin_unlock_irqrestore(&node->ready_lock, flags1);
++
++ /* Exit early if no config buffer has been queued. */
++ if (!buf[CONFIG_NODE])
++ return 0;
++
++ config_index = buf[CONFIG_NODE]->vb.vb2_buf.index;
++ config_tiles_buffer = &node_group->config[config_index];
++ tiles = (dma_addr_t)node_group->config_dma_addr +
++ config_index * sizeof(struct pisp_be_tiles_config) +
++ offsetof(struct pisp_be_tiles_config, tiles);
++
++ /* remember: srcimages, captures then metadata */
++ for (i = 0; i < PISPBE_NUM_NODES; i++) {
++ unsigned int bayer_en =
++ config_tiles_buffer->config.global.bayer_enables;
++ unsigned int rgb_en =
++ config_tiles_buffer->config.global.rgb_enables;
++ bool ignore_buffers = false;
++
++ /* Config node is handled outside the loop above. */
++ if (i == CONFIG_NODE)
++ continue;
++
++ buf[i] = NULL;
++ if (!(node_group->streaming_map & BIT(i)))
++ continue;
++
++ if ((!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT0) &&
++ i == OUTPUT0_NODE) ||
++ (!(rgb_en & PISP_BE_RGB_ENABLE_OUTPUT1) &&
++ i == OUTPUT1_NODE) ||
++ (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_INPUT) &&
++ i == TDN_INPUT_NODE) ||
++ (!(bayer_en & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) &&
++ i == TDN_OUTPUT_NODE) ||
++ (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_INPUT) &&
++ i == STITCH_INPUT_NODE) ||
++ (!(bayer_en & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) &&
++ i == STITCH_OUTPUT_NODE)) {
++ /*
++ * Ignore Output0/Output1/Tdn/Stitch buffer check if the
++ * global enables aren't set for these blocks. If a
++ * buffer has been provided, we dequeue it back to the
++ * user with the other in-use buffers.
++ *
++ */
++ ignore_buffers = true;
++ }
++
++ node = &node_group->node[i];
++
++ spin_lock_irqsave(&node->ready_lock, flags1);
++ buf[i] = list_first_entry_or_null(&node->ready_queue,
++ struct pispbe_buffer,
++ ready_list);
++ spin_unlock_irqrestore(&node->ready_lock, flags1);
++ if (!buf[i] && !ignore_buffers) {
++ dev_dbg(pispbe->dev, "Nothing to do\n");
++ return 0;
++ }
++ }
++
++ /* Pull a buffer from each V4L2 queue to form the queued job */
++ for (i = 0; i < PISPBE_NUM_NODES; i++) {
++ if (buf[i]) {
++ node = &node_group->node[i];
++
++ spin_lock_irqsave(&node->ready_lock, flags1);
++ list_del(&buf[i]->ready_list);
++ spin_unlock_irqrestore(&node->ready_lock,
++ flags1);
++ }
++ pispbe->queued_job.buf[i] = buf[i];
++ }
++
++ pispbe->queued_job.node_group = node_group;
++ pispbe->hw_busy = 1;
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++ /*
++ * We can kick the job off without the hw_lock, as this can
++ * never run again until hw_busy is cleared, which will happen
++ * only when the following job has been queued.
++ */
++ dev_dbg(pispbe->dev, "Have buffers - starting hardware\n");
++
++ /* Convert buffers to DMA addresses for the hardware */
++ fixup_addrs_enables(hw_dma_addrs, hw_enables,
++ config_tiles_buffer, buf, node_group);
++ /*
++ * This could be a spot to fill in the
++ * buf[i]->vb.vb2_buf.planes[j].bytesused fields?
++ */
++ i = config_tiles_buffer->num_tiles;
++ if (i <= 0 || i > PISP_BACK_END_NUM_TILES ||
++ !((hw_enables[0] | hw_enables[1]) &
++ PISP_BE_BAYER_ENABLE_INPUT)) {
++ /*
++ * Bad job. We can't let it proceed as it could lock up
++ * the hardware, or worse!
++ *
++ * XXX How to deal with this most cleanly? For now, just
++ * force num_tiles to 0, which causes the H/W to do
++ * something bizarre but survivable. It increments
++ * (started,done) counters by more than 1, but we seem
++ * to survive...
++ */
++ dev_err(pispbe->dev, "PROBLEM: Bad job");
++ i = 0;
++ }
++ hw_queue_job(pispbe, hw_dma_addrs, hw_enables,
++ &config_tiles_buffer->config, tiles, i);
++
++ return 1;
++}
++
++/* Try and schedule a job for just a single node group. */
++static void pispbe_schedule_one(struct pispbe_node_group *node_group)
++{
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&pispbe->hw_lock, flags);
++ if (pispbe->hw_busy) {
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++ return;
++ }
++
++ /* A non-zero return means the lock was released. */
++ ret = pispbe_schedule_internal(node_group, flags);
++ if (!ret)
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++}
++
++/* Try and schedule a job for any of the node groups. */
++static void pispbe_schedule_any(struct pispbe_dev *pispbe, int clear_hw_busy)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&pispbe->hw_lock, flags);
++
++ if (clear_hw_busy)
++ pispbe->hw_busy = 0;
++ if (pispbe->hw_busy == 0) {
++ unsigned int i;
++
++ for (i = 0; i < PISPBE_NUM_NODE_GROUPS; i++) {
++ /*
++ * A non-zero return from pispbe_schedule_internal means
++ * the lock was released.
++ */
++ if (pispbe_schedule_internal(&pispbe->node_group[i],
++ flags))
++ return;
++ }
++ }
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++}
++
++static void pispbe_isr_jobdone(struct pispbe_dev *pispbe,
++ struct pispbe_job *job)
++{
++ struct pispbe_buffer **buf = job->buf;
++ u64 ts = ktime_get_ns();
++ int i;
++
++ for (i = 0; i < PISPBE_NUM_NODES; i++) {
++ if (buf[i]) {
++ buf[i]->vb.vb2_buf.timestamp = ts;
++ vb2_buffer_done(&buf[i]->vb.vb2_buf,
++ VB2_BUF_STATE_DONE);
++ }
++ }
++}
++
++static irqreturn_t pispbe_isr(int irq, void *dev)
++{
++ struct pispbe_dev *pispbe = (struct pispbe_dev *)dev;
++ u8 started, done;
++ int can_queue_another = 0;
++ u32 u;
++
++ u = read_reg(pispbe, PISP_BE_INTERRUPT_STATUS_OFFSET);
++ if (u == 0)
++ return IRQ_NONE;
++
++ write_reg(pispbe, PISP_BE_INTERRUPT_STATUS_OFFSET, u);
++ dev_dbg(pispbe->dev, "Hardware interrupt\n");
++ u = read_reg(pispbe, PISP_BE_BATCH_STATUS_OFFSET);
++ done = (uint8_t)u;
++ started = (uint8_t)(u >> 8);
++ dev_dbg(pispbe->dev,
++ "H/W started %d done %d, previously started %d done %d\n",
++ (int)started, (int)done, (int)pispbe->started,
++ (int)pispbe->done);
++
++ /*
++ * Be aware that done can go up by 2 and started by 1 when: a job that
++ * we previously saw "start" now finishes, and we then queued a new job
++ * which we see both start and finish "simultaneously".
++ */
++ if (pispbe->running_job.node_group && pispbe->done != done) {
++ pispbe_isr_jobdone(pispbe, &pispbe->running_job);
++ memset(&pispbe->running_job, 0, sizeof(pispbe->running_job));
++ pispbe->done++;
++ dev_dbg(pispbe->dev, "Job done (1)\n");
++ }
++
++ if (pispbe->started != started) {
++ pispbe->started++;
++ can_queue_another = 1;
++ dev_dbg(pispbe->dev, "Job started\n");
++
++ if (pispbe->done != done && pispbe->queued_job.node_group) {
++ pispbe_isr_jobdone(pispbe, &pispbe->queued_job);
++ pispbe->done++;
++ dev_dbg(pispbe->dev, "Job done (2)\n");
++ } else {
++ pispbe->running_job = pispbe->queued_job;
++ }
++
++ memset(&pispbe->queued_job, 0, sizeof(pispbe->queued_job));
++ }
++
++ if (pispbe->done != done || pispbe->started != started) {
++ dev_err(pispbe->dev, "PROBLEM: counters not matching!\n");
++ pispbe->started = started;
++ pispbe->done = done;
++ }
++
++ /* check if there's more to do before going to sleep */
++ pispbe_schedule_any(pispbe, can_queue_another);
++
++ return IRQ_HANDLED;
++}
++
++static int pisp_be_validate_config(struct pispbe_node_group *node_group,
++ struct pisp_be_tiles_config *config)
++{
++ u32 bayer_enables = config->config.global.bayer_enables;
++ u32 rgb_enables = config->config.global.rgb_enables;
++ struct device *dev = node_group->pispbe->dev;
++ struct v4l2_format *fmt;
++ unsigned int bpl, size, i, j;
++
++ if (!(bayer_enables & PISP_BE_BAYER_ENABLE_INPUT) ==
++ !(rgb_enables & PISP_BE_RGB_ENABLE_INPUT)) {
++ dev_err(dev, "%s: Not one input enabled\n", __func__);
++ return -EIO;
++ }
++
++ /* Ensure output config strides and buffer sizes match the V4L2 formats. */
++ fmt = &node_group->node[TDN_OUTPUT_NODE].format;
++ if (bayer_enables & PISP_BE_BAYER_ENABLE_TDN_OUTPUT) {
++ bpl = config->config.tdn_output_format.stride;
++ size = bpl * config->config.tdn_output_format.height;
++ if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) {
++ dev_err(dev, "%s: bpl mismatch on tdn_output\n",
++ __func__);
++ return -EINVAL;
++ }
++ if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) {
++ dev_err(dev, "%s: size mismatch on tdn_output\n",
++ __func__);
++ return -EINVAL;
++ }
++ }
++
++ fmt = &node_group->node[STITCH_OUTPUT_NODE].format;
++ if (bayer_enables & PISP_BE_BAYER_ENABLE_STITCH_OUTPUT) {
++ bpl = config->config.stitch_output_format.stride;
++ size = bpl * config->config.stitch_output_format.height;
++ if (fmt->fmt.pix_mp.plane_fmt[0].bytesperline < bpl) {
++ dev_err(dev, "%s: bpl mismatch on stitch_output\n",
++ __func__);
++ return -EINVAL;
++ }
++ if (fmt->fmt.pix_mp.plane_fmt[0].sizeimage < size) {
++ dev_err(dev, "%s: size mismatch on stitch_output\n",
++ __func__);
++ return -EINVAL;
++ }
++ }
++
++ for (j = 0; j < PISP_BACK_END_NUM_OUTPUTS; j++) {
++ if (!(rgb_enables & PISP_BE_RGB_ENABLE_OUTPUT(j)))
++ continue;
++ if (config->config.output_format[j].image.format &
++ PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
++ continue; /* TODO: Size checks for wallpaper formats */
++
++ fmt = &node_group->node[OUTPUT0_NODE + j].format;
++ for (i = 0; i < fmt->fmt.pix_mp.num_planes; i++) {
++ bpl = !i ? config->config.output_format[j].image.stride
++ : config->config.output_format[j].image.stride2;
++ size = bpl * config->config.output_format[j].image.height;
++
++ if (config->config.output_format[j].image.format &
++ PISP_IMAGE_FORMAT_SAMPLING_420)
++ size >>= 1;
++ if (fmt->fmt.pix_mp.plane_fmt[i].bytesperline < bpl) {
++ dev_err(dev, "%s: bpl mismatch on output %d\n",
++ __func__, j);
++ return -EINVAL;
++ }
++ if (fmt->fmt.pix_mp.plane_fmt[i].sizeimage < size) {
++ dev_err(dev, "%s: size mismatch on output\n",
++ __func__);
++ return -EINVAL;
++ }
++ }
++ }
++
++ return 0;
++}
++
++static int pispbe_node_queue_setup(struct vb2_queue *q, unsigned int *nbuffers,
++ unsigned int *nplanes, unsigned int sizes[],
++ struct device *alloc_devs[])
++{
++ struct pispbe_node *node = vb2_get_drv_priv(q);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ *nplanes = 1;
++ if (NODE_IS_MPLANE(node)) {
++ unsigned int i;
++
++ *nplanes = node->format.fmt.pix_mp.num_planes;
++ for (i = 0; i < *nplanes; i++) {
++ unsigned int size =
++ node->format.fmt.pix_mp.plane_fmt[i].sizeimage;
++ if (sizes[i] && sizes[i] < size) {
++ dev_err(pispbe->dev, "%s: size %u < %u\n",
++ __func__, sizes[i], size);
++ return -EINVAL;
++ }
++ sizes[i] = size;
++ }
++ } else if (NODE_IS_META(node)) {
++ sizes[0] = node->format.fmt.meta.buffersize;
++ /*
++ * Limit the config node buffer count to the number of internal
++ * buffers allocated.
++ */
++ if (node->id == CONFIG_NODE)
++ *nbuffers = min_t(unsigned int, *nbuffers,
++ PISP_BE_NUM_CONFIG_BUFFERS);
++ }
++
++ dev_dbg(pispbe->dev,
++ "Image (or metadata) size %u, nbuffers %u for node %s\n",
++ sizes[0], *nbuffers, NODE_NAME(node));
++
++ return 0;
++}
++
++static int pispbe_node_buffer_prepare(struct vb2_buffer *vb)
++{
++ struct pispbe_node *node = vb2_get_drv_priv(vb->vb2_queue);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ unsigned long size = 0;
++ unsigned int num_planes = NODE_IS_MPLANE(node) ?
++ node->format.fmt.pix_mp.num_planes : 1;
++ unsigned int i;
++
++ for (i = 0; i < num_planes; i++) {
++ size = NODE_IS_MPLANE(node)
++ ? node->format.fmt.pix_mp.plane_fmt[i].sizeimage
++ : node->format.fmt.meta.buffersize;
++
++ if (vb2_plane_size(vb, i) < size) {
++ dev_err(pispbe->dev,
++ "data will not fit into plane %d (%lu < %lu)\n",
++ i, vb2_plane_size(vb, i), size);
++ return -EINVAL;
++ }
++
++ vb2_set_plane_payload(vb, i, size);
++ }
++
++ if (node->id == CONFIG_NODE) {
++ void *dst = &node->node_group->config[vb->index];
++ void *src = vb2_plane_vaddr(vb, 0);
++
++ memcpy(dst, src, sizeof(struct pisp_be_tiles_config));
++ return pisp_be_validate_config(node->node_group, dst);
++ }
++
++ return 0;
++}
++
++static void pispbe_node_buffer_queue(struct vb2_buffer *buf)
++{
++ struct vb2_v4l2_buffer *vbuf =
++ container_of(buf, struct vb2_v4l2_buffer, vb2_buf);
++ struct pispbe_buffer *buffer =
++ container_of(vbuf, struct pispbe_buffer, vb);
++ struct pispbe_node *node = vb2_get_drv_priv(buf->vb2_queue);
++ struct pispbe_node_group *node_group = node->node_group;
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ unsigned long flags;
++
++ dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
++ spin_lock_irqsave(&node->ready_lock, flags);
++ list_add_tail(&buffer->ready_list, &node->ready_queue);
++ spin_unlock_irqrestore(&node->ready_lock, flags);
++
++ /*
++ * Every time we add a buffer, check if there's now some work for the hw
++ * to do, but only for this client.
++ */
++ pispbe_schedule_one(node_group);
++}
++
++static int pispbe_node_start_streaming(struct vb2_queue *q, unsigned int count)
++{
++ unsigned long flags;
++ struct pispbe_node *node = vb2_get_drv_priv(q);
++ struct pispbe_node_group *node_group = node->node_group;
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ int ret;
++
++ ret = pm_runtime_resume_and_get(pispbe->dev);
++ if (ret < 0)
++ return ret;
++
++ spin_lock_irqsave(&pispbe->hw_lock, flags);
++ node->node_group->streaming_map |= BIT(node->id);
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++ dev_dbg(pispbe->dev, "%s: for node %s (count %u)\n",
++ __func__, NODE_NAME(node), count);
++ dev_dbg(pispbe->dev, "Nodes streaming for this group now 0x%x\n",
++ node->node_group->streaming_map);
++
++ /* Maybe we're ready to run. */
++ pispbe_schedule_one(node_group);
++
++ return 0;
++}
++
++static void pispbe_node_stop_streaming(struct vb2_queue *q)
++{
++ struct pispbe_node *node = vb2_get_drv_priv(q);
++ struct pispbe_node_group *node_group = node->node_group;
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ struct pispbe_buffer *buf;
++ unsigned long flags;
++
++ /*
++ * Now this is a bit awkward. In a simple M2M device we could just wait
++ * for all queued jobs to complete, but here there's a risk that a
++ * partial set of buffers was queued and cannot be run. For now, just
++ * cancel all buffers stuck in the "ready queue", then wait for any
++ * running job.
++ * XXX This may return buffers out of order.
++ */
++ dev_dbg(pispbe->dev, "%s: for node %s\n", __func__, NODE_NAME(node));
++ spin_lock_irqsave(&pispbe->hw_lock, flags);
++ do {
++ unsigned long flags1;
++
++ spin_lock_irqsave(&node->ready_lock, flags1);
++ buf = list_first_entry_or_null(&node->ready_queue,
++ struct pispbe_buffer,
++ ready_list);
++ if (buf) {
++ list_del(&buf->ready_list);
++ vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
++ }
++ spin_unlock_irqrestore(&node->ready_lock, flags1);
++ } while (buf);
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++ vb2_wait_for_all_buffers(&node->queue);
++
++ spin_lock_irqsave(&pispbe->hw_lock, flags);
++ node_group->streaming_map &= ~BIT(node->id);
++ spin_unlock_irqrestore(&pispbe->hw_lock, flags);
++
++ pm_runtime_mark_last_busy(pispbe->dev);
++ pm_runtime_put_autosuspend(pispbe->dev);
++
++ dev_dbg(pispbe->dev, "Nodes streaming for this group now 0x%x\n",
++ node_group->streaming_map);
++}
++
++static const struct vb2_ops pispbe_node_queue_ops = {
++ .queue_setup = pispbe_node_queue_setup,
++ .buf_prepare = pispbe_node_buffer_prepare,
++ .buf_queue = pispbe_node_buffer_queue,
++ .start_streaming = pispbe_node_start_streaming,
++ .stop_streaming = pispbe_node_stop_streaming,
++};
++
++static const struct v4l2_file_operations pispbe_fops = {
++ .owner = THIS_MODULE,
++ .open = v4l2_fh_open,
++ .release = vb2_fop_release,
++ .poll = vb2_fop_poll,
++ .unlocked_ioctl = video_ioctl2,
++ .mmap = vb2_fop_mmap
++};
++
++static int pispbe_node_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ strscpy(cap->driver, PISPBE_NAME, sizeof(cap->driver));
++ strscpy(cap->card, PISPBE_NAME, sizeof(cap->card));
++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++ dev_name(pispbe->dev));
++
++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
++ V4L2_CAP_VIDEO_OUTPUT_MPLANE |
++ V4L2_CAP_STREAMING | V4L2_CAP_DEVICE_CAPS |
++ V4L2_CAP_META_OUTPUT | V4L2_CAP_META_CAPTURE;
++ cap->device_caps = node->vfd.device_caps;
++
++ dev_dbg(pispbe->dev, "Caps for node %s: %x and %x (dev %x)\n",
++ NODE_NAME(node), cap->capabilities, cap->device_caps,
++ node->vfd.device_caps);
++ return 0;
++}
++
++static int pispbe_node_g_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
++ dev_err(pispbe->dev,
++ "Cannot get capture fmt for output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++ *f = node->format;
++ dev_dbg(pispbe->dev, "Get capture format for node %s\n",
++ NODE_NAME(node));
++ return 0;
++}
++
++static int pispbe_node_g_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
++ dev_err(pispbe->dev,
++ "Cannot get capture fmt for output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++ *f = node->format;
++ dev_dbg(pispbe->dev, "Get output format for node %s\n",
++ NODE_NAME(node));
++ return 0;
++}
++
++static int pispbe_node_g_fmt_meta_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) {
++ dev_err(pispbe->dev,
++ "Cannot get capture fmt for meta output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++ *f = node->format;
++ dev_dbg(pispbe->dev, "Get output format for meta node %s\n",
++ NODE_NAME(node));
++ return 0;
++}
++
++static int pispbe_node_g_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (!NODE_IS_META(node) || NODE_IS_OUTPUT(node)) {
++ dev_err(pispbe->dev,
++ "Cannot get capture fmt for meta output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++ *f = node->format;
++ dev_dbg(pispbe->dev, "Get output format for meta node %s\n",
++ NODE_NAME(node));
++ return 0;
++}
++
++static int verify_be_pix_format(const struct v4l2_format *f,
++ struct pispbe_node *node)
++{
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ unsigned int nplanes = f->fmt.pix_mp.num_planes;
++ unsigned int i;
++
++ if (f->fmt.pix_mp.width == 0 || f->fmt.pix_mp.height == 0) {
++ dev_err(pispbe->dev, "Details incorrect for output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++
++ if (nplanes == 0 || nplanes > MAX_PLANES) {
++ dev_err(pispbe->dev,
++ "Bad number of planes for output node %s, req =%d\n",
++ NODE_NAME(node), nplanes);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < nplanes; i++) {
++ const struct v4l2_plane_pix_format *p;
++
++ p = &f->fmt.pix_mp.plane_fmt[i];
++ if (p->bytesperline == 0 || p->sizeimage == 0) {
++ dev_err(pispbe->dev,
++ "Invalid plane %d for output node %s\n",
++ i, NODE_NAME(node));
++ return -EINVAL;
++ }
++ }
++
++ return 0;
++}
++
++static const struct pisp_be_format *find_format(unsigned int fourcc)
++{
++ const struct pisp_be_format *fmt;
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(supported_formats); i++) {
++ fmt = &supported_formats[i];
++ if (fmt->fourcc == fourcc)
++ return fmt;
++ }
++
++ return NULL;
++}
++
++static void set_plane_params(struct v4l2_format *f,
++ const struct pisp_be_format *fmt)
++{
++ unsigned int nplanes = f->fmt.pix_mp.num_planes;
++ unsigned int total_plane_factor = 0;
++ unsigned int i;
++
++ for (i = 0; i < MAX_PLANES; i++)
++ total_plane_factor += fmt->plane_factor[i];
++
++ for (i = 0; i < nplanes; i++) {
++ struct v4l2_plane_pix_format *p = &f->fmt.pix_mp.plane_fmt[i];
++ unsigned int bpl, plane_size;
++
++ bpl = (f->fmt.pix_mp.width * fmt->bit_depth) >> 3;
++ bpl = ALIGN(max(p->bytesperline, bpl), fmt->align);
++
++ plane_size = bpl * f->fmt.pix_mp.height *
++ (nplanes > 1 ? fmt->plane_factor[i] : total_plane_factor);
++ /*
++ * The shift is to divide out the plane_factor fixed point
++ * scaling of 8.
++ */
++ plane_size = max(p->sizeimage, plane_size >> 3);
++
++ p->bytesperline = bpl;
++ p->sizeimage = plane_size;
++ }
++}
++
++static int try_format(struct v4l2_format *f, struct pispbe_node *node)
++{
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ const struct pisp_be_format *fmt;
++ unsigned int i;
++ bool is_rgb;
++ u32 pixfmt = f->fmt.pix_mp.pixelformat;
++
++ dev_dbg(pispbe->dev,
++ "%s: [%s] req %ux%u " V4L2_FOURCC_CONV ", planes %d\n",
++ __func__, NODE_NAME(node), f->fmt.pix_mp.width,
++ f->fmt.pix_mp.height, V4L2_FOURCC_CONV_ARGS(pixfmt),
++ f->fmt.pix_mp.num_planes);
++
++ if (pixfmt == V4L2_PIX_FMT_RPI_BE)
++ return verify_be_pix_format(f, node);
++
++ fmt = find_format(pixfmt);
++ if (!fmt)
++ fmt = find_format(V4L2_PIX_FMT_YUV420M);
++
++ f->fmt.pix_mp.pixelformat = fmt->fourcc;
++ f->fmt.pix_mp.num_planes = fmt->num_planes;
++ f->fmt.pix_mp.field = V4L2_FIELD_NONE;
++ f->fmt.pix_mp.width = max(min(f->fmt.pix_mp.width, 65536u),
++ PISP_BACK_END_MIN_TILE_WIDTH);
++ f->fmt.pix_mp.height = max(min(f->fmt.pix_mp.height, 65536u),
++ PISP_BACK_END_MIN_TILE_HEIGHT);
++
++ /*
++ * Fill in the actual colour space when the requested one was
++ * not supported. This also catches the case when the "default"
++ * colour space was requested (as that's never in the mask).
++ */
++ if (!(V4L2_COLORSPACE_MASK(f->fmt.pix_mp.colorspace) & fmt->colorspace_mask))
++ f->fmt.pix_mp.colorspace = fmt->colorspace_default;
++
++ /* In all cases, we only support the defaults for these: */
++ f->fmt.pix_mp.ycbcr_enc =
++ V4L2_MAP_YCBCR_ENC_DEFAULT(f->fmt.pix_mp.colorspace);
++ f->fmt.pix_mp.xfer_func =
++ V4L2_MAP_XFER_FUNC_DEFAULT(f->fmt.pix_mp.colorspace);
++
++ is_rgb = f->fmt.pix_mp.colorspace == V4L2_COLORSPACE_SRGB;
++ f->fmt.pix_mp.quantization =
++ V4L2_MAP_QUANTIZATION_DEFAULT(is_rgb, f->fmt.pix_mp.colorspace,
++ f->fmt.pix_mp.ycbcr_enc);
++
++ /* Set plane size and bytes/line for each plane. */
++ set_plane_params(f, fmt);
++
++ for (i = 0; i < f->fmt.pix_mp.num_planes; i++) {
++ dev_dbg(pispbe->dev,
++ "%s: [%s] calc plane %d, %ux%u, depth %u, bpl %u size %u\n",
++ __func__, NODE_NAME(node), i, f->fmt.pix_mp.width,
++ f->fmt.pix_mp.height, fmt->bit_depth,
++ f->fmt.pix_mp.plane_fmt[i].bytesperline,
++ f->fmt.pix_mp.plane_fmt[i].sizeimage);
++ }
++
++ return 0;
++}
++
++static int pispbe_node_try_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ int ret;
++
++ if (!NODE_IS_CAPTURE(node) || NODE_IS_META(node)) {
++ dev_err(pispbe->dev,
++ "Cannot set capture fmt for output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++
++ ret = try_format(f, node);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int pispbe_node_try_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ int ret;
++
++ if (!NODE_IS_OUTPUT(node) || NODE_IS_META(node)) {
++ dev_err(pispbe->dev,
++ "Cannot set capture fmt for output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++
++ ret = try_format(f, node);
++ if (ret < 0)
++ return ret;
++
++ return 0;
++}
++
++static int pispbe_node_try_fmt_meta_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (!NODE_IS_META(node) || NODE_IS_CAPTURE(node)) {
++ dev_err(pispbe->dev,
++ "Cannot set capture fmt for meta output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++
++ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG;
++ f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config);
++
++ return 0;
++}
++
++static int pispbe_node_try_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (!NODE_IS_META(node) || NODE_IS_OUTPUT(node)) {
++ dev_err(pispbe->dev,
++ "Cannot set capture fmt for meta output node %s\n",
++ NODE_NAME(node));
++ return -EINVAL;
++ }
++
++ f->fmt.meta.dataformat = V4L2_PIX_FMT_RPI_BE;
++ if (!f->fmt.meta.buffersize)
++ f->fmt.meta.buffersize = BIT(20);
++
++ return 0;
++}
++
++static int pispbe_node_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ int ret = pispbe_node_try_fmt_vid_cap(file, priv, f);
++
++ if (ret < 0)
++ return ret;
++
++ node->format = *f;
++ node->pisp_format = find_format(f->fmt.pix_mp.pixelformat);
++
++ dev_dbg(pispbe->dev,
++ "Set capture format for node %s to " V4L2_FOURCC_CONV "\n",
++ NODE_NAME(node),
++ V4L2_FOURCC_CONV_ARGS(f->fmt.pix_mp.pixelformat));
++ return 0;
++}
++
++static int pispbe_node_s_fmt_vid_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ int ret = pispbe_node_try_fmt_vid_out(file, priv, f);
++
++ if (ret < 0)
++ return ret;
++
++ node->format = *f;
++ node->pisp_format = find_format(f->fmt.pix_mp.pixelformat);
++
++ dev_dbg(pispbe->dev,
++ "Set output format for node %s to " V4L2_FOURCC_CONV "\n",
++ NODE_NAME(node),
++ V4L2_FOURCC_CONV_ARGS(f->fmt.pix_mp.pixelformat));
++ return 0;
++}
++
++static int pispbe_node_s_fmt_meta_out(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ int ret = pispbe_node_try_fmt_meta_out(file, priv, f);
++
++ if (ret < 0)
++ return ret;
++
++ node->format = *f;
++ node->pisp_format = &meta_out_supported_formats[0];
++
++ dev_dbg(pispbe->dev,
++ "Set output format for meta node %s to " V4L2_FOURCC_CONV "\n",
++ NODE_NAME(node),
++ V4L2_FOURCC_CONV_ARGS(f->fmt.meta.dataformat));
++ return 0;
++}
++
++static int pispbe_node_s_fmt_meta_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++ int ret = pispbe_node_try_fmt_meta_cap(file, priv, f);
++
++ if (ret < 0)
++ return ret;
++
++ node->format = *f;
++ node->pisp_format = find_format(f->fmt.meta.dataformat);
++
++ dev_dbg(pispbe->dev,
++ "Set capture format for meta node %s to " V4L2_FOURCC_CONV "\n",
++ NODE_NAME(node),
++ V4L2_FOURCC_CONV_ARGS(f->fmt.meta.dataformat));
++ return 0;
++}
++
++static int pispbe_node_enum_fmt(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct pispbe_node *node = video_drvdata(file);
++
++ if (f->type != node->queue.type)
++ return -EINVAL;
++
++ if (NODE_IS_META(node)) {
++ if (f->index)
++ return -EINVAL;
++
++ if (NODE_IS_OUTPUT(node))
++ f->pixelformat = V4L2_META_FMT_RPI_BE_CFG;
++ else
++ f->pixelformat = V4L2_PIX_FMT_RPI_BE;
++ f->flags = 0;
++ return 0;
++ }
++
++ if (f->index >= ARRAY_SIZE(supported_formats))
++ return -EINVAL;
++
++ f->pixelformat = supported_formats[f->index].fourcc;
++ f->flags = 0;
++
++ return 0;
++}
++
++static int pispbe_enum_framesizes(struct file *file, void *priv,
++ struct v4l2_frmsizeenum *fsize)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ if (NODE_IS_META(node) || fsize->index)
++ return -EINVAL;
++
++ if (!find_format(fsize->pixel_format)) {
++ dev_err(pispbe->dev, "Invalid pixel code: %x\n",
++ fsize->pixel_format);
++ return -EINVAL;
++ }
++
++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
++ fsize->stepwise.min_width = 32;
++ fsize->stepwise.max_width = 65535;
++ fsize->stepwise.step_width = 2;
++
++ fsize->stepwise.min_height = 32;
++ fsize->stepwise.max_height = 65535;
++ fsize->stepwise.step_height = 2;
++
++ return 0;
++}
++
++static int pispbe_node_streamon(struct file *file, void *priv,
++ enum v4l2_buf_type type)
++{
++ struct pispbe_node *node = video_drvdata(file);
++ struct pispbe_dev *pispbe = node->node_group->pispbe;
++
++ /* Do we need a node->stream_lock mutex? */
++
++ dev_dbg(pispbe->dev, "Stream on for node %s\n", NODE_NAME(node));
++
++ /* Do we care about the type? Each node has only one queue. */
++
++ INIT_LIST_HEAD(&node->ready_queue);
++
++ /* locking should be handled by the queue->lock? */
++ return vb2_streamon(&node->queue, type);
++}
++
++static int pispbe_node_streamoff(struct file *file, void *priv,
++ enum v4l2_buf_type type)
++{
++ struct pispbe_node *node = video_drvdata(file);
++
++ return vb2_streamoff(&node->queue, type);
++}
++
++static const struct v4l2_ioctl_ops pispbe_node_ioctl_ops = {
++ .vidioc_querycap = pispbe_node_querycap,
++ .vidioc_g_fmt_vid_cap_mplane = pispbe_node_g_fmt_vid_cap,
++ .vidioc_g_fmt_vid_out_mplane = pispbe_node_g_fmt_vid_out,
++ .vidioc_g_fmt_meta_out = pispbe_node_g_fmt_meta_out,
++ .vidioc_g_fmt_meta_cap = pispbe_node_g_fmt_meta_cap,
++ .vidioc_try_fmt_vid_cap_mplane = pispbe_node_try_fmt_vid_cap,
++ .vidioc_try_fmt_vid_out_mplane = pispbe_node_try_fmt_vid_out,
++ .vidioc_try_fmt_meta_out = pispbe_node_try_fmt_meta_out,
++ .vidioc_try_fmt_meta_cap = pispbe_node_try_fmt_meta_cap,
++ .vidioc_s_fmt_vid_cap_mplane = pispbe_node_s_fmt_vid_cap,
++ .vidioc_s_fmt_vid_out_mplane = pispbe_node_s_fmt_vid_out,
++ .vidioc_s_fmt_meta_out = pispbe_node_s_fmt_meta_out,
++ .vidioc_s_fmt_meta_cap = pispbe_node_s_fmt_meta_cap,
++ .vidioc_enum_fmt_vid_cap = pispbe_node_enum_fmt,
++ .vidioc_enum_fmt_vid_out = pispbe_node_enum_fmt,
++ .vidioc_enum_fmt_meta_cap = pispbe_node_enum_fmt,
++ .vidioc_enum_fmt_meta_out = pispbe_node_enum_fmt,
++ .vidioc_enum_framesizes = pispbe_enum_framesizes,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_streamon = pispbe_node_streamon,
++ .vidioc_streamoff = pispbe_node_streamoff,
++};
++
++static const struct video_device pispbe_videodev = {
++ .name = PISPBE_NAME,
++ .vfl_dir = VFL_DIR_M2M, /* gets overwritten */
++ .fops = &pispbe_fops,
++ .ioctl_ops = &pispbe_node_ioctl_ops,
++ .minor = -1,
++ .release = video_device_release_empty,
++};
++
++static void node_set_default_format(struct pispbe_node *node)
++{
++ if (NODE_IS_META(node) && NODE_IS_OUTPUT(node)) {
++ /* Config node */
++ struct v4l2_format *f = &node->format;
++
++ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_BE_CFG;
++ f->fmt.meta.buffersize = sizeof(struct pisp_be_tiles_config);
++ f->type = node->buf_type;
++ } else if (NODE_IS_META(node) && NODE_IS_CAPTURE(node)) {
++ /* HOG output node */
++ struct v4l2_format *f = &node->format;
++
++ f->fmt.meta.dataformat = V4L2_PIX_FMT_RPI_BE;
++ f->fmt.meta.buffersize = BIT(20);
++ f->type = node->buf_type;
++ } else {
++ struct v4l2_format f = {0};
++
++ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
++ f.fmt.pix_mp.width = 1920;
++ f.fmt.pix_mp.height = 1080;
++ f.type = node->buf_type;
++ try_format(&f, node);
++ node->format = f;
++ }
++
++ node->pisp_format = find_format(node->format.fmt.pix_mp.pixelformat);
++}
++
++/*
++ * Initialise a struct pispbe_node and register it as /dev/video<N>
++ * to represent one of the PiSP Back End's input or output streams.
++ */
++static int
++pispbe_init_node(struct pispbe_node_group *node_group, unsigned int id)
++{
++ bool output = NODE_DESC_IS_OUTPUT(&node_desc[id]);
++ struct pispbe_node *node = &node_group->node[id];
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ struct media_entity *entity = &node->vfd.entity;
++ struct video_device *vdev = &node->vfd;
++ struct vb2_queue *q = &node->queue;
++ int ret;
++
++ node->id = id;
++ node->node_group = node_group;
++ node->buf_type = node_desc[id].buf_type;
++
++ mutex_init(&node->node_lock);
++ mutex_init(&node->queue_lock);
++ INIT_LIST_HEAD(&node->ready_queue);
++ spin_lock_init(&node->ready_lock);
++
++ node->format.type = node->buf_type;
++ node_set_default_format(node);
++
++ q->type = node->buf_type;
++ q->io_modes = VB2_MMAP | VB2_DMABUF;
++ q->mem_ops = &vb2_dma_contig_memops;
++ q->drv_priv = node;
++ q->ops = &pispbe_node_queue_ops;
++ q->buf_struct_size = sizeof(struct pispbe_buffer);
++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++ q->dev = node->node_group->pispbe->dev;
++ /* get V4L2 to handle node->queue locking */
++ q->lock = &node->queue_lock;
++
++ ret = vb2_queue_init(q);
++ if (ret < 0) {
++ dev_err(pispbe->dev, "vb2_queue_init failed\n");
++ return ret;
++ }
++
++ *vdev = pispbe_videodev; /* default initialization */
++ strscpy(vdev->name, node_desc[id].ent_name, sizeof(vdev->name));
++ vdev->v4l2_dev = &node_group->v4l2_dev;
++ vdev->vfl_dir = output ? VFL_DIR_TX : VFL_DIR_RX;
++ /* get V4L2 to serialise our ioctls */
++ vdev->lock = &node->node_lock;
++ vdev->queue = &node->queue;
++ vdev->device_caps = V4L2_CAP_STREAMING | node_desc[id].caps;
++
++ node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK;
++ ret = media_entity_pads_init(entity, 1, &node->pad);
++ if (ret) {
++ dev_err(pispbe->dev,
++ "Failed to register media pads for %s device node\n",
++ NODE_NAME(node));
++ goto err_unregister_queue;
++ }
++
++ ret = video_register_device(vdev, VFL_TYPE_VIDEO,
++ PISPBE_VIDEO_NODE_OFFSET);
++ if (ret) {
++ dev_err(pispbe->dev,
++ "Failed to register video %s device node\n",
++ NODE_NAME(node));
++ goto err_unregister_queue;
++ }
++ video_set_drvdata(vdev, node);
++
++ if (output)
++ ret = media_create_pad_link(entity, 0, &node_group->sd.entity,
++ id, MEDIA_LNK_FL_IMMUTABLE |
++ MEDIA_LNK_FL_ENABLED);
++ else
++ ret = media_create_pad_link(&node_group->sd.entity, id, entity,
++ 0, MEDIA_LNK_FL_IMMUTABLE |
++ MEDIA_LNK_FL_ENABLED);
++ if (ret)
++ goto err_unregister_video_dev;
++
++ dev_info(pispbe->dev,
++ "%s device node registered as /dev/video%d\n",
++ NODE_NAME(node), node->vfd.num);
++ return 0;
++
++err_unregister_video_dev:
++ video_unregister_device(&node->vfd);
++err_unregister_queue:
++ vb2_queue_release(&node->queue);
++ return ret;
++}
++
++static const struct v4l2_subdev_pad_ops pispbe_pad_ops = {
++ .link_validate = v4l2_subdev_link_validate_default,
++};
++
++static const struct v4l2_subdev_ops pispbe_sd_ops = {
++ .pad = &pispbe_pad_ops,
++};
++
++static int pispbe_init_subdev(struct pispbe_node_group *node_group)
++{
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ struct v4l2_subdev *sd = &node_group->sd;
++ unsigned int i;
++ int ret;
++
++ v4l2_subdev_init(sd, &pispbe_sd_ops);
++ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
++ sd->owner = THIS_MODULE;
++ sd->dev = pispbe->dev;
++ strscpy(sd->name, PISPBE_NAME, sizeof(sd->name));
++
++ for (i = 0; i < PISPBE_NUM_NODES; i++)
++ node_group->pad[i].flags =
++ NODE_DESC_IS_OUTPUT(&node_desc[i]) ?
++ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&sd->entity, PISPBE_NUM_NODES,
++ node_group->pad);
++ if (ret)
++ goto error;
++
++ ret = v4l2_device_register_subdev(&node_group->v4l2_dev, sd);
++ if (ret)
++ goto error;
++
++ return 0;
++
++error:
++ media_entity_cleanup(&sd->entity);
++ return ret;
++}
++
++static int pispbe_init_group(struct pispbe_dev *pispbe, unsigned int id)
++{
++ struct pispbe_node_group *node_group = &pispbe->node_group[id];
++ struct v4l2_device *v4l2_dev;
++ struct media_device *mdev;
++ unsigned int num_registered = 0;
++ int ret;
++
++ node_group->id = id;
++ node_group->pispbe = pispbe;
++ node_group->streaming_map = 0;
++
++ dev_info(pispbe->dev, "Register nodes for group %u\n", id);
++
++ /* Register v4l2_device and media_device */
++ mdev = &node_group->mdev;
++ mdev->hw_revision = node_group->pispbe->hw_version;
++ mdev->dev = node_group->pispbe->dev;
++ strscpy(mdev->model, PISPBE_NAME, sizeof(mdev->model));
++ snprintf(mdev->bus_info, sizeof(mdev->bus_info),
++ "platform:%s", dev_name(node_group->pispbe->dev));
++ media_device_init(mdev);
++
++ v4l2_dev = &node_group->v4l2_dev;
++ v4l2_dev->mdev = &node_group->mdev;
++ strscpy(v4l2_dev->name, PISPBE_NAME, sizeof(v4l2_dev->name));
++
++ ret = v4l2_device_register(pispbe->dev, &node_group->v4l2_dev);
++ if (ret)
++ goto err_media_dev_cleanup;
++
++ /* Register the PISPBE subdevice. */
++ ret = pispbe_init_subdev(node_group);
++ if (ret)
++ goto err_unregister_v4l2;
++
++ /* Create device video nodes */
++ for (; num_registered < PISPBE_NUM_NODES; num_registered++) {
++ ret = pispbe_init_node(node_group, num_registered);
++ if (ret)
++ goto err_unregister_nodes;
++ }
++
++ ret = media_device_register(mdev);
++ if (ret)
++ goto err_unregister_nodes;
++
++ node_group->config =
++ dma_alloc_coherent(pispbe->dev,
++ sizeof(struct pisp_be_tiles_config) *
++ PISP_BE_NUM_CONFIG_BUFFERS,
++ &node_group->config_dma_addr, GFP_KERNEL);
++ if (!node_group->config) {
++ dev_err(pispbe->dev, "Unable to allocate cached config buffers.\n");
++ ret = -ENOMEM;
++ goto err_unregister_mdev;
++ }
++
++ return 0;
++
++err_unregister_mdev:
++ media_device_unregister(mdev);
++err_unregister_nodes:
++ while (num_registered-- > 0) {
++ video_unregister_device(&node_group->node[num_registered].vfd);
++ vb2_queue_release(&node_group->node[num_registered].queue);
++ }
++ v4l2_device_unregister_subdev(&node_group->sd);
++ media_entity_cleanup(&node_group->sd.entity);
++err_unregister_v4l2:
++ v4l2_device_unregister(v4l2_dev);
++err_media_dev_cleanup:
++ media_device_cleanup(mdev);
++ return ret;
++}
++
++static void pispbe_destroy_node_group(struct pispbe_node_group *node_group)
++{
++ struct pispbe_dev *pispbe = node_group->pispbe;
++ int i;
++
++ if (node_group->config) {
++ dma_free_coherent(node_group->pispbe->dev,
++ sizeof(struct pisp_be_tiles_config) *
++ PISP_BE_NUM_CONFIG_BUFFERS,
++ node_group->config,
++ node_group->config_dma_addr);
++ }
++
++ dev_info(pispbe->dev, "Unregister from media controller\n");
++
++ v4l2_device_unregister_subdev(&node_group->sd);
++ media_entity_cleanup(&node_group->sd.entity);
++ media_device_unregister(&node_group->mdev);
++
++ for (i = PISPBE_NUM_NODES - 1; i >= 0; i--) {
++ video_unregister_device(&node_group->node[i].vfd);
++ vb2_queue_release(&node_group->node[i].queue);
++ }
++
++ media_device_cleanup(&node_group->mdev);
++ v4l2_device_unregister(&node_group->v4l2_dev);
++}
++
++static int pispbe_runtime_suspend(struct device *dev)
++{
++ struct pispbe_dev *pispbe = dev_get_drvdata(dev);
++
++ clk_disable_unprepare(pispbe->clk);
++
++ return 0;
++}
++
++static int pispbe_runtime_resume(struct device *dev)
++{
++ struct pispbe_dev *pispbe = dev_get_drvdata(dev);
++ int ret;
++
++ ret = clk_prepare_enable(pispbe->clk);
++ if (ret) {
++ dev_err(dev, "Unable to enable clock\n");
++ return ret;
++ }
++
++ dev_dbg(dev, "%s: Enabled clock, rate=%lu\n",
++ __func__, clk_get_rate(pispbe->clk));
++
++ return 0;
++}
++
++/*
++ * Probe the ISP-BE hardware block, as a single platform device.
++ * This will instantiate multiple "node groups" each with many device nodes.
++ */
++static int pispbe_probe(struct platform_device *pdev)
++{
++ unsigned int num_groups = 0;
++ struct pispbe_dev *pispbe;
++ int ret;
++
++ pispbe = devm_kzalloc(&pdev->dev, sizeof(*pispbe), GFP_KERNEL);
++ if (!pispbe)
++ return -ENOMEM;
++
++ dev_set_drvdata(&pdev->dev, pispbe);
++ pispbe->dev = &pdev->dev;
++ platform_set_drvdata(pdev, pispbe);
++
++ pispbe->be_reg_base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(pispbe->be_reg_base)) {
++ dev_err(&pdev->dev, "Failed to get ISP-BE registers address\n");
++ return PTR_ERR(pispbe->be_reg_base);
++ }
++
++ pispbe->irq = platform_get_irq(pdev, 0);
++ if (pispbe->irq <= 0) {
++ dev_err(&pdev->dev, "No IRQ resource\n");
++ return -EINVAL;
++ }
++
++ ret = devm_request_irq(&pdev->dev, pispbe->irq, pispbe_isr, 0,
++ PISPBE_NAME, pispbe);
++ if (ret) {
++ dev_err(&pdev->dev, "Unable to request interrupt\n");
++ return ret;
++ }
++
++ ret = dma_set_mask_and_coherent(pispbe->dev, DMA_BIT_MASK(36));
++ if (ret)
++ return ret;
++
++ pispbe->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(pispbe->clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(pispbe->clk),
++ "Failed to get clock");
++
++ /* Hardware initialisation */
++ pm_runtime_set_autosuspend_delay(pispbe->dev, 200);
++ pm_runtime_use_autosuspend(pispbe->dev);
++ pm_runtime_enable(pispbe->dev);
++
++ ret = pm_runtime_resume_and_get(pispbe->dev);
++ if (ret)
++ goto pm_runtime_disable_err;
++
++ pispbe->hw_busy = 0;
++ spin_lock_init(&pispbe->hw_lock);
++ ret = hw_init(pispbe);
++ if (ret)
++ goto pm_runtime_put_err;
++
++ /*
++ * Initialise and register devices for each node_group, including media
++ * device
++ */
++ for (num_groups = 0;
++ num_groups < PISPBE_NUM_NODE_GROUPS;
++ num_groups++) {
++ ret = pispbe_init_group(pispbe, num_groups);
++ if (ret)
++ goto disable_nodes_err;
++ }
++
++ pm_runtime_mark_last_busy(pispbe->dev);
++ pm_runtime_put_autosuspend(pispbe->dev);
++
++ return 0;
++
++disable_nodes_err:
++ while (num_groups-- > 0)
++ pispbe_destroy_node_group(&pispbe->node_group[num_groups]);
++pm_runtime_put_err:
++ pm_runtime_put(pispbe->dev);
++pm_runtime_disable_err:
++ pm_runtime_dont_use_autosuspend(pispbe->dev);
++ pm_runtime_disable(pispbe->dev);
++
++ dev_err(&pdev->dev, "%s: returning %d", __func__, ret);
++
++ return ret;
++}
++
++static int pispbe_remove(struct platform_device *pdev)
++{
++ struct pispbe_dev *pispbe = platform_get_drvdata(pdev);
++ int i;
++
++ for (i = PISPBE_NUM_NODE_GROUPS - 1; i >= 0; i--)
++ pispbe_destroy_node_group(&pispbe->node_group[i]);
++
++ pm_runtime_dont_use_autosuspend(pispbe->dev);
++ pm_runtime_disable(pispbe->dev);
++
++ return 0;
++}
++
++static const struct dev_pm_ops pispbe_pm_ops = {
++ SET_RUNTIME_PM_OPS(pispbe_runtime_suspend, pispbe_runtime_resume, NULL)
++};
++
++static const struct of_device_id pispbe_of_match[] = {
++ {
++ .compatible = "raspberrypi,pispbe",
++ },
++ { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, pispbe_of_match);
++
++static struct platform_driver pispbe_pdrv = {
++ .probe = pispbe_probe,
++ .remove = pispbe_remove,
++ .driver = {
++ .name = PISPBE_NAME,
++ .of_match_table = pispbe_of_match,
++ .pm = &pispbe_pm_ops,
++ },
++};
++
++module_platform_driver(pispbe_pdrv);
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
+@@ -0,0 +1,533 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * PiSP Back End configuration definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd
++ *
++ */
++#ifndef _PISP_BE_CONFIG_H_
++#define _PISP_BE_CONFIG_H_
++
++#include <linux/types.h>
++
++#include <media/raspberrypi/pisp_common.h>
++
++/* byte alignment for inputs */
++#define PISP_BACK_END_INPUT_ALIGN 4u
++/* alignment for compressed inputs */
++#define PISP_BACK_END_COMPRESSED_ALIGN 8u
++/* minimum required byte alignment for outputs */
++#define PISP_BACK_END_OUTPUT_MIN_ALIGN 16u
++/* preferred byte alignment for outputs */
++#define PISP_BACK_END_OUTPUT_MAX_ALIGN 64u
++
++/* minimum allowed tile width anywhere in the pipeline */
++#define PISP_BACK_END_MIN_TILE_WIDTH 16u
++/* minimum allowed tile width anywhere in the pipeline */
++#define PISP_BACK_END_MIN_TILE_HEIGHT 16u
++
++#define PISP_BACK_END_NUM_OUTPUTS 2
++#define PISP_BACK_END_HOG_OUTPUT 1
++
++#define PISP_BACK_END_NUM_TILES 64
++
++enum pisp_be_bayer_enable {
++ PISP_BE_BAYER_ENABLE_INPUT = 0x000001,
++ PISP_BE_BAYER_ENABLE_DECOMPRESS = 0x000002,
++ PISP_BE_BAYER_ENABLE_DPC = 0x000004,
++ PISP_BE_BAYER_ENABLE_GEQ = 0x000008,
++ PISP_BE_BAYER_ENABLE_TDN_INPUT = 0x000010,
++ PISP_BE_BAYER_ENABLE_TDN_DECOMPRESS = 0x000020,
++ PISP_BE_BAYER_ENABLE_TDN = 0x000040,
++ PISP_BE_BAYER_ENABLE_TDN_COMPRESS = 0x000080,
++ PISP_BE_BAYER_ENABLE_TDN_OUTPUT = 0x000100,
++ PISP_BE_BAYER_ENABLE_SDN = 0x000200,
++ PISP_BE_BAYER_ENABLE_BLC = 0x000400,
++ PISP_BE_BAYER_ENABLE_STITCH_INPUT = 0x000800,
++ PISP_BE_BAYER_ENABLE_STITCH_DECOMPRESS = 0x001000,
++ PISP_BE_BAYER_ENABLE_STITCH = 0x002000,
++ PISP_BE_BAYER_ENABLE_STITCH_COMPRESS = 0x004000,
++ PISP_BE_BAYER_ENABLE_STITCH_OUTPUT = 0x008000,
++ PISP_BE_BAYER_ENABLE_WBG = 0x010000,
++ PISP_BE_BAYER_ENABLE_CDN = 0x020000,
++ PISP_BE_BAYER_ENABLE_LSC = 0x040000,
++ PISP_BE_BAYER_ENABLE_TONEMAP = 0x080000,
++ PISP_BE_BAYER_ENABLE_CAC = 0x100000,
++ PISP_BE_BAYER_ENABLE_DEBIN = 0x200000,
++ PISP_BE_BAYER_ENABLE_DEMOSAIC = 0x400000,
++};
++
++enum pisp_be_rgb_enable {
++ PISP_BE_RGB_ENABLE_INPUT = 0x000001,
++ PISP_BE_RGB_ENABLE_CCM = 0x000002,
++ PISP_BE_RGB_ENABLE_SAT_CONTROL = 0x000004,
++ PISP_BE_RGB_ENABLE_YCBCR = 0x000008,
++ PISP_BE_RGB_ENABLE_FALSE_COLOUR = 0x000010,
++ PISP_BE_RGB_ENABLE_SHARPEN = 0x000020,
++ /* Preferred colours would occupy 0x000040 */
++ PISP_BE_RGB_ENABLE_YCBCR_INVERSE = 0x000080,
++ PISP_BE_RGB_ENABLE_GAMMA = 0x000100,
++ PISP_BE_RGB_ENABLE_CSC0 = 0x000200,
++ PISP_BE_RGB_ENABLE_CSC1 = 0x000400,
++ PISP_BE_RGB_ENABLE_DOWNSCALE0 = 0x001000,
++ PISP_BE_RGB_ENABLE_DOWNSCALE1 = 0x002000,
++ PISP_BE_RGB_ENABLE_RESAMPLE0 = 0x008000,
++ PISP_BE_RGB_ENABLE_RESAMPLE1 = 0x010000,
++ PISP_BE_RGB_ENABLE_OUTPUT0 = 0x040000,
++ PISP_BE_RGB_ENABLE_OUTPUT1 = 0x080000,
++ PISP_BE_RGB_ENABLE_HOG = 0x200000
++};
++
++#define PISP_BE_RGB_ENABLE_CSC(i) (PISP_BE_RGB_ENABLE_CSC0 << (i))
++#define PISP_BE_RGB_ENABLE_DOWNSCALE(i) (PISP_BE_RGB_ENABLE_DOWNSCALE0 << (i))
++#define PISP_BE_RGB_ENABLE_RESAMPLE(i) (PISP_BE_RGB_ENABLE_RESAMPLE0 << (i))
++#define PISP_BE_RGB_ENABLE_OUTPUT(i) (PISP_BE_RGB_ENABLE_OUTPUT0 << (i))
++
++/*
++ * We use the enable flags to show when blocks are "dirty", but we need some
++ * extra ones too.
++ */
++enum pisp_be_dirty {
++ PISP_BE_DIRTY_GLOBAL = 0x0001,
++ PISP_BE_DIRTY_SH_FC_COMBINE = 0x0002,
++ PISP_BE_DIRTY_CROP = 0x0004
++};
++
++struct pisp_be_global_config {
++ u32 bayer_enables;
++ u32 rgb_enables;
++ u8 bayer_order;
++ u8 pad[3];
++};
++
++struct pisp_be_input_buffer_config {
++ /* low 32 bits followed by high 32 bits (for each of up to 3 planes) */
++ u32 addr[3][2];
++};
++
++struct pisp_be_dpc_config {
++ u8 coeff_level;
++ u8 coeff_range;
++ u8 pad;
++#define PISP_BE_DPC_FLAG_FOLDBACK 1
++ u8 flags;
++};
++
++struct pisp_be_geq_config {
++ u16 offset;
++#define PISP_BE_GEQ_SHARPER BIT(15)
++#define PISP_BE_GEQ_SLOPE ((1 << 10) - 1)
++ /* top bit is the "sharper" flag, slope value is bottom 10 bits */
++ u16 slope_sharper;
++ u16 min;
++ u16 max;
++};
++
++struct pisp_be_tdn_input_buffer_config {
++ /* low 32 bits followed by high 32 bits */
++ u32 addr[2];
++};
++
++struct pisp_be_tdn_config {
++ u16 black_level;
++ u16 ratio;
++ u16 noise_constant;
++ u16 noise_slope;
++ u16 threshold;
++ u8 reset;
++ u8 pad;
++};
++
++struct pisp_be_tdn_output_buffer_config {
++ /* low 32 bits followed by high 32 bits */
++ u32 addr[2];
++};
++
++struct pisp_be_sdn_config {
++ u16 black_level;
++ u8 leakage;
++ u8 pad;
++ u16 noise_constant;
++ u16 noise_slope;
++ u16 noise_constant2;
++ u16 noise_slope2;
++};
++
++struct pisp_be_stitch_input_buffer_config {
++ /* low 32 bits followed by high 32 bits */
++ u32 addr[2];
++};
++
++#define PISP_BE_STITCH_STREAMING_LONG 0x8000
++#define PISP_BE_STITCH_EXPOSURE_RATIO_MASK 0x7fff
++
++struct pisp_be_stitch_config {
++ u16 threshold_lo;
++ u8 threshold_diff_power;
++ u8 pad;
++
++ /* top bit indicates whether streaming input is the long exposure */
++ u16 exposure_ratio;
++
++ u8 motion_threshold_256;
++ u8 motion_threshold_recip;
++};
++
++struct pisp_be_stitch_output_buffer_config {
++ /* low 32 bits followed by high 32 bits */
++ u32 addr[2];
++};
++
++struct pisp_be_cdn_config {
++ u16 thresh;
++ u8 iir_strength;
++ u8 g_adjust;
++};
++
++#define PISP_BE_LSC_LOG_GRID_SIZE 5
++#define PISP_BE_LSC_GRID_SIZE (1 << PISP_BE_LSC_LOG_GRID_SIZE)
++#define PISP_BE_LSC_STEP_PRECISION 18
++
++struct pisp_be_lsc_config {
++ /* (1<<18) / grid_cell_width */
++ u16 grid_step_x;
++ /* (1<<18) / grid_cell_height */
++ u16 grid_step_y;
++ /* RGB gains jointly encoded in 32 bits */
++ u32 lut_packed[PISP_BE_LSC_GRID_SIZE + 1]
++ [PISP_BE_LSC_GRID_SIZE + 1];
++};
++
++struct pisp_be_lsc_extra {
++ u16 offset_x;
++ u16 offset_y;
++};
++
++#define PISP_BE_CAC_LOG_GRID_SIZE 3
++#define PISP_BE_CAC_GRID_SIZE (1 << PISP_BE_CAC_LOG_GRID_SIZE)
++#define PISP_BE_CAC_STEP_PRECISION 20
++
++struct pisp_be_cac_config {
++ /* (1<<20) / grid_cell_width */
++ u16 grid_step_x;
++ /* (1<<20) / grid_cell_height */
++ u16 grid_step_y;
++ /* [gridy][gridx][rb][xy] */
++ s8 lut[PISP_BE_CAC_GRID_SIZE + 1][PISP_BE_CAC_GRID_SIZE + 1][2][2];
++};
++
++struct pisp_be_cac_extra {
++ u16 offset_x;
++ u16 offset_y;
++};
++
++#define PISP_BE_DEBIN_NUM_COEFFS 4
++
++struct pisp_be_debin_config {
++ s8 coeffs[PISP_BE_DEBIN_NUM_COEFFS];
++ s8 h_enable;
++ s8 v_enable;
++ s8 pad[2];
++};
++
++#define PISP_BE_TONEMAP_LUT_SIZE 64
++
++struct pisp_be_tonemap_config {
++ u16 detail_constant;
++ u16 detail_slope;
++ u16 iir_strength;
++ u16 strength;
++ u32 lut[PISP_BE_TONEMAP_LUT_SIZE];
++};
++
++struct pisp_be_demosaic_config {
++ u8 sharper;
++ u8 fc_mode;
++ u8 pad[2];
++};
++
++struct pisp_be_ccm_config {
++ s16 coeffs[9];
++ u8 pad[2];
++ s32 offsets[3];
++};
++
++struct pisp_be_sat_control_config {
++ u8 shift_r;
++ u8 shift_g;
++ u8 shift_b;
++ u8 pad;
++};
++
++struct pisp_be_false_colour_config {
++ u8 distance;
++ u8 pad[3];
++};
++
++#define PISP_BE_SHARPEN_SIZE 5
++#define PISP_BE_SHARPEN_FUNC_NUM_POINTS 9
++
++struct pisp_be_sharpen_config {
++ s8 kernel0[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++ s8 pad0[3];
++ s8 kernel1[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++ s8 pad1[3];
++ s8 kernel2[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++ s8 pad2[3];
++ s8 kernel3[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++ s8 pad3[3];
++ s8 kernel4[PISP_BE_SHARPEN_SIZE * PISP_BE_SHARPEN_SIZE];
++ s8 pad4[3];
++ u16 threshold_offset0;
++ u16 threshold_slope0;
++ u16 scale0;
++ u16 pad5;
++ u16 threshold_offset1;
++ u16 threshold_slope1;
++ u16 scale1;
++ u16 pad6;
++ u16 threshold_offset2;
++ u16 threshold_slope2;
++ u16 scale2;
++ u16 pad7;
++ u16 threshold_offset3;
++ u16 threshold_slope3;
++ u16 scale3;
++ u16 pad8;
++ u16 threshold_offset4;
++ u16 threshold_slope4;
++ u16 scale4;
++ u16 pad9;
++ u16 positive_strength;
++ u16 positive_pre_limit;
++ u16 positive_func[PISP_BE_SHARPEN_FUNC_NUM_POINTS];
++ u16 positive_limit;
++ u16 negative_strength;
++ u16 negative_pre_limit;
++ u16 negative_func[PISP_BE_SHARPEN_FUNC_NUM_POINTS];
++ u16 negative_limit;
++ u8 enables;
++ u8 white;
++ u8 black;
++ u8 grey;
++};
++
++struct pisp_be_sh_fc_combine_config {
++ u8 y_factor;
++ u8 c1_factor;
++ u8 c2_factor;
++ u8 pad;
++};
++
++#define PISP_BE_GAMMA_LUT_SIZE 64
++
++struct pisp_be_gamma_config {
++ u32 lut[PISP_BE_GAMMA_LUT_SIZE];
++};
++
++struct pisp_be_crop_config {
++ u16 offset_x, offset_y;
++ u16 width, height;
++};
++
++#define PISP_BE_RESAMPLE_FILTER_SIZE 96
++
++struct pisp_be_resample_config {
++ u16 scale_factor_h, scale_factor_v;
++ s16 coef[PISP_BE_RESAMPLE_FILTER_SIZE];
++};
++
++struct pisp_be_resample_extra {
++ u16 scaled_width;
++ u16 scaled_height;
++ s16 initial_phase_h[3];
++ s16 initial_phase_v[3];
++};
++
++struct pisp_be_downscale_config {
++ u16 scale_factor_h;
++ u16 scale_factor_v;
++ u16 scale_recip_h;
++ u16 scale_recip_v;
++};
++
++struct pisp_be_downscale_extra {
++ u16 scaled_width;
++ u16 scaled_height;
++};
++
++struct pisp_be_hog_config {
++ u8 compute_signed;
++ u8 channel_mix[3];
++ u32 stride;
++};
++
++struct pisp_be_axi_config {
++ u8 r_qos; /* Read QoS */
++ u8 r_cache_prot; /* Read { prot[2:0], cache[3:0] } */
++ u8 w_qos; /* Write QoS */
++ u8 w_cache_prot; /* Write { prot[2:0], cache[3:0] } */
++};
++
++enum pisp_be_transform {
++ PISP_BE_TRANSFORM_NONE = 0x0,
++ PISP_BE_TRANSFORM_HFLIP = 0x1,
++ PISP_BE_TRANSFORM_VFLIP = 0x2,
++ PISP_BE_TRANSFORM_ROT180 =
++ (PISP_BE_TRANSFORM_HFLIP | PISP_BE_TRANSFORM_VFLIP)
++};
++
++struct pisp_be_output_format_config {
++ struct pisp_image_format_config image;
++ u8 transform;
++ u8 pad[3];
++ u16 lo;
++ u16 hi;
++ u16 lo2;
++ u16 hi2;
++};
++
++struct pisp_be_output_buffer_config {
++ /* low 32 bits followed by high 32 bits (for each of 3 planes) */
++ u32 addr[3][2];
++};
++
++struct pisp_be_hog_buffer_config {
++ /* low 32 bits followed by high 32 bits */
++ u32 addr[2];
++};
++
++struct pisp_be_config {
++ /* I/O configuration: */
++ struct pisp_be_input_buffer_config input_buffer;
++ struct pisp_be_tdn_input_buffer_config tdn_input_buffer;
++ struct pisp_be_stitch_input_buffer_config stitch_input_buffer;
++ struct pisp_be_tdn_output_buffer_config tdn_output_buffer;
++ struct pisp_be_stitch_output_buffer_config stitch_output_buffer;
++ struct pisp_be_output_buffer_config
++ output_buffer[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_hog_buffer_config hog_buffer;
++ /* Processing configuration: */
++ struct pisp_be_global_config global;
++ struct pisp_image_format_config input_format;
++ struct pisp_decompress_config decompress;
++ struct pisp_be_dpc_config dpc;
++ struct pisp_be_geq_config geq;
++ struct pisp_image_format_config tdn_input_format;
++ struct pisp_decompress_config tdn_decompress;
++ struct pisp_be_tdn_config tdn;
++ struct pisp_compress_config tdn_compress;
++ struct pisp_image_format_config tdn_output_format;
++ struct pisp_be_sdn_config sdn;
++ struct pisp_bla_config blc;
++ struct pisp_compress_config stitch_compress;
++ struct pisp_image_format_config stitch_output_format;
++ struct pisp_image_format_config stitch_input_format;
++ struct pisp_decompress_config stitch_decompress;
++ struct pisp_be_stitch_config stitch;
++ struct pisp_be_lsc_config lsc;
++ struct pisp_wbg_config wbg;
++ struct pisp_be_cdn_config cdn;
++ struct pisp_be_cac_config cac;
++ struct pisp_be_debin_config debin;
++ struct pisp_be_tonemap_config tonemap;
++ struct pisp_be_demosaic_config demosaic;
++ struct pisp_be_ccm_config ccm;
++ struct pisp_be_sat_control_config sat_control;
++ struct pisp_be_ccm_config ycbcr;
++ struct pisp_be_sharpen_config sharpen;
++ struct pisp_be_false_colour_config false_colour;
++ struct pisp_be_sh_fc_combine_config sh_fc_combine;
++ struct pisp_be_ccm_config ycbcr_inverse;
++ struct pisp_be_gamma_config gamma;
++ struct pisp_be_ccm_config csc[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_downscale_config downscale[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_resample_config resample[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_output_format_config
++ output_format[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_hog_config hog;
++ struct pisp_be_axi_config axi;
++ /* Non-register fields: */
++ struct pisp_be_lsc_extra lsc_extra;
++ struct pisp_be_cac_extra cac_extra;
++ struct pisp_be_downscale_extra
++ downscale_extra[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_resample_extra resample_extra[PISP_BACK_END_NUM_OUTPUTS];
++ struct pisp_be_crop_config crop;
++ struct pisp_image_format_config hog_format;
++ u32 dirty_flags_bayer; /* these use pisp_be_bayer_enable */
++ u32 dirty_flags_rgb; /* use pisp_be_rgb_enable */
++ u32 dirty_flags_extra; /* these use pisp_be_dirty_t */
++};
++
++/*
++ * We also need a tile structure to describe the size of the tiles going
++ * through the pipeline.
++ */
++
++enum pisp_tile_edge {
++ PISP_LEFT_EDGE = (1 << 0),
++ PISP_RIGHT_EDGE = (1 << 1),
++ PISP_TOP_EDGE = (1 << 2),
++ PISP_BOTTOM_EDGE = (1 << 3)
++};
++
++struct pisp_tile {
++ u8 edge; // enum pisp_tile_edge
++ u8 pad0[3];
++ // 4 bytes
++ u32 input_addr_offset;
++ u32 input_addr_offset2;
++ u16 input_offset_x;
++ u16 input_offset_y;
++ u16 input_width;
++ u16 input_height;
++ // 20 bytes
++ u32 tdn_input_addr_offset;
++ u32 tdn_output_addr_offset;
++ u32 stitch_input_addr_offset;
++ u32 stitch_output_addr_offset;
++ // 36 bytes
++ u32 lsc_grid_offset_x;
++ u32 lsc_grid_offset_y;
++ // 44 bytes
++ u32 cac_grid_offset_x;
++ u32 cac_grid_offset_y;
++ // 52 bytes
++ u16 crop_x_start[PISP_BACK_END_NUM_OUTPUTS];
++ u16 crop_x_end[PISP_BACK_END_NUM_OUTPUTS];
++ u16 crop_y_start[PISP_BACK_END_NUM_OUTPUTS];
++ u16 crop_y_end[PISP_BACK_END_NUM_OUTPUTS];
++ // 68 bytes
++ /* Ordering is planes then branches */
++ u16 downscale_phase_x[3 * PISP_BACK_END_NUM_OUTPUTS];
++ u16 downscale_phase_y[3 * PISP_BACK_END_NUM_OUTPUTS];
++ // 92 bytes
++ u16 resample_in_width[PISP_BACK_END_NUM_OUTPUTS];
++ u16 resample_in_height[PISP_BACK_END_NUM_OUTPUTS];
++ // 100 bytes
++ /* Ordering is planes then branches */
++ u16 resample_phase_x[3 * PISP_BACK_END_NUM_OUTPUTS];
++ u16 resample_phase_y[3 * PISP_BACK_END_NUM_OUTPUTS];
++ // 124 bytes
++ u16 output_offset_x[PISP_BACK_END_NUM_OUTPUTS];
++ u16 output_offset_y[PISP_BACK_END_NUM_OUTPUTS];
++ u16 output_width[PISP_BACK_END_NUM_OUTPUTS];
++ u16 output_height[PISP_BACK_END_NUM_OUTPUTS];
++ // 140 bytes
++ u32 output_addr_offset[PISP_BACK_END_NUM_OUTPUTS];
++ u32 output_addr_offset2[PISP_BACK_END_NUM_OUTPUTS];
++ // 156 bytes
++ u32 output_hog_addr_offset;
++ // 160 bytes
++};
++
++static_assert(sizeof(struct pisp_tile) == 160);
++
++struct pisp_be_tiles_config {
++ struct pisp_be_config config;
++ struct pisp_tile tiles[PISP_BACK_END_NUM_TILES];
++ int num_tiles;
++};
++
++#endif /* _PISP_BE_CONFIG_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+@@ -0,0 +1,469 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PiSP Back End driver image format definitions.
++ *
++ * Copyright (c) 2021 Raspberry Pi Ltd
++ */
++
++#ifndef _PISP_BE_FORMATS_
++#define _PISP_BE_FORMATS_
++
++#include <linux/bits.h>
++#include <linux/videodev2.h>
++
++#define MAX_PLANES 3
++#define P3(x) ((x) * 8)
++
++struct pisp_be_format {
++ unsigned int fourcc;
++ unsigned int align;
++ unsigned int bit_depth;
++ /* 0P3 factor for plane sizing */
++ unsigned int plane_factor[MAX_PLANES];
++ unsigned int num_planes;
++ unsigned int colorspace_mask;
++ enum v4l2_colorspace colorspace_default;
++};
++
++#define V4L2_COLORSPACE_MASK(colorspace) BIT(colorspace)
++
++#define V4L2_COLORSPACE_MASK_JPEG V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_JPEG)
++#define V4L2_COLORSPACE_MASK_SMPTE170M V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SMPTE170M)
++#define V4L2_COLORSPACE_MASK_REC709 V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_REC709)
++#define V4L2_COLORSPACE_MASK_SRGB V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_SRGB)
++#define V4L2_COLORSPACE_MASK_RAW V4L2_COLORSPACE_MASK(V4L2_COLORSPACE_RAW)
++
++/*
++ * All three colour spaces JPEG, SMPTE170M and REC709 are fundamentally sRGB
++ * underneath (as near as makes no difference to us), just with different YCbCr
++ * encodings. Therefore the ISP can generate sRGB on its main output and any of
++ * the others on its low resolution output. Applications should, when using both
++ * outputs, program the colour spaces on them to be the same, matching whatever
++ * is requested for the low resolution output, even if the main output is
++ * producing an RGB format. In turn this requires us to allow all these colour
++ * spaces for every YUV/RGB output format.
++ */
++#define V4L2_COLORSPACE_MASK_ALL_SRGB (V4L2_COLORSPACE_MASK_JPEG | \
++ V4L2_COLORSPACE_MASK_SRGB | \
++ V4L2_COLORSPACE_MASK_SMPTE170M | \
++ V4L2_COLORSPACE_MASK_REC709)
++
++static const struct pisp_be_format supported_formats[] = {
++ /* Single plane YUV formats */
++ {
++ .fourcc = V4L2_PIX_FMT_YUV420,
++ /* 128 alignment to ensure U/V planes are 64 byte aligned. */
++ .align = 128,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_JPEG,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YVU420,
++ /* 128 alignment to ensure U/V planes are 64 byte aligned. */
++ .align = 128,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_NV12,
++ .align = 32,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.5) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_NV21,
++ .align = 32,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.5) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .align = 64,
++ .bit_depth = 16,
++ .plane_factor = { P3(1) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_UYVY,
++ .align = 64,
++ .bit_depth = 16,
++ .plane_factor = { P3(1) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YVYU,
++ .align = 64,
++ .bit_depth = 16,
++ .plane_factor = { P3(1) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_VYUY,
++ .align = 64,
++ .bit_depth = 16,
++ .plane_factor = { P3(1) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ /* Multiplane YUV formats */
++ {
++ .fourcc = V4L2_PIX_FMT_YUV420M,
++ .align = 64,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
++ .num_planes = 3,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_JPEG,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_NV12M,
++ .align = 32,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.5) },
++ .num_planes = 2,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_NV21M,
++ .align = 32,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.5) },
++ .num_planes = 2,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YVU420M,
++ .align = 64,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.25), P3(0.25) },
++ .num_planes = 3,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YUV422M,
++ .align = 64,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.5), P3(0.5) },
++ .num_planes = 3,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_JPEG,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YVU422M,
++ .align = 64,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(0.5), P3(0.5) },
++ .num_planes = 3,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YUV444M,
++ .align = 64,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(1), P3(1) },
++ .num_planes = 3,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_JPEG,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YVU444M,
++ .align = 64,
++ .bit_depth = 8,
++ .plane_factor = { P3(1), P3(1), P3(1) },
++ .num_planes = 3,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SMPTE170M,
++ },
++ /* RGB formats */
++ {
++ .fourcc = V4L2_PIX_FMT_RGB24,
++ .align = 32,
++ .bit_depth = 24,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SRGB,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_BGR24,
++ .align = 32,
++ .bit_depth = 24,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SRGB,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_XBGR32,
++ .align = 64,
++ .bit_depth = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SRGB,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_RGBX32,
++ .align = 64,
++ .bit_depth = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SRGB,
++ },
++ /* Bayer formats - 8-bit */
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB8,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR8,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG8,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG8,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ /* Bayer formats - 16-bit */
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB16,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR16,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG16,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG16,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ /* Bayer formats unpacked to 16bpp */
++ /* 10 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB10,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR10,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG10,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG10,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ /* 12 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB12,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR12,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG12,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG12,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ /* 14 bit */
++ .fourcc = V4L2_PIX_FMT_SRGGB14,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR14,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG14,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG14,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ /* Bayer formats - 16-bit PiSP Compressed */
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++};
++
++static const struct pisp_be_format meta_out_supported_formats[] = {
++ /* Configuration buffer format. */
++ {
++ .fourcc = V4L2_META_FMT_RPI_BE_CFG,
++ },
++};
++
++#endif /* _PISP_BE_FORMATS_ */
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1452,6 +1452,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_NV12M_10BE_8L128: descr = "10-bit NV12M (8x128 Linear, BE)"; break;
+ case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break;
+ case V4L2_META_FMT_BCM2835_ISP_STATS: descr = "BCM2835 ISP Image Statistics"; break;
++ case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP Config format"; break;
+
+ default:
+ /* Compressed formats */
+@@ -1503,6 +1504,7 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_MT21C: descr = "Mediatek Compressed Format"; break;
+ case V4L2_PIX_FMT_QC08C: descr = "QCOM Compressed 8-bit Format"; break;
+ case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
++ case V4L2_PIX_FMT_RPI_BE: descr = "PiSP Opaque Format"; break;
+ default:
+ if (fmt->description[0])
+ return;
+--- /dev/null
++++ b/include/media/raspberrypi/pisp_common.h
+@@ -0,0 +1,65 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Raspberry Pi PiSP common configuration definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi (Trading) Ltd.
++ *
++ */
++#ifndef _PISP_COMMON_H_
++#define _PISP_COMMON_H_
++
++#include <linux/types.h>
++
++#include "pisp_types.h"
++
++struct pisp_bla_config {
++ uint16_t black_level_r;
++ uint16_t black_level_gr;
++ uint16_t black_level_gb;
++ uint16_t black_level_b;
++ uint16_t output_black_level;
++ uint8_t pad[2];
++};
++
++struct pisp_wbg_config {
++ uint16_t gain_r;
++ uint16_t gain_g;
++ uint16_t gain_b;
++ uint8_t pad[2];
++};
++
++struct pisp_compress_config {
++ /* value subtracted from incoming data */
++ uint16_t offset;
++ uint8_t pad;
++ /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++ uint8_t mode;
++};
++
++struct pisp_decompress_config {
++ /* value added to reconstructed data */
++ uint16_t offset;
++ uint8_t pad;
++ /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++ uint8_t mode;
++};
++
++enum pisp_axi_flags {
++ /* round down bursts to end at a 32-byte boundary, to align following bursts */
++ PISP_AXI_FLAG_ALIGN = 128,
++ /* for FE writer: force WSTRB high, to pad output to 16-byte boundary */
++ PISP_AXI_FLAG_PAD = 64,
++ /* for FE writer: Use Output FIFO level to trigger "panic" */
++ PISP_AXI_FLAG_PANIC = 32
++};
++
++struct pisp_axi_config {
++ /* burst length minus one, which must be in the range 0:15; OR'd with flags */
++ uint8_t maxlen_flags;
++ /* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */
++ uint8_t cache_prot;
++ /* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */
++ uint16_t qos;
++};
++
++#endif /* _PISP_COMMON_H_ */
+--- /dev/null
++++ b/include/media/raspberrypi/pisp_types.h
+@@ -0,0 +1,144 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * Raspberry Pi PiSP common types.
++ *
++ * Copyright (C) 2021 - Raspberry Pi (Trading) Ltd.
++ *
++ */
++#ifndef _PISP_TYPES_H_
++#define _PISP_TYPES_H_
++
++/* This definition must match the format description in the hardware exactly! */
++struct pisp_image_format_config {
++ /* size in pixels */
++ uint16_t width, height;
++ /* must match struct pisp_image_format below */
++ uint32_t format;
++ int32_t stride;
++ /* some planar image formats will need a second stride */
++ int32_t stride2;
++};
++
++static_assert(sizeof(struct pisp_image_format_config) == 16);
++
++enum pisp_bayer_order {
++ /*
++ * Note how bayer_order&1 tells you if G is on the even pixels of the
++ * checkerboard or not, and bayer_order&2 tells you if R is on the even
++ * rows or is swapped with B. Note that if the top (of the 8) bits is
++ * set, this denotes a monochrome or greyscale image, and the lower bits
++ * should all be ignored.
++ */
++ PISP_BAYER_ORDER_RGGB = 0,
++ PISP_BAYER_ORDER_GBRG = 1,
++ PISP_BAYER_ORDER_BGGR = 2,
++ PISP_BAYER_ORDER_GRBG = 3,
++ PISP_BAYER_ORDER_GREYSCALE = 128
++};
++
++enum pisp_image_format {
++ /*
++ * Precise values are mostly tbd. Generally these will be portmanteau
++ * values comprising bit fields and flags. This format must be shared
++ * throughout the PiSP.
++ */
++ PISP_IMAGE_FORMAT_BPS_8 = 0x00000000,
++ PISP_IMAGE_FORMAT_BPS_10 = 0x00000001,
++ PISP_IMAGE_FORMAT_BPS_12 = 0x00000002,
++ PISP_IMAGE_FORMAT_BPS_16 = 0x00000003,
++ PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003,
++
++ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000,
++ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010,
++ PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020,
++ PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030,
++
++ PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000,
++ PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100,
++ PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200,
++ PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300,
++
++ PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000,
++ PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000,
++
++ PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000,
++ PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000,
++ PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000,
++ PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000,
++ PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000,
++ PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000,
++ PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000,
++ PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000,
++ PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000,
++ PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000,
++
++ PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000,
++
++ PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000,
++ PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000,
++ PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000,
++ PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000,
++ PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000,
++
++ /* Lastly a few specific instantiations of the above. */
++ PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16,
++ PISP_IMAGE_FORMAT_THREE_16 =
++ PISP_IMAGE_FORMAT_BPS_16 | PISP_IMAGE_FORMAT_THREE_CHANNEL
++};
++
++#define PISP_IMAGE_FORMAT_bps_8(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8)
++#define PISP_IMAGE_FORMAT_bps_10(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10)
++#define PISP_IMAGE_FORMAT_bps_12(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12)
++#define PISP_IMAGE_FORMAT_bps_16(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16)
++#define PISP_IMAGE_FORMAT_bps(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) ? \
++ 8 + (2 << (((fmt)&PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : \
++ 8)
++#define PISP_IMAGE_FORMAT_shift(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1)
++#define PISP_IMAGE_FORMAT_three_channel(fmt) \
++ ((fmt)&PISP_IMAGE_FORMAT_THREE_CHANNEL)
++#define PISP_IMAGE_FORMAT_single_channel(fmt) \
++ (!((fmt)&PISP_IMAGE_FORMAT_THREE_CHANNEL))
++#define PISP_IMAGE_FORMAT_compressed(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_COMPRESSION_MASK) != \
++ PISP_IMAGE_FORMAT_UNCOMPRESSED)
++#define PISP_IMAGE_FORMAT_sampling_444(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_SAMPLING_MASK) == \
++ PISP_IMAGE_FORMAT_SAMPLING_444)
++#define PISP_IMAGE_FORMAT_sampling_422(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_SAMPLING_MASK) == \
++ PISP_IMAGE_FORMAT_SAMPLING_422)
++#define PISP_IMAGE_FORMAT_sampling_420(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_SAMPLING_MASK) == \
++ PISP_IMAGE_FORMAT_SAMPLING_420)
++#define PISP_IMAGE_FORMAT_order_normal(fmt) \
++ (!((fmt)&PISP_IMAGE_FORMAT_ORDER_SWAPPED))
++#define PISP_IMAGE_FORMAT_order_swapped(fmt) \
++ ((fmt)&PISP_IMAGE_FORMAT_ORDER_SWAPPED)
++#define PISP_IMAGE_FORMAT_interleaved(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_PLANARITY_MASK) == \
++ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED)
++#define PISP_IMAGE_FORMAT_semiplanar(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_PLANARITY_MASK) == \
++ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR)
++#define PISP_IMAGE_FORMAT_planar(fmt) \
++ (((fmt)&PISP_IMAGE_FORMAT_PLANARITY_MASK) == \
++ PISP_IMAGE_FORMAT_PLANARITY_PLANAR)
++#define PISP_IMAGE_FORMAT_wallpaper(fmt) \
++ ((fmt)&PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
++#define PISP_IMAGE_FORMAT_HOG(fmt) \
++ ((fmt) & \
++ (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED))
++
++#define PISP_WALLPAPER_WIDTH 128 // in bytes
++
++#endif /* _PISP_TYPES_H_ */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -793,6 +793,9 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_IPU3_SGRBG10 v4l2_fourcc('i', 'p', '3', 'G') /* IPU3 packed 10-bit GRBG bayer */
+ #define V4L2_PIX_FMT_IPU3_SRGGB10 v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
+
++/* The pixel format for all our buffers (the precise format is found in the config buffer). */
++#define V4L2_PIX_FMT_RPI_BE v4l2_fourcc('R', 'P', 'B', 'P')
++
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
+ #define V4L2_SDR_FMT_CU16LE v4l2_fourcc('C', 'U', '1', '6') /* IQ u16le */
+@@ -822,6 +825,9 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_RK_ISP1_PARAMS v4l2_fourcc('R', 'K', '1', 'P') /* Rockchip ISP1 3A Parameters */
+ #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+
++/* The metadata format identifier for our configuration buffers. */
++#define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C')
++
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
+
--- /dev/null
+From 89b748416358e4e04765b9a4f20e1c3d256b9d9e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 28 Jul 2021 11:13:39 +0100
+Subject: [PATCH] irqchip: irq-bcm2712-mip: Support for 2712's MIP
+
+irqchip: irq-bcm2712-mip: specify bitmap search size as ilog2(N) not N
+
+Freeing also has the same interface.
+
+irqchip: irq-bcm2712-mip: Fix build warnings
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+irqchip: bcm2712-mip: add a quick hack to optionally shift MSI vectors
+
+There are two MIP peripherals in bcm2712, the first gets a first-class
+treatment where 64 consecutive GIC SPIs are assigned to all 64 output
+vectors. The second gets an agglomeration of 17 GIC SPIs, but only 8 of
+these are consecutive starting at the 8th output vector.
+
+For now, allow the use of this smaller contiguous range within a larger
+whole.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/irqchip/Kconfig | 8 +
+ drivers/irqchip/Makefile | 1 +
+ drivers/irqchip/irq-bcm2712-mip.c | 325 ++++++++++++++++++++++++++++++
+ 3 files changed, 334 insertions(+)
+ create mode 100644 drivers/irqchip/irq-bcm2712-mip.c
+
+--- a/drivers/irqchip/Kconfig
++++ b/drivers/irqchip/Kconfig
+@@ -109,6 +109,14 @@ config I8259
+ bool
+ select IRQ_DOMAIN
+
++config BCM2712_MIP
++ bool "Broadcom 2712 MSI-X Interrupt Peripheral support"
++ depends on ARM_GIC
++ select GENERIC_IRQ_CHIP
++ select IRQ_DOMAIN
++ help
++ Enable support for the Broadcom BCM2712 MSI-X target peripheral.
++
+ config BCM6345_L1_IRQ
+ bool
+ select GENERIC_IRQ_CHIP
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -63,6 +63,7 @@ obj-$(CONFIG_XTENSA_MX) += irq-xtensa-
+ obj-$(CONFIG_XILINX_INTC) += irq-xilinx-intc.o
+ obj-$(CONFIG_IRQ_CROSSBAR) += irq-crossbar.o
+ obj-$(CONFIG_SOC_VF610) += irq-vf610-mscm-ir.o
++obj-$(CONFIG_BCM2712_MIP) += irq-bcm2712-mip.o
+ obj-$(CONFIG_BCM6345_L1_IRQ) += irq-bcm6345-l1.o
+ obj-$(CONFIG_BCM7038_L1_IRQ) += irq-bcm7038-l1.o
+ obj-$(CONFIG_BCM7120_L2_IRQ) += irq-bcm7120-l2.o
+--- /dev/null
++++ b/drivers/irqchip/irq-bcm2712-mip.c
+@@ -0,0 +1,325 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * Copyright (C) 2021 Raspberry Pi Ltd., All Rights Reserved.
++ */
++
++#include <linux/pci.h>
++#include <linux/msi.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_irq.h>
++#include <linux/of_pci.h>
++
++#include <linux/irqchip.h>
++
++#define MIP_INT_RAISED 0x00
++#define MIP_INT_CLEARED 0x10
++#define MIP_INT_CFGL_HOST 0x20
++#define MIP_INT_CFGH_HOST 0x30
++#define MIP_INT_MASKL_HOST 0x40
++#define MIP_INT_MASKH_HOST 0x50
++#define MIP_INT_MASKL_VPU 0x60
++#define MIP_INT_MASKH_VPU 0x70
++#define MIP_INT_STATUSL_HOST 0x80
++#define MIP_INT_STATUSH_HOST 0x90
++#define MIP_INT_STATUSL_VPU 0xa0
++#define MIP_INT_STATUSH_VPU 0xb0
++
++struct mip_priv {
++ spinlock_t msi_map_lock;
++ spinlock_t hw_lock;
++ void * __iomem base;
++ phys_addr_t msg_addr;
++ u32 msi_base; /* The SGI number that MSIs start */
++ u32 num_msis; /* The number of SGIs for MSIs */
++ u32 msi_offset; /* Shift the allocated msi up by N */
++ unsigned long *msi_map;
++};
++
++static void mip_mask_msi_irq(struct irq_data *d)
++{
++ pci_msi_mask_irq(d);
++ irq_chip_mask_parent(d);
++}
++
++static void mip_unmask_msi_irq(struct irq_data *d)
++{
++ pci_msi_unmask_irq(d);
++ irq_chip_unmask_parent(d);
++}
++
++static void mip_compose_msi_msg(struct irq_data *d, struct msi_msg *msg)
++{
++ struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++
++ msg->address_hi = upper_32_bits(priv->msg_addr);
++ msg->address_lo = lower_32_bits(priv->msg_addr);
++ msg->data = d->hwirq;
++}
++
++// The "bus-specific" irq_chip (the MIP doesn't _have_ to be used with PCIe)
++
++static struct irq_chip mip_msi_irq_chip = {
++ .name = "MIP-MSI",
++ .irq_unmask = mip_unmask_msi_irq,
++ .irq_mask = mip_mask_msi_irq,
++ .irq_eoi = irq_chip_eoi_parent,
++ .irq_set_affinity = irq_chip_set_affinity_parent,
++};
++
++static struct msi_domain_info mip_msi_domain_info = {
++ .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS |
++ MSI_FLAG_PCI_MSIX),
++ .chip = &mip_msi_irq_chip,
++};
++
++// The "middle" irq_chip (the hardware control part)
++
++static struct irq_chip mip_irq_chip = {
++ .name = "MIP",
++ .irq_mask = irq_chip_mask_parent,
++ .irq_unmask = irq_chip_unmask_parent,
++ .irq_eoi = irq_chip_eoi_parent,
++ .irq_set_affinity = irq_chip_set_affinity_parent,
++ .irq_set_type = irq_chip_set_type_parent,
++ .irq_compose_msi_msg = mip_compose_msi_msg,
++};
++
++
++// And a domain to connect it to its parent (the GIC)
++
++static int mip_irq_domain_alloc(struct irq_domain *domain,
++ unsigned int virq, unsigned int nr_irqs,
++ void *args)
++{
++ struct mip_priv *priv = domain->host_data;
++ struct irq_fwspec fwspec;
++ struct irq_data *irqd;
++ int hwirq, ret, i;
++
++ spin_lock(&priv->msi_map_lock);
++
++ hwirq = bitmap_find_free_region(priv->msi_map, priv->num_msis, ilog2(nr_irqs));
++
++ spin_unlock(&priv->msi_map_lock);
++
++ if (hwirq < 0)
++ return -ENOSPC;
++
++ hwirq += priv->msi_offset;
++ fwspec.fwnode = domain->parent->fwnode;
++ fwspec.param_count = 3;
++ fwspec.param[0] = 0;
++ fwspec.param[1] = hwirq + priv->msi_base;
++ fwspec.param[2] = IRQ_TYPE_EDGE_RISING;
++
++ ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &fwspec);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < nr_irqs; i++) {
++ irqd = irq_domain_get_irq_data(domain->parent, virq + i);
++ irqd->chip->irq_set_type(irqd, IRQ_TYPE_EDGE_RISING);
++
++ irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
++ &mip_irq_chip, priv);
++ irqd = irq_get_irq_data(virq + i);
++ irqd_set_single_target(irqd);
++ irqd_set_affinity_on_activate(irqd);
++ }
++
++ return 0;
++}
++
++static void mip_irq_domain_free(struct irq_domain *domain,
++ unsigned int virq, unsigned int nr_irqs)
++{
++ struct irq_data *d = irq_domain_get_irq_data(domain, virq);
++ struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++
++ irq_domain_free_irqs_parent(domain, virq, nr_irqs);
++ d->hwirq -= priv->msi_offset;
++
++ spin_lock(&priv->msi_map_lock);
++
++ bitmap_release_region(priv->msi_map, d->hwirq, ilog2(nr_irqs));
++
++ spin_unlock(&priv->msi_map_lock);
++}
++
++#if 0
++static int mip_irq_domain_activate(struct irq_domain *domain,
++ struct irq_data *d, bool reserve)
++{
++ struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++ unsigned long flags;
++ unsigned int irq = d->hwirq;
++ void *__iomem reg = priv->base +
++ ((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST);
++ u32 val;
++
++ spin_lock_irqsave(&priv->hw_lock, flags);
++ val = readl(reg);
++ val &= ~(1 << (irq % 32)); // Clear the mask
++ writel(val, reg);
++ spin_unlock_irqrestore(&priv->hw_lock, flags);
++ return 0;
++}
++
++static void mip_irq_domain_deactivate(struct irq_domain *domain,
++ struct irq_data *d)
++{
++ struct mip_priv *priv = irq_data_get_irq_chip_data(d);
++ unsigned long flags;
++ unsigned int irq = d->hwirq - priv->msi_base;
++ void *__iomem reg = priv->base +
++ ((irq < 32) ? MIP_INT_MASKL_HOST : MIP_INT_MASKH_HOST);
++ u32 val;
++
++ spin_lock_irqsave(&priv->hw_lock, flags);
++ val = readl(reg);
++ val |= (1 << (irq % 32)); // Mask it out
++ writel(val, reg);
++ spin_unlock_irqrestore(&priv->hw_lock, flags);
++}
++#endif
++
++static const struct irq_domain_ops mip_irq_domain_ops = {
++ .alloc = mip_irq_domain_alloc,
++ .free = mip_irq_domain_free,
++ //.activate = mip_irq_domain_activate,
++ //.deactivate = mip_irq_domain_deactivate,
++};
++
++static int mip_init_domains(struct mip_priv *priv,
++ struct device_node *node)
++{
++ struct irq_domain *middle_domain, *msi_domain, *gic_domain;
++ struct device_node *gic_node;
++
++ gic_node = of_irq_find_parent(node);
++ if (!gic_node) {
++ pr_err("Failed to find the GIC node\n");
++ return -ENODEV;
++ }
++
++ gic_domain = irq_find_host(gic_node);
++ if (!gic_domain) {
++ pr_err("Failed to find the GIC domain\n");
++ return -ENXIO;
++ }
++
++ middle_domain = irq_domain_add_tree(NULL,
++ &mip_irq_domain_ops,
++ priv);
++ if (!middle_domain) {
++ pr_err("Failed to create the MIP middle domain\n");
++ return -ENOMEM;
++ }
++
++ middle_domain->parent = gic_domain;
++
++ msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
++ &mip_msi_domain_info,
++ middle_domain);
++ if (!msi_domain) {
++ pr_err("Failed to create MSI domain\n");
++ irq_domain_remove(middle_domain);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++static int __init mip_of_msi_init(struct device_node *node,
++ struct device_node *parent)
++{
++ struct mip_priv *priv;
++ struct resource res;
++ int ret;
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ spin_lock_init(&priv->msi_map_lock);
++ spin_lock_init(&priv->hw_lock);
++
++ ret = of_address_to_resource(node, 0, &res);
++ if (ret) {
++ pr_err("Failed to allocate resource\n");
++ goto err_priv;
++ }
++
++ if (of_property_read_u32(node, "brcm,msi-base-spi", &priv->msi_base)) {
++ pr_err("Unable to parse MSI base\n");
++ ret = -EINVAL;
++ goto err_priv;
++ }
++
++ if (of_property_read_u32(node, "brcm,msi-num-spis", &priv->num_msis)) {
++ pr_err("Unable to parse MSI numbers\n");
++ ret = -EINVAL;
++ goto err_priv;
++ }
++
++ if (of_property_read_u32(node, "brcm,msi-offset", &priv->msi_offset))
++ priv->msi_offset = 0;
++
++ if (of_property_read_u64(node, "brcm,msi-pci-addr", &priv->msg_addr)) {
++ pr_err("Unable to parse MSI address\n");
++ ret = -EINVAL;
++ goto err_priv;
++ }
++
++ priv->base = ioremap(res.start, resource_size(&res));
++ if (!priv->base) {
++ pr_err("Failed to ioremap regs\n");
++ ret = -ENOMEM;
++ goto err_priv;
++ }
++
++ priv->msi_map = kcalloc(BITS_TO_LONGS(priv->num_msis),
++ sizeof(*priv->msi_map),
++ GFP_KERNEL);
++ if (!priv->msi_map) {
++ ret = -ENOMEM;
++ goto err_base;
++ }
++
++ pr_debug("Registering %d msixs, starting at %d\n",
++ priv->num_msis, priv->msi_base);
++
++ /*
++ * Begin with all MSI-Xs masked in for the host, masked out for the
++ * VPU, and edge-triggered.
++ */
++ writel(0, priv->base + MIP_INT_MASKL_HOST);
++ writel(0, priv->base + MIP_INT_MASKH_HOST);
++ writel(~0, priv->base + MIP_INT_MASKL_VPU);
++ writel(~0, priv->base + MIP_INT_MASKH_VPU);
++ writel(~0, priv->base + MIP_INT_CFGL_HOST);
++ writel(~0, priv->base + MIP_INT_CFGH_HOST);
++
++ ret = mip_init_domains(priv, node);
++ if (ret) {
++ pr_err("Failed to allocate msi_map\n");
++ goto err_map;
++ }
++
++ return 0;
++
++err_map:
++ kfree(priv->msi_map);
++
++err_base:
++ iounmap(priv->base);
++
++err_priv:
++ kfree(priv);
++
++ pr_err("%s: failed - err %d\n", __func__, ret);
++
++ return ret;
++}
++IRQCHIP_DECLARE(bcm_mip, "brcm,bcm2712-mip-intc", mip_of_msi_init);
--- /dev/null
+From 87b1126181f79fb2558652af0d7fafd9deaab5f3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 7 Sep 2021 14:49:00 +0100
+Subject: [PATCH] reset: reset-brcmstb-rescal: Support shared use
+
+reset_control_reset should not be used with shared reset controllers.
+Add support for reset_control_assert and _deassert to get the desired
+behaviour and avoid ugly warnings in the kernel log.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/reset/reset-brcmstb-rescal.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/reset/reset-brcmstb-rescal.c
++++ b/drivers/reset/reset-brcmstb-rescal.c
+@@ -20,6 +20,7 @@ struct brcm_rescal_reset {
+ struct reset_controller_dev rcdev;
+ };
+
++/* Also doubles a deassert */
+ static int brcm_rescal_reset_set(struct reset_controller_dev *rcdev,
+ unsigned long id)
+ {
+@@ -52,6 +53,13 @@ static int brcm_rescal_reset_set(struct
+ return 0;
+ }
+
++/* A dummy function - deassert/reset does all the work */
++static int brcm_rescal_reset_assert(struct reset_controller_dev *rcdev,
++ unsigned long id)
++{
++ return 0;
++}
++
+ static int brcm_rescal_reset_xlate(struct reset_controller_dev *rcdev,
+ const struct of_phandle_args *reset_spec)
+ {
+@@ -61,6 +69,8 @@ static int brcm_rescal_reset_xlate(struc
+
+ static const struct reset_control_ops brcm_rescal_reset_ops = {
+ .reset = brcm_rescal_reset_set,
++ .deassert = brcm_rescal_reset_set,
++ .assert = brcm_rescal_reset_assert,
+ };
+
+ static int brcm_rescal_reset_probe(struct platform_device *pdev)
--- /dev/null
+From bd36586dd9e05bde8e23dc3d99771269b48b65f8 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 10 Sep 2021 17:20:45 +0100
+Subject: [PATCH] net: macb: Also set DMA coherent mask
+
+macb: Add device tree properties that allow configuration of the AXI max pipeline register
+
+net: macb: add support for ethtool interrupt moderation configuration
+
+Only global throttling of rx or tx by time quanta is supported.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+macb: add platform device shutdown function. Prevents AXI master over PCIE from hanging when the host is rebooted.
+
+net: macb: increase polling interval for MDIO completion
+
+MDIO is a slow bus (single-digit MHz). Polling at 1us intervals
+is a bit aggressive, so increase to 100us as the transaction
+usually takes 100-200us to complete.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+net: macb: Several patches for RP1
+
+64-bit RX fix
+
+Also set DMA coherent mask
+
+Add device tree properties that allow configuration of the AXI max
+pipeline register
+
+Add support for ethtool interrupt moderation configuration
+
+Only global throttling of rx or tx by time quanta is supported.
+
+Add platform device shutdown function. Prevents AXI master over PCIE
+from hanging when the host is rebooted.
+
+Increase polling interval for MDIO completion
+
+MDIO is a slow bus (single-digit MHz). Polling at 1us intervals
+is a bit aggressive, so increase to 100us as the transaction
+usually takes 100-200us to complete.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+net: macb: Support the phy-reset-gpios property
+
+Allow a PHY to be reset with an optional GPIO. The reset duration can
+be specified in milliseconds - the default is 10ms.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+drivers: net: macb: close device on driver shutdown
+
+Fix some suspicious locking and instead call into macb_close, which
+deregisters and frees all resources the corresponding macb_open
+claimed.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+net: macb: add hack to prevent TX stalls in a quiet system
+
+See https://github.com/raspberrypi/linux-2712/issues/89
+
+There is some critical window during TX where a further write to the
+TSTART bit while TX is active does not cause newly queued TX descriptors
+to be consumed.
+
+For now "wait a bit, then try anyway" seems to work.
+
+Requires further investigation, but this unsticks NFS reliably.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+net: macb: set default interrupt moderation for GEM hardware
+
+Defaulting to intmod = 0 is antisocial, as the MAC can generate over
+130,000 interrupts per second. 50us is a sensible default.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/net/ethernet/cadence/macb.h | 25 ++++
+ drivers/net/ethernet/cadence/macb_main.c | 151 ++++++++++++++++++++++-
+ 2 files changed, 174 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/ethernet/cadence/macb.h
++++ b/drivers/net/ethernet/cadence/macb.h
+@@ -84,6 +84,8 @@
+ #define GEM_DMACFG 0x0010 /* DMA Configuration */
+ #define GEM_JML 0x0048 /* Jumbo Max Length */
+ #define GEM_HS_MAC_CONFIG 0x0050 /* GEM high speed config */
++#define GEM_AMP 0x0054 /* AXI Max Pipeline */
++#define GEM_INTMOD 0x005c /* Interrupt moderation */
+ #define GEM_HRB 0x0080 /* Hash Bottom */
+ #define GEM_HRT 0x0084 /* Hash Top */
+ #define GEM_SA1B 0x0088 /* Specific1 Bottom */
+@@ -346,6 +348,21 @@
+ #define GEM_ADDR64_OFFSET 30 /* Address bus width - 64b or 32b */
+ #define GEM_ADDR64_SIZE 1
+
++/* Bitfields in AMP */
++#define GEM_AR2R_MAX_PIPE_OFFSET 0 /* Maximum number of outstanding AXI read requests */
++#define GEM_AR2R_MAX_PIPE_SIZE 8
++#define GEM_AW2W_MAX_PIPE_OFFSET 8 /* Maximum number of outstanding AXI write requests */
++#define GEM_AW2W_MAX_PIPE_SIZE 8
++#define GEM_AW2B_FILL_OFFSET 16 /* Select wether the max AW2W transactions operates between: */
++#define GEM_AW2B_FILL_AW2W 0 /* 0: the AW to W AXI channel */
++#define GEM_AW2B_FILL_AW2B 1 /* 1: AW to B channel */
++#define GEM_AW2B_FILL_SIZE 1
++
++/* Bitfields in INTMOD */
++#define GEM_RX_MODERATION_OFFSET 0 /* RX interrupt moderation */
++#define GEM_RX_MODERATION_SIZE 8
++#define GEM_TX_MODERATION_OFFSET 16 /* TX interrupt moderation */
++#define GEM_TX_MODERATION_SIZE 8
+
+ /* Bitfields in NSR */
+ #define MACB_NSR_LINK_OFFSET 0 /* pcs_link_state */
+@@ -798,6 +815,7 @@
+ })
+
+ #define MACB_READ_NSR(bp) macb_readl(bp, NSR)
++#define MACB_READ_TSR(bp) macb_readl(bp, TSR)
+
+ /* struct macb_dma_desc - Hardware DMA descriptor
+ * @addr: DMA address of data buffer
+@@ -1217,6 +1235,7 @@ struct macb_queue {
+ dma_addr_t tx_ring_dma;
+ struct work_struct tx_error_task;
+ bool txubr_pending;
++ bool tx_pending;
+ struct napi_struct napi_tx;
+
+ dma_addr_t rx_ring_dma;
+@@ -1286,9 +1305,15 @@ struct macb {
+
+ u32 caps;
+ unsigned int dma_burst_length;
++ u8 aw2w_max_pipe;
++ u8 ar2r_max_pipe;
++ bool use_aw2b_fill;
+
+ phy_interface_t phy_interface;
+
++ struct gpio_desc *phy_reset_gpio;
++ int phy_reset_ms;
++
+ /* AT91RM9200 transmit queue (1 on wire + 1 queued) */
+ struct macb_tx_skb rm9200_txq[2];
+ unsigned int max_tx_length;
+--- a/drivers/net/ethernet/cadence/macb_main.c
++++ b/drivers/net/ethernet/cadence/macb_main.c
+@@ -41,6 +41,9 @@
+ #include <linux/firmware/xlnx-zynqmp.h>
+ #include "macb.h"
+
++static unsigned int txdelay = 35;
++module_param(txdelay, uint, 0644);
++
+ /* This structure is only used for MACB on SiFive FU540 devices */
+ struct sifive_fu540_macb_mgmt {
+ void __iomem *reg;
+@@ -336,7 +339,7 @@ static int macb_mdio_wait_for_idle(struc
+ u32 val;
+
+ return readx_poll_timeout(MACB_READ_NSR, bp, val, val & MACB_BIT(IDLE),
+- 1, MACB_MDIO_TIMEOUT);
++ 100, MACB_MDIO_TIMEOUT);
+ }
+
+ static int macb_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
+@@ -442,6 +445,19 @@ mdio_pm_exit:
+ return status;
+ }
+
++static int macb_mdio_reset(struct mii_bus *bus)
++{
++ struct macb *bp = bus->priv;
++
++ if (bp->phy_reset_gpio) {
++ gpiod_set_value_cansleep(bp->phy_reset_gpio, 1);
++ msleep(bp->phy_reset_ms);
++ gpiod_set_value_cansleep(bp->phy_reset_gpio, 0);
++ }
++
++ return 0;
++}
++
+ static void macb_init_buffers(struct macb *bp)
+ {
+ struct macb_queue *queue;
+@@ -915,6 +931,7 @@ static int macb_mii_init(struct macb *bp
+ bp->mii_bus->name = "MACB_mii_bus";
+ bp->mii_bus->read = &macb_mdio_read;
+ bp->mii_bus->write = &macb_mdio_write;
++ bp->mii_bus->reset = &macb_mdio_reset;
+ snprintf(bp->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
+ bp->pdev->name, bp->pdev->id);
+ bp->mii_bus->priv = bp;
+@@ -1584,6 +1601,11 @@ static int macb_rx(struct macb_queue *qu
+
+ macb_init_rx_ring(queue);
+ queue_writel(queue, RBQP, queue->rx_ring_dma);
++#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
++ if (bp->hw_dma_cap & HW_DMA_CAP_64B)
++ macb_writel(bp, RBQPH,
++ upper_32_bits(queue->rx_ring_dma));
++#endif
+
+ macb_writel(bp, NCR, ctrl | MACB_BIT(RE));
+
+@@ -1884,8 +1906,9 @@ static irqreturn_t macb_interrupt(int ir
+ queue_writel(queue, ISR, MACB_BIT(TCOMP) |
+ MACB_BIT(TXUBR));
+
+- if (status & MACB_BIT(TXUBR)) {
++ if (status & MACB_BIT(TXUBR) || queue->tx_pending) {
+ queue->txubr_pending = true;
++ queue->tx_pending = 0;
+ wmb(); // ensure softirq can see update
+ }
+
+@@ -2332,6 +2355,11 @@ static netdev_tx_t macb_start_xmit(struc
+ skb_tx_timestamp(skb);
+
+ spin_lock_irq(&bp->lock);
++
++ /* TSTART write might get dropped, so make the IRQ retrigger a buffer read */
++ if (macb_readl(bp, TSR) & MACB_BIT(TGO))
++ queue->tx_pending = 1;
++
+ macb_writel(bp, NCR, macb_readl(bp, NCR) | MACB_BIT(TSTART));
+ spin_unlock_irq(&bp->lock);
+
+@@ -2699,6 +2727,37 @@ static void macb_configure_dma(struct ma
+ }
+ }
+
++static void gem_init_axi(struct macb *bp)
++{
++ u32 amp;
++
++ /* AXI pipeline setup - don't touch values unless specified in device
++ * tree. Some hardware could have reset values > 1.
++ */
++ amp = gem_readl(bp, AMP);
++
++ if (bp->use_aw2b_fill)
++ amp = GEM_BFINS(AW2B_FILL, bp->use_aw2b_fill, amp);
++ if (bp->aw2w_max_pipe)
++ amp = GEM_BFINS(AW2W_MAX_PIPE, bp->aw2w_max_pipe, amp);
++ if (bp->ar2r_max_pipe)
++ amp = GEM_BFINS(AR2R_MAX_PIPE, bp->ar2r_max_pipe, amp);
++
++ gem_writel(bp, AMP, amp);
++}
++
++static void gem_init_intmod(struct macb *bp)
++{
++ unsigned int throttle;
++ u32 intmod = 0;
++
++ /* Use sensible interrupt moderation thresholds (50us rx and tx) */
++ throttle = (1000 * 50) / 800;
++ intmod = GEM_BFINS(TX_MODERATION, throttle, intmod);
++ intmod = GEM_BFINS(RX_MODERATION, throttle, intmod);
++ gem_writel(bp, INTMOD, intmod);
++}
++
+ static void macb_init_hw(struct macb *bp)
+ {
+ u32 config;
+@@ -2727,6 +2786,11 @@ static void macb_init_hw(struct macb *bp
+ if (bp->caps & MACB_CAPS_JUMBO)
+ bp->rx_frm_len_mask = MACB_RX_JFRMLEN_MASK;
+
++ if (macb_is_gem(bp)) {
++ gem_init_axi(bp);
++ gem_init_intmod(bp);
++ }
++
+ macb_configure_dma(bp);
+ }
+
+@@ -3072,6 +3136,52 @@ static void gem_get_ethtool_strings(stru
+ }
+ }
+
++static int gem_set_coalesce(struct net_device *dev,
++ struct ethtool_coalesce *ec,
++ struct kernel_ethtool_coalesce *kernel_coal,
++ struct netlink_ext_ack *extack)
++{
++ struct macb *bp = netdev_priv(dev);
++ unsigned int tx_throttle;
++ unsigned int rx_throttle;
++ u32 intmod = 0;
++
++ /* GEM has simple IRQ throttling support. RX and TX interrupts
++ * are separately moderated on 800ns quantums, with no support
++ * for frame coalescing.
++ */
++
++ /* Max is 255 * 0.8us = 204us. Zero implies no moderation. */
++ if (ec->rx_coalesce_usecs > 204 || ec->tx_coalesce_usecs > 204)
++ return -EINVAL;
++
++ tx_throttle = (1000 * ec->tx_coalesce_usecs) / 800;
++ rx_throttle = (1000 * ec->rx_coalesce_usecs) / 800;
++
++ intmod = GEM_BFINS(TX_MODERATION, tx_throttle, intmod);
++ intmod = GEM_BFINS(RX_MODERATION, rx_throttle, intmod);
++
++ gem_writel(bp, INTMOD, intmod);
++
++ return 0;
++}
++
++static int gem_get_coalesce(struct net_device *dev,
++ struct ethtool_coalesce *ec,
++ struct kernel_ethtool_coalesce *kernel_coal,
++ struct netlink_ext_ack *extack)
++{
++ struct macb *bp = netdev_priv(dev);
++ u32 intmod;
++
++ intmod = gem_readl(bp, INTMOD);
++
++ ec->tx_coalesce_usecs = (GEM_BFEXT(TX_MODERATION, intmod) * 800) / 1000;
++ ec->rx_coalesce_usecs = (GEM_BFEXT(RX_MODERATION, intmod) * 800) / 1000;
++
++ return 0;
++}
++
+ static struct net_device_stats *macb_get_stats(struct net_device *dev)
+ {
+ struct macb *bp = netdev_priv(dev);
+@@ -3664,6 +3774,8 @@ static const struct ethtool_ops macb_eth
+ };
+
+ static const struct ethtool_ops gem_ethtool_ops = {
++ .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS |
++ ETHTOOL_COALESCE_TX_USECS,
+ .get_regs_len = macb_get_regs_len,
+ .get_regs = macb_get_regs,
+ .get_wol = macb_get_wol,
+@@ -3673,6 +3785,8 @@ static const struct ethtool_ops gem_etht
+ .get_ethtool_stats = gem_get_ethtool_stats,
+ .get_strings = gem_get_ethtool_strings,
+ .get_sset_count = gem_get_sset_count,
++ .get_coalesce = gem_get_coalesce,
++ .set_coalesce = gem_set_coalesce,
+ .get_link_ksettings = macb_get_link_ksettings,
+ .set_link_ksettings = macb_set_link_ksettings,
+ .get_ringparam = macb_get_ringparam,
+@@ -4940,6 +5054,10 @@ static int macb_probe(struct platform_de
+
+ bp->usrio = macb_config->usrio;
+
++ device_property_read_u8(&pdev->dev, "cdns,aw2w-max-pipe", &bp->aw2w_max_pipe);
++ device_property_read_u8(&pdev->dev, "cdns,ar2r-max-pipe", &bp->ar2r_max_pipe);
++ bp->use_aw2b_fill = device_property_read_bool(&pdev->dev, "cdns,use-aw2b-fill");
++
+ spin_lock_init(&bp->lock);
+
+ /* setup capabilities */
+@@ -4995,6 +5113,21 @@ static int macb_probe(struct platform_de
+ else
+ bp->phy_interface = interface;
+
++ /* optional PHY reset-related properties */
++ bp->phy_reset_gpio = devm_gpiod_get_optional(&pdev->dev, "phy-reset",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(bp->phy_reset_gpio)) {
++ dev_err(&pdev->dev, "Failed to obtain phy-reset gpio\n");
++ err = PTR_ERR(bp->phy_reset_gpio);
++ goto err_out_free_netdev;
++ }
++
++ bp->phy_reset_ms = 10;
++ of_property_read_u32(np, "phy-reset-duration", &bp->phy_reset_ms);
++ /* A sane reset duration should not be longer than 1s */
++ if (bp->phy_reset_ms > 1000)
++ bp->phy_reset_ms = 1000;
++
+ /* IP specific init */
+ err = init(pdev);
+ if (err)
+@@ -5071,6 +5204,19 @@ static int macb_remove(struct platform_d
+ return 0;
+ }
+
++static void macb_shutdown(struct platform_device *pdev)
++{
++ struct net_device *dev;
++
++ dev = platform_get_drvdata(pdev);
++
++ rtnl_lock();
++ netif_device_detach(dev);
++ if (netif_running(dev))
++ dev_close(dev);
++ rtnl_unlock();
++}
++
+ static int __maybe_unused macb_suspend(struct device *dev)
+ {
+ struct net_device *netdev = dev_get_drvdata(dev);
+@@ -5285,6 +5431,7 @@ static const struct dev_pm_ops macb_pm_o
+ static struct platform_driver macb_driver = {
+ .probe = macb_probe,
+ .remove = macb_remove,
++ .shutdown = macb_shutdown,
+ .driver = {
+ .name = "macb",
+ .of_match_table = of_match_ptr(macb_dt_ids),
--- /dev/null
+From 4ffa5f2c5fc7854683964bb2f2bf23907c18213f Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 13 Sep 2021 11:14:32 +0100
+Subject: [PATCH] usb: dwc3: Set DMA and coherent masks early
+
+dwc3 allocates scratch and event buffers in the top-level driver. Hack the
+probe function to set the DMA mask before trying to allocate these.
+
+I think the event buffers are only used in device mode, but the scratch
+buffers may be used if core hibernation is enabled.
+
+usb: dwc3: add support for new DT quirks
+
+Apply the optional axi-pipe-limit and dis-in-autoretry-quirk properties
+during driver probe.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+phy: phy-brcm-usb: Add 2712 support
+
+usb: dwc3: if the host controller instance number is present in DT, use it
+
+If two instances of a dwc3 host controller are specified in devicetree,
+then the probe order may be arbitrary which results in the device names
+swapping on a per-boot basis.
+
+If a "usb" alias with the instance number is specified, then use
+that to construct the device name instead of autogenerating one.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+rp1 dwc3 changes
+
+drivers: usb: dwc3: allow setting GTXTHRCFG on dwc_usb3.0 hardware
+
+Equivalent register fields exist in the SuperSpeed Host version of the
+hardware, so allow the use of TX thresholds if specified in devicetree.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+drivers: usb: dwc3: remove downstream quirk dis-in-autoretry
+
+Upstream have unilaterally disabled the feature.
+
+Partially reverts 6e9142a26ee0fdc3a5adc49ed6cedc0b16ec2ed1 (downstream)
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/phy/broadcom/Kconfig | 2 +-
+ .../phy/broadcom/phy-brcm-usb-init-synopsys.c | 59 +++++++++++++++++++
+ drivers/phy/broadcom/phy-brcm-usb-init.h | 2 +
+ drivers/phy/broadcom/phy-brcm-usb.c | 18 +++++-
+ drivers/usb/dwc3/core.c | 52 ++++++++++++++++
+ drivers/usb/dwc3/core.h | 10 ++++
+ drivers/usb/dwc3/host.c | 17 ++++--
+ 7 files changed, 153 insertions(+), 7 deletions(-)
+
+--- a/drivers/phy/broadcom/Kconfig
++++ b/drivers/phy/broadcom/Kconfig
+@@ -93,7 +93,7 @@ config PHY_BRCM_SATA
+
+ config PHY_BRCM_USB
+ tristate "Broadcom STB USB PHY driver"
+- depends on ARCH_BCMBCA || ARCH_BRCMSTB || COMPILE_TEST
++ depends on ARCH_BCMBCA || ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
+ depends on OF
+ select GENERIC_PHY
+ select SOC_BRCMSTB if ARCH_BRCMSTB
+--- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
++++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
+@@ -318,6 +318,36 @@ static void usb_init_common_7216(struct
+ usb_init_common(params);
+ }
+
++static void usb_init_common_2712(struct brcm_usb_init_params *params)
++{
++ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
++ void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC];
++ u32 reg;
++
++ if (params->syscon_piarbctl)
++ syscon_piarbctl_init(params->syscon_piarbctl);
++
++ USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
++
++ usb_wake_enable_7211b0(params, false);
++
++ usb_init_common(params);
++
++ /*
++ * The BDC controller will get occasional failures with
++ * the default "Read Transaction Size" of 6 (1024 bytes).
++ * Set it to 4 (256 bytes).
++ */
++ if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) {
++ reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA);
++ reg &= ~BDC_EC_AXIRDA_RTS_MASK;
++ reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT);
++ brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA);
++ }
++
++ usb2_eye_fix_7211b0(params);
++}
++
+ static void usb_init_xhci(struct brcm_usb_init_params *params)
+ {
+ pr_debug("%s\n", __func__);
+@@ -363,6 +393,18 @@ static void usb_uninit_common_7211b0(str
+
+ }
+
++static void usb_uninit_common_2712(struct brcm_usb_init_params *params)
++{
++ void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
++
++ if (params->wake_enabled) {
++ USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN);
++ usb_wake_enable_7211b0(params, true);
++ } else {
++ USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
++ }
++}
++
+ static void usb_uninit_xhci(struct brcm_usb_init_params *params)
+ {
+
+@@ -417,6 +459,16 @@ static const struct brcm_usb_init_ops bc
+ .set_dual_select = usb_set_dual_select,
+ };
+
++static const struct brcm_usb_init_ops bcm2712_ops = {
++ .init_ipp = usb_init_ipp,
++ .init_common = usb_init_common_2712,
++ .init_xhci = usb_init_xhci,
++ .uninit_common = usb_uninit_common_2712,
++ .uninit_xhci = usb_uninit_xhci,
++ .get_dual_select = usb_get_dual_select,
++ .set_dual_select = usb_set_dual_select,
++};
++
+ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
+ {
+
+@@ -434,3 +486,10 @@ void brcm_usb_dvr_init_7211b0(struct brc
+ params->family_name = "7211";
+ params->ops = &bcm7211b0_ops;
+ }
++
++void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params)
++{
++ params->family_name = "2712";
++ params->ops = &bcm2712_ops;
++ params->suspend_with_clocks = true;
++}
+--- a/drivers/phy/broadcom/phy-brcm-usb-init.h
++++ b/drivers/phy/broadcom/phy-brcm-usb-init.h
+@@ -61,12 +61,14 @@ struct brcm_usb_init_params {
+ const struct brcm_usb_init_ops *ops;
+ struct regmap *syscon_piarbctl;
+ bool wake_enabled;
++ bool suspend_with_clocks;
+ };
+
+ void brcm_usb_dvr_init_4908(struct brcm_usb_init_params *params);
+ void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
+ void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
+ void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
++void brcm_usb_dvr_init_2712(struct brcm_usb_init_params *params);
+
+ static inline u32 brcm_usb_readl(void __iomem *addr)
+ {
+--- a/drivers/phy/broadcom/phy-brcm-usb.c
++++ b/drivers/phy/broadcom/phy-brcm-usb.c
+@@ -76,7 +76,7 @@ struct brcm_usb_phy_data {
+ };
+
+ static s8 *node_reg_names[BRCM_REGS_MAX] = {
+- "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
++ "ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
+ };
+
+ static int brcm_pm_notifier(struct notifier_block *notifier,
+@@ -315,6 +315,18 @@ static const struct match_chip_info chip
+ .optional_reg = BRCM_REGS_BDC_EC,
+ };
+
++static const struct match_chip_info chip_info_2712 = {
++ .init_func = &brcm_usb_dvr_init_2712,
++ .required_regs = {
++ BRCM_REGS_CTRL,
++ BRCM_REGS_XHCI_EC,
++ BRCM_REGS_XHCI_GBL,
++ BRCM_REGS_USB_MDIO,
++ -1,
++ },
++ .optional_reg = BRCM_REGS_BDC_EC,
++};
++
+ static const struct match_chip_info chip_info_7445 = {
+ .init_func = &brcm_usb_dvr_init_7445,
+ .required_regs = {
+@@ -338,6 +350,10 @@ static const struct of_device_id brcm_us
+ .data = &chip_info_7211b0,
+ },
+ {
++ .compatible = "brcm,bcm2712-usb-phy",
++ .data = &chip_info_2712,
++ },
++ {
+ .compatible = "brcm,brcmstb-usb-phy",
+ .data = &chip_info_7445,
+ },
+--- a/drivers/usb/dwc3/core.c
++++ b/drivers/usb/dwc3/core.c
+@@ -1216,6 +1216,24 @@ static void dwc3_config_threshold(struct
+ }
+ }
+
++static void dwc3_set_axi_pipe_limit(struct dwc3 *dwc)
++{
++ struct device *dev = dwc->dev;
++ u32 cfg;
++
++ if (!dwc->axi_pipe_limit)
++ return;
++ if (dwc->axi_pipe_limit > 16) {
++ dev_err(dev, "Invalid axi_pipe_limit property\n");
++ return;
++ }
++ cfg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG1);
++ cfg &= ~DWC3_GSBUSCFG1_PIPETRANSLIMIT(15);
++ cfg |= DWC3_GSBUSCFG1_PIPETRANSLIMIT(dwc->axi_pipe_limit - 1);
++
++ dwc3_writel(dwc->regs, DWC3_GSBUSCFG1, cfg);
++}
++
+ /**
+ * dwc3_core_init - Low-level initialization of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+@@ -1308,6 +1326,8 @@ static int dwc3_core_init(struct dwc3 *d
+
+ dwc3_set_incr_burst_type(dwc);
+
++ dwc3_set_axi_pipe_limit(dwc);
++
+ usb_phy_set_suspend(dwc->usb2_phy, 0);
+ usb_phy_set_suspend(dwc->usb3_phy, 0);
+ ret = phy_power_on(dwc->usb2_generic_phy);
+@@ -1541,6 +1561,7 @@ static void dwc3_get_properties(struct d
+ u8 tx_thr_num_pkt_prd = 0;
+ u8 tx_max_burst_prd = 0;
+ u8 tx_fifo_resize_max_num;
++ u8 axi_pipe_limit;
+ const char *usb_psy_name;
+ int ret;
+
+@@ -1563,6 +1584,9 @@ static void dwc3_get_properties(struct d
+ */
+ tx_fifo_resize_max_num = 6;
+
++ /* Default to 0 (don't override hardware defaults) */
++ axi_pipe_limit = 0;
++
+ dwc->maximum_speed = usb_get_maximum_speed(dev);
+ dwc->max_ssp_rate = usb_get_maximum_ssp_rate(dev);
+ dwc->dr_mode = usb_get_dr_mode(dev);
+@@ -1678,6 +1702,9 @@ static void dwc3_get_properties(struct d
+ dwc->dis_split_quirk = device_property_read_bool(dev,
+ "snps,dis-split-quirk");
+
++ device_property_read_u8(dev, "snps,axi-pipe-limit",
++ &axi_pipe_limit);
++
+ dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+ dwc->tx_de_emphasis = tx_de_emphasis;
+
+@@ -1695,6 +1722,8 @@ static void dwc3_get_properties(struct d
+ dwc->tx_thr_num_pkt_prd = tx_thr_num_pkt_prd;
+ dwc->tx_max_burst_prd = tx_max_burst_prd;
+
++ dwc->axi_pipe_limit = axi_pipe_limit;
++
+ dwc->imod_interval = 0;
+
+ dwc->tx_fifo_resize_max_num = tx_fifo_resize_max_num;
+@@ -1903,6 +1932,12 @@ static int dwc3_probe(struct platform_de
+
+ dwc3_get_properties(dwc);
+
++ if (!dwc->sysdev_is_parent) {
++ ret = dma_set_mask_and_coherent(dwc->sysdev, DMA_BIT_MASK(64));
++ if (ret)
++ return ret;
++ }
++
+ dwc->reset = devm_reset_control_array_get_optional_shared(dev);
+ if (IS_ERR(dwc->reset)) {
+ ret = PTR_ERR(dwc->reset);
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -183,6 +183,9 @@
+ #define DWC3_GSBUSCFG0_INCRBRSTENA (1 << 0) /* undefined length enable */
+ #define DWC3_GSBUSCFG0_INCRBRST_MASK 0xff
+
++/* Global SoC Bus Configuration Register 1 */
++#define DWC3_GSBUSCFG1_PIPETRANSLIMIT(n) (((n) & 0xf) << 8)
++
+ /* Global Debug LSP MUX Select */
+ #define DWC3_GDBGLSPMUX_ENDBC BIT(15) /* Host only */
+ #define DWC3_GDBGLSPMUX_HOSTSELECT(n) ((n) & 0x3fff)
+@@ -1056,6 +1059,7 @@ struct dwc3_scratchpad_array {
+ * @tx_max_burst_prd: max periodic ESS transmit burst size
+ * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
+ * @clear_stall_protocol: endpoint number that requires a delayed status phase
++ * @axi_max_pipe: set to override the maximum number of pipelined AXI transfers
+ * @hsphy_interface: "utmi" or "ulpi"
+ * @connected: true when we're connected to a host, false otherwise
+ * @softconnect: true when gadget connect is called, false when disconnect runs
+@@ -1287,6 +1291,7 @@ struct dwc3 {
+ u8 tx_max_burst_prd;
+ u8 tx_fifo_resize_max_num;
+ u8 clear_stall_protocol;
++ u8 axi_pipe_limit;
+
+ const char *hsphy_interface;
+
+--- a/drivers/usb/dwc3/host.c
++++ b/drivers/usb/dwc3/host.c
+@@ -30,10 +30,10 @@ static void dwc3_host_fill_xhci_irq_res(
+
+ static int dwc3_host_get_irq(struct dwc3 *dwc)
+ {
+- struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
++ struct platform_device *pdev = to_platform_device(dwc->dev);
+ int irq;
+
+- irq = platform_get_irq_byname_optional(dwc3_pdev, "host");
++ irq = platform_get_irq_byname_optional(pdev, "host");
+ if (irq > 0) {
+ dwc3_host_fill_xhci_irq_res(dwc, irq, "host");
+ goto out;
+@@ -42,7 +42,7 @@ static int dwc3_host_get_irq(struct dwc3
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+- irq = platform_get_irq_byname_optional(dwc3_pdev, "dwc_usb3");
++ irq = platform_get_irq_byname_optional(pdev, "dwc_usb3");
+ if (irq > 0) {
+ dwc3_host_fill_xhci_irq_res(dwc, irq, "dwc_usb3");
+ goto out;
+@@ -51,7 +51,7 @@ static int dwc3_host_get_irq(struct dwc3
+ if (irq == -EPROBE_DEFER)
+ goto out;
+
+- irq = platform_get_irq(dwc3_pdev, 0);
++ irq = platform_get_irq(pdev, 0);
+ if (irq > 0) {
+ dwc3_host_fill_xhci_irq_res(dwc, irq, NULL);
+ goto out;
+@@ -66,16 +66,23 @@ out:
+
+ int dwc3_host_init(struct dwc3 *dwc)
+ {
++ struct platform_device *pdev = to_platform_device(dwc->dev);
+ struct property_entry props[4];
+ struct platform_device *xhci;
+ int ret, irq;
+ int prop_idx = 0;
++ int id;
+
+ irq = dwc3_host_get_irq(dwc);
+ if (irq < 0)
+ return irq;
+
+- xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
++ id = of_alias_get_id(pdev->dev.of_node, "usb");
++ if (id >= 0)
++ xhci = platform_device_alloc("xhci-hcd", id);
++ else
++ xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
++
+ if (!xhci) {
+ dev_err(dwc->dev, "couldn't allocate xHCI device\n");
+ return -ENOMEM;
--- /dev/null
+From 480c8e9f48f8a96c457eb3dc0079a73598fb7477 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.org>
+Date: Wed, 1 Dec 2021 19:43:08 +0000
+Subject: [PATCH] drm/panel/raspberrypi-touchscreen: Insert more delays.
+
+This avoids failures in cases where the panel is enabled
+or re-probed very soon after being disabled or probed.
+These can occur because the Atmel device can mis-behave
+over I2C for a few ms after any write to the POWERON register.
+---
+ drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
++++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+@@ -299,6 +299,13 @@ static int rpi_touchscreen_prepare(struc
+ struct rpi_touchscreen *ts = panel_to_ts(panel);
+ int i, data;
+
++ /*
++ * Power up the Toshiba bridge. The Atmel device can misbehave
++ * over I2C for a few ms after writes to REG_POWERON (including the
++ * write in rpi_touchscreen_disable()), so sleep before and after.
++ * Also to ensure that the bridge has been off for at least 100ms.
++ */
++ msleep(100);
+ rpi_touchscreen_i2c_write(ts, REG_POWERON, 1);
+ usleep_range(20000, 25000);
+ /* Wait for nPWRDWN to go low to indicate poweron is done. */
+@@ -431,6 +438,7 @@ static int rpi_touchscreen_probe(struct
+
+ /* Turn off at boot, so we can cleanly sequence powering on. */
+ rpi_touchscreen_i2c_write(ts, REG_POWERON, 0);
++ usleep_range(20000, 25000);
+
+ /* Look up the DSI host. It needs to probe before we do. */
+ endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
--- /dev/null
+From 29702857d1ab71243ea6c247dfe9b5bc43dd422f Mon Sep 17 00:00:00 2001
+From: Jim Quinlan <james.quinlan@broadcom.com>
+Date: Fri, 23 Jun 2023 10:40:57 -0400
+Subject: [PATCH] PCI: brcmstb: Add BCM2712 support
+
+PCI: brcmstb: differing register offsets on 2712
+
+pcie-brcmstb: Add 2712 bridge reset support
+
+pcie: 2712 PORT_MASK and rescal support
+
+pcie-brcmstb: don't alter the L1SS debug register
+
+For reasons unknown, this disables the reference clock
+
+pcie-brcmstb: fix BAR2 enable and window decode
+
+Set UBUS ACCESS_EN to let inbound DMA work. Also BCM2712 has grown
+an index in the inbound window size decode register.
+
+PCIe: brcmstb: Enable support for 64 MSI-Xs
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pcie-brcmstb: Suppress read error responses
+
+If the link is down or the EP fails to return a read completion, the
+RC's default behaviour is to return an AXI error. This causes fatal
+exceptions on A76, so it's better to respond with all 1s instead.
+
+pcie-brcmstb: increase UBUS timeout to cater for link retrain events
+
+pcie-brcmstb: Handle additional inbound regions
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+pcie-brcmstb: Add support for external MSI controller
+
+pcie-brcmstb: add a reasonable default traffic class to priority map
+
+BCM2712 supports multiple traffic classes (TCs) with independent
+maximally sized transfer queues for each TC. Traffic classes have no
+transaction ordering requirements between them, which facilitates
+out-of-order completions and arbitration between posted writes for
+data streams that have no dependence on each other.
+
+In addition to the above benefits of splitting endpoint traffic into
+individual queues, priorities can be assigned to traffic classes by
+a heuristic or deterministic mechanism. The heuristic elevates AXI
+QOS priority in accordance with the number of pending transfers in
+each TC's queue, but for true priority signalling a forwarding
+mechanism using vendor-defined messages is implemented.
+
+Receipt of a 3 DWORD VDM assigns a priority tag to a TC on-the-fly,
+and this tag corresponds to a configurable AXI QOS value.
+
+As a simple baseline, assign a linear map of AXI QOS to each tag.
+
+pcie: brcmstb: set up the VDM forwarding interface when setting up QoS
+
+pcie-brcmstb: add DT bindings for MPS-size Read Completion Mode
+
+This controller has an optional feature that allows read completion
+TLPs to be sized up to the Maximum Packet Size of a configured link.
+
+This can exceed the Read Completion Boundary of 128B specified in
+the PCIe specification, but depending on endpoint support may increase
+link read bandwidth significantly.
+
+pcie-brcmstb: clean up debug messages
+
+pcie-brcmstb: fix BCM2712A0 PHY PM errata
+
+The power management clock is 54MHz not 50MHz, so adjust the PM clock period
+to suit. Powering off the PHY PLL in L1.2 is unsafe, so force it on.
+
+pcie-brcmstb: set CLKREQ functionality according to link partner support
+
+The RC supports either L1 with clock PM or L1 sub-state control, not both
+at the same time. Examine the link partner's capabilities to determine
+which is the most suitable scheme to use.
+
+pcie: brcmstb: don't reset block bridges in suspend or removal cases
+
+BCM2712 has a single rescal block for all three root complexes, and
+holding PCIE1's bridge in reset will hang the chip if a different
+RC wants to access any of the rescal registers.
+
+pcie: brcmstb: guard 2712-specific setup with a RC type check
+
+BCM2711 doesn't implement the UBUS control registers.
+
+pcie: brcmstb: On 2712 keeping the PLL powered in L1.x is not required
+
+A separate misconfiguration when enabling SSC (the MDIO registers no
+longer do the same thing on BCM2712) had the side-effect of breaking
+PLL powerdown and resume sequencing.
+
+Allow entry into a true L1.2 state where analogue is depowered.
+
+pcie: brcmstb: Fix reset warning on probe failure
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+bcm2712: pcie: adjust PHY PLL setup to use a 54MHz input refclk
+
+Use canned MDIO writes from Broadcom that switch the ref_clk output
+pair to run from the internal fractional PLL, and set the internal PLL
+to expect a 54MHz input reference clock.
+
+Gen3 operation is not guaranteed to be stable in this setup, so default
+to gen2.
+
+This only works if the LCPLL is bypassed (requires latest bootloader).
+
+pcie: brcmstb: add missing register writes
+
+drivers: pcie: brcmstb: cater for BCM2712C0 bug dropping QoS on the floor
+
+The AXI QoS value extracted from the request fifo ends up as zero forever.
+Disabling this means that "panic" signalling doesn't do anything useful,
+but static priorites do work.
+
+Also align the selected TC:QoS map with RP1's expectations of service.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+drivers: pcie: brcmstb: shuffle TC priorities up to 8
+
+Use the range 8-11 which puts the highest below HVS but leaves space
+below for other 2712 masters.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+drivers: pcie: brcmstb: optionally enable QoS features by DT for BCM2712
+
+It's a bad idea to universally enable "realtime" priorities for TCs
+across all the RC instances on the chip. Endpoints other than RP1 may
+make use of these, so you don't want e.g. NVMe descriptor fetches getting
+higher priority than your remote display.
+
+Add two optional DT properties controlling the behaviour - FIFO-based
+backpressure QoS or "message-based". Message-based signalling is
+fundamentally broken due to a chip bug, so it collapses into a set of
+static assignments that RP1 needs.
+
+The default if neither property is specified is to assign everything a
+QoS of 0.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+
+drivers: pcie: brcmstb: adjust completion timeouts for bcm2712
+
+Setting the RC config retry timeout makes CRS auto-polling work, but
+the UBUS timeout will override the config retry. Both need to be large.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 507 +++++++++++++++++++++++---
+ 1 file changed, 458 insertions(+), 49 deletions(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -13,6 +13,7 @@
+ #include <linux/irqchip/chained_irq.h>
+ #include <linux/irqdomain.h>
+ #include <linux/kernel.h>
++#include <linux/kthread.h>
+ #include <linux/list.h>
+ #include <linux/log2.h>
+ #include <linux/module.h>
+@@ -47,11 +48,25 @@
+ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY 0x04dc
+ #define PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK 0xc00
+
++#define PCIE_RC_TL_VDM_CTL0 0x0a20
++#define PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK 0x10000
++#define PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK 0x20000
++#define PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK 0x40000
++
++#define PCIE_RC_TL_VDM_CTL1 0x0a0c
++#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID0_MASK 0x0000ffff
++#define PCIE_RC_TL_VDM_CTL1_VDM_VNDRID1_MASK 0xffff0000
++
+ #define PCIE_RC_DL_MDIO_ADDR 0x1100
+ #define PCIE_RC_DL_MDIO_WR_DATA 0x1104
+ #define PCIE_RC_DL_MDIO_RD_DATA 0x1108
+
++#define PCIE_RC_PL_PHY_CTL_15 0x184c
++#define PCIE_RC_PL_PHY_CTL_15_DIS_PLL_PD_MASK 0x400000
++#define PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK 0xff
++
+ #define PCIE_MISC_MISC_CTRL 0x4008
++#define PCIE_MISC_MISC_CTRL_RCB_MPS_MODE_MASK 0x400
+ #define PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK 0x1000
+ #define PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK 0x2000
+ #define PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK 0x300000
+@@ -71,6 +86,7 @@
+
+ #define PCIE_MISC_RC_BAR1_CONFIG_LO 0x402c
+ #define PCIE_MISC_RC_BAR1_CONFIG_LO_SIZE_MASK 0x1f
++#define PCIE_MISC_RC_BAR1_CONFIG_HI 0x4030
+
+ #define PCIE_MISC_RC_BAR2_CONFIG_LO 0x4034
+ #define PCIE_MISC_RC_BAR2_CONFIG_LO_SIZE_MASK 0x1f
+@@ -78,6 +94,7 @@
+
+ #define PCIE_MISC_RC_BAR3_CONFIG_LO 0x403c
+ #define PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK 0x1f
++#define PCIE_MISC_RC_BAR3_CONFIG_HI 0x4040
+
+ #define PCIE_MISC_MSI_BAR_CONFIG_LO 0x4044
+ #define PCIE_MISC_MSI_BAR_CONFIG_HI 0x4048
+@@ -86,12 +103,15 @@
+ #define PCIE_MISC_MSI_DATA_CONFIG_VAL_32 0xffe06540
+ #define PCIE_MISC_MSI_DATA_CONFIG_VAL_8 0xfff86540
+
++#define PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT 0x405c
++
+ #define PCIE_MISC_PCIE_CTRL 0x4064
+ #define PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK 0x1
+ #define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK 0x4
+
+ #define PCIE_MISC_PCIE_STATUS 0x4068
+ #define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK 0x80
++#define PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712 0x40
+ #define PCIE_MISC_PCIE_STATUS_PCIE_DL_ACTIVE_MASK 0x20
+ #define PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK 0x10
+ #define PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK 0x40
+@@ -116,14 +136,73 @@
+ #define PCIE_MEM_WIN0_LIMIT_HI(win) \
+ PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LIMIT_HI + ((win) * 8)
+
+-#define PCIE_MISC_HARD_PCIE_HARD_DEBUG 0x4204
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG pcie->reg_offsets[PCIE_HARD_DEBUG]
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
+ #define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK 0x00200000
+
++#define PCIE_MISC_CTRL_1 0x40A0
++#define PCIE_MISC_CTRL_1_OUTBOUND_TC_MASK 0xf
++#define PCIE_MISC_CTRL_1_OUTBOUND_NO_SNOOP_MASK BIT(3)
++#define PCIE_MISC_CTRL_1_OUTBOUND_RO_MASK BIT(4)
++#define PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK BIT(5)
++
++#define PCIE_MISC_UBUS_CTRL 0x40a4
++#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK BIT(13)
++#define PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK BIT(19)
++
++#define PCIE_MISC_UBUS_TIMEOUT 0x40A8
++
++#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP 0x40ac
++#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0)
++#define PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI 0x40b0
++
++#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP 0x40b4
++#define PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK BIT(0)
++
++/* Additional RC BARs */
++#define PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK 0x1f
++#define PCIE_MISC_RC_BAR4_CONFIG_LO 0x40d4
++#define PCIE_MISC_RC_BAR4_CONFIG_HI 0x40d8
++/* ... */
++#define PCIE_MISC_RC_BAR10_CONFIG_LO 0x4104
++#define PCIE_MISC_RC_BAR10_CONFIG_HI 0x4108
++
++#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE 0x1
++#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK 0xfffff000
++#define PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK 0xff
++#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO 0x410c
++#define PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI 0x4110
++/* ... */
++#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_LO 0x413c
++#define PCIE_MISC_UBUS_BAR10_CONFIG_REMAP_HI 0x4140
++
++/* AXI priority forwarding - automatic level-based */
++#define PCIE_MISC_TC_QUEUE_TO_QOS_MAP(x) (0x4160 - (x) * 4)
++/* Defined in quarter-fullness */
++#define QUEUE_THRESHOLD_34_TO_QOS_MAP_SHIFT 12
++#define QUEUE_THRESHOLD_23_TO_QOS_MAP_SHIFT 8
++#define QUEUE_THRESHOLD_12_TO_QOS_MAP_SHIFT 4
++#define QUEUE_THRESHOLD_01_TO_QOS_MAP_SHIFT 0
++#define QUEUE_THRESHOLD_MASK 0xf
++
++/* VDM messages indexing TCs to AXI priorities */
++/* Indexes 8-15 */
++#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI 0x4164
++/* Indexes 0-7 */
++#define PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO 0x4168
++#define VDM_PRIORITY_TO_QOS_MAP_SHIFT(x) (4 * (x))
++#define VDM_PRIORITY_TO_QOS_MAP_MASK 0xf
++
++#define PCIE_MISC_AXI_INTF_CTRL 0x416C
++#define AXI_REQFIFO_EN_QOS_PROPAGATION BIT(7)
++#define AXI_BRIDGE_LOW_LATENCY_MODE BIT(6)
++#define AXI_MASTER_MAX_OUTSTANDING_REQUESTS_MASK 0x3f
+
+-#define PCIE_INTR2_CPU_BASE 0x4300
++#define PCIE_MISC_AXI_READ_ERROR_DATA 0x4170
++
++#define PCIE_INTR2_CPU_BASE (pcie->reg_offsets[INTR2_CPU])
+ #define PCIE_MSI_INTR2_BASE 0x4500
+ /* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */
+ #define MSI_INT_STATUS 0x0
+@@ -197,6 +276,8 @@ enum {
+ RGR1_SW_INIT_1,
+ EXT_CFG_INDEX,
+ EXT_CFG_DATA,
++ PCIE_HARD_DEBUG,
++ INTR2_CPU,
+ };
+
+ enum {
+@@ -211,6 +292,7 @@ enum pcie_type {
+ BCM4908,
+ BCM7278,
+ BCM2711,
++ BCM2712,
+ };
+
+ struct pcie_cfg_data {
+@@ -218,6 +300,7 @@ struct pcie_cfg_data {
+ const enum pcie_type type;
+ void (*perst_set)(struct brcm_pcie *pcie, u32 val);
+ void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
++ bool (*rc_mode)(struct brcm_pcie *pcie);
+ };
+
+ struct subdev_regulators {
+@@ -234,7 +317,7 @@ struct brcm_msi {
+ struct mutex lock; /* guards the alloc/free operations */
+ u64 target_addr;
+ int irq;
+- DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR);
++ DECLARE_BITMAP(used, 64);
+ bool legacy;
+ /* Some chips have MSIs in bits [31..24] of a shared register. */
+ int legacy_shift;
+@@ -251,6 +334,7 @@ struct brcm_pcie {
+ struct device_node *np;
+ bool ssc;
+ bool l1ss;
++ bool rcb_mps_mode;
+ int gen;
+ u64 msi_target_addr;
+ struct brcm_msi *msi;
+@@ -258,11 +342,14 @@ struct brcm_pcie {
+ enum pcie_type type;
+ struct reset_control *rescal;
+ struct reset_control *perst_reset;
++ struct reset_control *bridge_reset;
+ int num_memc;
+ u64 memc_size[PCIE_BRCM_MAX_MEMC];
+ u32 hw_rev;
++ u32 qos_map;
+ void (*perst_set)(struct brcm_pcie *pcie, u32 val);
+ void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
++ bool (*rc_mode)(struct brcm_pcie *pcie);
+ struct subdev_regulators *sr;
+ bool ep_wakeup_capable;
+ };
+@@ -283,8 +370,8 @@ static int brcm_pcie_encode_ibar_size(u6
+ if (log2_in >= 12 && log2_in <= 15)
+ /* Covers 4KB to 32KB (inclusive) */
+ return (log2_in - 12) + 0x1c;
+- else if (log2_in >= 16 && log2_in <= 35)
+- /* Covers 64KB to 32GB, (inclusive) */
++ else if (log2_in >= 16 && log2_in <= 36)
++ /* Covers 64KB to 64GB, (inclusive) */
+ return log2_in - 15;
+ /* Something is awry so disable */
+ return 0;
+@@ -381,6 +468,35 @@ static int brcm_pcie_set_ssc(struct brcm
+ return ssc && pll ? 0 : -EIO;
+ }
+
++static void brcm_pcie_munge_pll(struct brcm_pcie *pcie)
++{
++ //print "MDIO block 0x1600 written per Dannys instruction"
++ //tmp = pcie_mdio_write(phyad, &h16&, &h50b9&)
++ //tmp = pcie_mdio_write(phyad, &h17&, &hbd1a&)
++ //tmp = pcie_mdio_write(phyad, &h1b&, &h5030&)
++ //tmp = pcie_mdio_write(phyad, &h1e&, &h0007&)
++
++ u32 tmp;
++ int ret, i;
++ u8 regs[] = { 0x16, 0x17, 0x18, 0x19, 0x1b, 0x1c, 0x1e };
++ u16 data[] = { 0x50b9, 0xbda1, 0x0094, 0x97b4, 0x5030, 0x5030, 0x0007 };
++
++ ret = brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, SET_ADDR_OFFSET,
++ 0x1600);
++ for (i = 0; i < ARRAY_SIZE(regs); i++) {
++ brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
++ dev_dbg(pcie->dev, "PCIE MDIO pre_refclk 0x%02x = 0x%04x\n",
++ regs[i], tmp);
++ }
++ for (i = 0; i < ARRAY_SIZE(regs); i++) {
++ brcm_pcie_mdio_write(pcie->base, MDIO_PORT0, regs[i], data[i]);
++ brcm_pcie_mdio_read(pcie->base, MDIO_PORT0, regs[i], &tmp);
++ dev_dbg(pcie->dev, "PCIE MDIO post_refclk 0x%02x = 0x%04x\n",
++ regs[i], tmp);
++ }
++ usleep_range(100, 200);
++}
++
+ /* Limits operation to a specific generation (1, 2, or 3) */
+ static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)
+ {
+@@ -438,6 +554,97 @@ static void brcm_pcie_set_outbound_win(s
+ writel(tmp, pcie->base + PCIE_MEM_WIN0_LIMIT_HI(win));
+ }
+
++static void brcm_pcie_set_tc_qos(struct brcm_pcie *pcie)
++{
++ int i;
++ u32 reg;
++
++ if (pcie->type != BCM2712)
++ return;
++
++ /* XXX: BCM2712C0 is broken, disable the forwarding search */
++ reg = readl(pcie->base + PCIE_MISC_AXI_INTF_CTRL);
++ reg &= ~AXI_REQFIFO_EN_QOS_PROPAGATION;
++ writel(reg, pcie->base + PCIE_MISC_AXI_INTF_CTRL);
++
++ /* Disable VDM reception by default - QoS map defaults to 0 */
++ reg = readl(pcie->base + PCIE_MISC_CTRL_1);
++ reg &= ~PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
++ writel(reg, pcie->base + PCIE_MISC_CTRL_1);
++
++ if (!of_property_read_u32(pcie->np, "brcm,fifo-qos-map", &pcie->qos_map)) {
++ /*
++ * Backpressure mode - bottom 4 nibbles are QoS for each
++ * quartile of FIFO level. Each TC gets the same map, because
++ * this mode is intended for nonrealtime EPs.
++ */
++
++ pcie->qos_map &= 0x0000ffff;
++ for (i = 0; i < 8; i++)
++ writel(pcie->qos_map, pcie->base + PCIE_MISC_TC_QUEUE_TO_QOS_MAP(i));
++
++ return;
++ }
++
++ if (!of_property_read_u32(pcie->np, "brcm,vdm-qos-map", &pcie->qos_map)) {
++
++ reg = readl(pcie->base + PCIE_MISC_CTRL_1);
++ reg |= PCIE_MISC_CTRL_1_EN_VDM_QOS_CONTROL_MASK;
++ writel(reg, pcie->base + PCIE_MISC_CTRL_1);
++
++ /* No forwarding means no point separating panic priorities from normal */
++ writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_LO);
++ writel(pcie->qos_map, pcie->base + PCIE_MISC_VDM_PRIORITY_TO_QOS_MAP_HI);
++
++ /* Match Vendor ID of 0 */
++ writel(0, pcie->base + PCIE_RC_TL_VDM_CTL1);
++ /* Forward VDMs to priority interface - at least the rx counters work */
++ reg = readl(pcie->base + PCIE_RC_TL_VDM_CTL0);
++ reg |= PCIE_RC_TL_VDM_CTL0_VDM_ENABLED_MASK |
++ PCIE_RC_TL_VDM_CTL0_VDM_IGNORETAG_MASK |
++ PCIE_RC_TL_VDM_CTL0_VDM_IGNOREVNDRID_MASK;
++ writel(reg, pcie->base + PCIE_RC_TL_VDM_CTL0);
++ }
++}
++
++static void brcm_pcie_config_clkreq(struct brcm_pcie *pcie)
++{
++ void __iomem *base = pcie->base;
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++ int domain = pci_domain_nr(bridge->bus);
++ const struct pci_bus *bus = pci_find_bus(domain, 1);
++ struct pci_dev *pdev = (struct pci_dev *)bus->devices.next;
++ u32 tmp, link_cap = 0;
++ u16 link_ctl = 0;
++ int clkpm = 0;
++ int substates = 0;
++
++ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
++ if ((link_cap & PCI_EXP_LNKCAP_CLKPM))
++ clkpm = 1;
++
++ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &link_ctl);
++ if (!(link_ctl & PCI_EXP_LNKCTL_CLKREQ_EN))
++ clkpm = 0;
++
++ if (pcie->l1ss && pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS))
++ substates = 1;
++
++ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
++ tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
++
++ if (substates)
++ tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
++ else if (clkpm)
++ tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
++
++ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++ if (substates || clkpm)
++ dev_info(pcie->dev, "clkreq control enabled\n");
++}
++
+ static struct irq_chip brcm_msi_irq_chip = {
+ .name = "BRCM STB PCIe MSI",
+ .irq_ack = irq_chip_ack_parent,
+@@ -455,7 +662,7 @@ static struct msi_domain_info brcm_msi_d
+ static void brcm_pcie_msi_isr(struct irq_desc *desc)
+ {
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+- unsigned long status;
++ unsigned long status, virq;
+ struct brcm_msi *msi;
+ struct device *dev;
+ u32 bit;
+@@ -467,10 +674,22 @@ static void brcm_pcie_msi_isr(struct irq
+ status = readl(msi->intr_base + MSI_INT_STATUS);
+ status >>= msi->legacy_shift;
+
+- for_each_set_bit(bit, &status, msi->nr) {
+- int ret;
+- ret = generic_handle_domain_irq(msi->inner_domain, bit);
+- if (ret)
++ for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR/*msi->nr*/) {
++ bool found = false;
++
++ virq = irq_find_mapping(msi->inner_domain, bit);
++ if (virq) {
++ found = true;
++ dev_dbg(dev, "MSI -> %ld\n", virq);
++ generic_handle_irq(virq);
++ }
++ virq = irq_find_mapping(msi->inner_domain, bit + 32);
++ if (virq) {
++ found = true;
++ dev_dbg(dev, "MSI -> %ld\n", virq);
++ generic_handle_irq(virq);
++ }
++ if (!found)
+ dev_dbg(dev, "unexpected MSI\n");
+ }
+
+@@ -483,7 +702,7 @@ static void brcm_msi_compose_msi_msg(str
+
+ msg->address_lo = lower_32_bits(msi->target_addr);
+ msg->address_hi = upper_32_bits(msi->target_addr);
+- msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq;
++ msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | (data->hwirq & 0x1f);
+ }
+
+ static int brcm_msi_set_affinity(struct irq_data *irq_data,
+@@ -495,7 +714,7 @@ static int brcm_msi_set_affinity(struct
+ static void brcm_msi_ack_irq(struct irq_data *data)
+ {
+ struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
+- const int shift_amt = data->hwirq + msi->legacy_shift;
++ const int shift_amt = (data->hwirq & 0x1f) + msi->legacy_shift;
+
+ writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR);
+ }
+@@ -653,7 +872,7 @@ static int brcm_pcie_enable_msi(struct b
+ msi->legacy_shift = 24;
+ } else {
+ msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE;
+- msi->nr = BRCM_INT_PCI_MSI_NR;
++ msi->nr = 64; //BRCM_INT_PCI_MSI_NR;
+ msi->legacy_shift = 0;
+ }
+
+@@ -670,7 +889,7 @@ static int brcm_pcie_enable_msi(struct b
+ }
+
+ /* The controller is capable of serving in both RC and EP roles */
+-static bool brcm_pcie_rc_mode(struct brcm_pcie *pcie)
++static bool brcm_pcie_rc_mode_generic(struct brcm_pcie *pcie)
+ {
+ void __iomem *base = pcie->base;
+ u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
+@@ -678,6 +897,14 @@ static bool brcm_pcie_rc_mode(struct brc
+ return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK, val);
+ }
+
++static bool brcm_pcie_rc_mode_2712(struct brcm_pcie *pcie)
++{
++ void __iomem *base = pcie->base;
++ u32 val = readl(base + PCIE_MISC_PCIE_STATUS);
++
++ return !!FIELD_GET(PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK_2712, val) | 1; //XXX
++}
++
+ static bool brcm_pcie_link_up(struct brcm_pcie *pcie)
+ {
+ u32 val = readl(pcie->base + PCIE_MISC_PCIE_STATUS);
+@@ -749,6 +976,18 @@ static inline void brcm_pcie_bridge_sw_i
+ writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
+ }
+
++static inline void brcm_pcie_bridge_sw_init_set_2712(struct brcm_pcie *pcie, u32 val)
++{
++ if (WARN_ONCE(!pcie->bridge_reset,
++ "missing bridge reset controller\n"))
++ return;
++
++ if (val)
++ reset_control_assert(pcie->bridge_reset);
++ else
++ reset_control_deassert(pcie->bridge_reset);
++}
++
+ static inline void brcm_pcie_perst_set_4908(struct brcm_pcie *pcie, u32 val)
+ {
+ if (WARN_ONCE(!pcie->perst_reset, "missing PERST# reset controller\n"))
+@@ -770,6 +1009,16 @@ static inline void brcm_pcie_perst_set_7
+ writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL);
+ }
+
++static inline void brcm_pcie_perst_set_2712(struct brcm_pcie *pcie, u32 val)
++{
++ u32 tmp;
++
++ /* Perst bit has moved and assert value is 0 */
++ tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
++ u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK);
++ writel(tmp, pcie->base + PCIE_MISC_PCIE_CTRL);
++}
++
+ static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
+ {
+ u32 tmp;
+@@ -796,6 +1045,8 @@ static inline int brcm_pcie_get_rc_bar2_
+ size += entry->res->end - entry->res->start + 1;
+ if (pcie_beg < lowest_pcie_addr)
+ lowest_pcie_addr = pcie_beg;
++ if (pcie->type == BCM2711 || pcie->type == BCM2712)
++ break; // Only consider the first entry
+ }
+
+ if (lowest_pcie_addr == ~(u64)0) {
+@@ -866,6 +1117,30 @@ static inline int brcm_pcie_get_rc_bar2_
+ return 0;
+ }
+
++static int brcm_pcie_get_rc_bar_n(struct brcm_pcie *pcie,
++ int idx,
++ u64 *rc_bar_cpu,
++ u64 *rc_bar_size,
++ u64 *rc_bar_pci)
++{
++ struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
++ struct resource_entry *entry;
++ int i = 0;
++
++ resource_list_for_each_entry(entry, &bridge->dma_ranges) {
++ if (i == idx) {
++ *rc_bar_cpu = entry->res->start;
++ *rc_bar_size = entry->res->end - entry->res->start + 1;
++ *rc_bar_pci = entry->res->start - entry->offset;
++ return 0;
++ }
++
++ i++;
++ }
++
++ return -EINVAL;
++}
++
+ static int brcm_pcie_setup(struct brcm_pcie *pcie)
+ {
+ u64 rc_bar2_offset, rc_bar2_size;
+@@ -874,11 +1149,14 @@ static int brcm_pcie_setup(struct brcm_p
+ struct resource_entry *entry;
+ u32 tmp, burst, aspm_support;
+ int num_out_wins = 0;
+- int ret, memc;
++ int ret, memc, count, i;
+
+ /* Reset the bridge */
+ pcie->bridge_sw_init_set(pcie, 1);
+- pcie->perst_set(pcie, 1);
++
++ /* Ensure that PERST# is asserted; some bootloaders may deassert it. */
++ if (pcie->type == BCM2711)
++ pcie->perst_set(pcie, 1);
+
+ usleep_range(100, 200);
+
+@@ -894,6 +1172,17 @@ static int brcm_pcie_setup(struct brcm_p
+ /* Wait for SerDes to be stable */
+ usleep_range(100, 200);
+
++ if (pcie->type == BCM2712) {
++ /* Allow a 54MHz (xosc) refclk source */
++ brcm_pcie_munge_pll(pcie);
++ /* Fix for L1SS errata */
++ tmp = readl(base + PCIE_RC_PL_PHY_CTL_15);
++ tmp &= ~PCIE_RC_PL_PHY_CTL_15_PM_CLK_PERIOD_MASK;
++ /* PM clock period is 18.52ns (round down) */
++ tmp |= 0x12;
++ writel(tmp, base + PCIE_RC_PL_PHY_CTL_15);
++ }
++
+ /*
+ * SCB_MAX_BURST_SIZE is a two bit field. For GENERIC chips it
+ * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
+@@ -903,18 +1192,25 @@ static int brcm_pcie_setup(struct brcm_p
+ burst = 0x1; /* 256 bytes */
+ else if (pcie->type == BCM2711)
+ burst = 0x0; /* 128 bytes */
++ else if (pcie->type == BCM2712)
++ burst = 0x1; /* 128 bytes */
+ else if (pcie->type == BCM7278)
+ burst = 0x3; /* 512 bytes */
+ else
+ burst = 0x2; /* 512 bytes */
+
+- /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
++ /* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN, RCB_MPS_MODE */
+ tmp = readl(base + PCIE_MISC_MISC_CTRL);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
+ u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
++ if (pcie->rcb_mps_mode)
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_RCB_MPS_MODE_MASK);
++ dev_info(pcie->dev, "setting SCB_ACCESS_EN, READ_UR_MODE, MAX_BURST_SIZE\n");
+ writel(tmp, base + PCIE_MISC_MISC_CTRL);
+
++ brcm_pcie_set_tc_qos(pcie);
++
+ ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
+ &rc_bar2_offset);
+ if (ret)
+@@ -927,7 +1223,11 @@ static int brcm_pcie_setup(struct brcm_p
+ writel(upper_32_bits(rc_bar2_offset),
+ base + PCIE_MISC_RC_BAR2_CONFIG_HI);
+
++ tmp = readl(base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_BAR2_CONFIG_REMAP_ACCESS_ENABLE_MASK);
++ writel(tmp, base + PCIE_MISC_UBUS_BAR2_CONFIG_REMAP);
+ tmp = readl(base + PCIE_MISC_MISC_CTRL);
++
+ for (memc = 0; memc < pcie->num_memc; memc++) {
+ u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15;
+
+@@ -938,8 +1238,32 @@ static int brcm_pcie_setup(struct brcm_p
+ else if (memc == 2)
+ u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2));
+ }
++
+ writel(tmp, base + PCIE_MISC_MISC_CTRL);
+
++ if (pcie->type == BCM2712) {
++ /* Suppress AXI error responses and return 1s for read failures */
++ tmp = readl(base + PCIE_MISC_UBUS_CTRL);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_ERR_DIS_MASK);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_UBUS_CTRL_UBUS_PCIE_REPLY_DECERR_DIS_MASK);
++ writel(tmp, base + PCIE_MISC_UBUS_CTRL);
++ writel(0xffffffff, base + PCIE_MISC_AXI_READ_ERROR_DATA);
++
++ /*
++ * Adjust timeouts. The UBUS timeout also affects CRS
++ * completion retries, as the request will get terminated if
++ * either timeout expires, so both have to be a large value
++ * (in clocks of 750MHz).
++ * Set UBUS timeout to 250ms, then set RC config retry timeout
++ * to be ~240ms.
++ *
++ * Setting CRSVis=1 will stop the core from blocking on a CRS
++ * response, but does require the device to be well-behaved...
++ */
++ writel(0xB2D0000, base + PCIE_MISC_UBUS_TIMEOUT);
++ writel(0xABA0000, base + PCIE_MISC_RC_CONFIG_RETRY_TIMEOUT);
++ }
++
+ /*
+ * We ideally want the MSI target address to be located in the 32bit
+ * addressable memory area. Some devices might depend on it. This is
+@@ -952,7 +1276,7 @@ static int brcm_pcie_setup(struct brcm_p
+ else
+ pcie->msi_target_addr = BRCM_MSI_TARGET_ADDR_GT_4GB;
+
+- if (!brcm_pcie_rc_mode(pcie)) {
++ if (!pcie->rc_mode(pcie)) {
+ dev_err(pcie->dev, "PCIe RC controller misconfigured as Endpoint\n");
+ return -EINVAL;
+ }
+@@ -976,6 +1300,38 @@ static int brcm_pcie_setup(struct brcm_p
+ PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK);
+ writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY);
+
++ /* program additional inbound windows (RC_BAR4..RC_BAR10) */
++ count = (pcie->type == BCM2712) ? 7 : 0;
++ for (i = 0; i < count; i++) {
++ u64 bar_cpu, bar_size, bar_pci;
++
++ ret = brcm_pcie_get_rc_bar_n(pcie, 1 + i, &bar_cpu, &bar_size,
++ &bar_pci);
++ if (ret)
++ break;
++
++ tmp = lower_32_bits(bar_pci);
++ u32p_replace_bits(&tmp, brcm_pcie_encode_ibar_size(bar_size),
++ PCIE_MISC_RC_BAR_CONFIG_LO_SIZE_MASK);
++ writel(tmp, base + PCIE_MISC_RC_BAR4_CONFIG_LO + i * 8);
++ writel(upper_32_bits(bar_pci),
++ base + PCIE_MISC_RC_BAR4_CONFIG_HI + i * 8);
++
++ tmp = upper_32_bits(bar_cpu) &
++ PCIE_MISC_UBUS_BAR_CONFIG_REMAP_HI_MASK;
++ writel(tmp,
++ base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_HI + i * 8);
++ tmp = lower_32_bits(bar_cpu) &
++ PCIE_MISC_UBUS_BAR_CONFIG_REMAP_LO_MASK;
++ writel(tmp | PCIE_MISC_UBUS_BAR_CONFIG_REMAP_ENABLE,
++ base + PCIE_MISC_UBUS_BAR4_CONFIG_REMAP_LO + i * 8);
++ }
++
++ if (pcie->gen) {
++ dev_info(pcie->dev, "Forcing gen %d\n", pcie->gen);
++ brcm_pcie_set_gen(pcie, pcie->gen);
++ }
++
+ /*
+ * For config space accesses on the RC, show the right class for
+ * a PCIe-PCIe bridge (the default setting is to be EP mode).
+@@ -1031,7 +1387,6 @@ static int brcm_pcie_start_link(struct b
+ void __iomem *base = pcie->base;
+ u16 nlw, cls, lnksta;
+ bool ssc_good = false;
+- u32 tmp;
+ int ret, i;
+
+ /* Unassert the fundamental reset */
+@@ -1067,6 +1422,7 @@ static int brcm_pcie_start_link(struct b
+ dev_err(dev, "failed attempt to enter ssc mode\n");
+ }
+
++
+ lnksta = readw(base + BRCM_PCIE_CAP_REGS + PCI_EXP_LNKSTA);
+ cls = FIELD_GET(PCI_EXP_LNKSTA_CLS, lnksta);
+ nlw = FIELD_GET(PCI_EXP_LNKSTA_NLW, lnksta);
+@@ -1074,27 +1430,6 @@ static int brcm_pcie_start_link(struct b
+ pci_speed_string(pcie_link_speed[cls]), nlw,
+ ssc_good ? "(SSC)" : "(!SSC)");
+
+- tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+- if (pcie->l1ss) {
+- /*
+- * Enable CLKREQ# signalling include L1 Substate control of
+- * the CLKREQ# signal and the external reference clock buffer.
+- * meet requirement for Endpoints that require CLKREQ#
+- * assertion to clock active within 400ns.
+- */
+- tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+- tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
+- } else {
+- /*
+- * Refclk from RC should be gated with CLKREQ# input when
+- * ASPM L0s,L1 is enabled => setting the CLKREQ_DEBUG_ENABLE
+- * field to 1.
+- */
+- tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK;
+- tmp |= PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK;
+- }
+- writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+-
+ return 0;
+ }
+
+@@ -1202,6 +1537,7 @@ static void brcm_pcie_enter_l23(struct b
+
+ static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
+ {
++#if 0
+ static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
+ PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT,
+ PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT,
+@@ -1234,6 +1570,9 @@ static int brcm_phy_cntl(struct brcm_pci
+ dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop"));
+
+ return ret;
++#else
++ return 0;
++#endif
+ }
+
+ static inline int brcm_phy_start(struct brcm_pcie *pcie)
+@@ -1266,6 +1605,12 @@ static void brcm_pcie_turn_off(struct br
+ u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
+ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
+
++ /*
++ * Shutting down this bridge on pcie1 means accesses to rescal block
++ * will hang the chip if another RC wants to assert/deassert rescal.
++ */
++ if (pcie->type == BCM2712)
++ return;
+ /* Shutdown PCIe bridge */
+ pcie->bridge_sw_init_set(pcie, 1);
+ }
+@@ -1296,9 +1641,9 @@ static int brcm_pcie_suspend_noirq(struc
+ if (brcm_phy_stop(pcie))
+ dev_err(dev, "Could not stop phy for suspend\n");
+
+- ret = reset_control_rearm(pcie->rescal);
++ ret = reset_control_assert(pcie->rescal);
+ if (ret) {
+- dev_err(dev, "Could not rearm rescal reset\n");
++ dev_err(dev, "Could not assert rescal reset\n");
+ return ret;
+ }
+
+@@ -1393,7 +1738,7 @@ err_regulator:
+ if (pcie->sr)
+ regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);
+ err_reset:
+- reset_control_rearm(pcie->rescal);
++ reset_control_assert(pcie->rescal);
+ err_disable_clk:
+ clk_disable_unprepare(pcie->clk);
+ return ret;
+@@ -1405,8 +1750,8 @@ static void __brcm_pcie_remove(struct br
+ brcm_pcie_turn_off(pcie);
+ if (brcm_phy_stop(pcie))
+ dev_err(pcie->dev, "Could not stop phy\n");
+- if (reset_control_rearm(pcie->rescal))
+- dev_err(pcie->dev, "Could not rearm rescal reset\n");
++ if (reset_control_assert(pcie->rescal))
++ dev_err(pcie->dev, "Could not assert rescal reset\n");
+ clk_disable_unprepare(pcie->clk);
+ }
+
+@@ -1426,12 +1771,16 @@ static const int pcie_offsets[] = {
+ [RGR1_SW_INIT_1] = 0x9210,
+ [EXT_CFG_INDEX] = 0x9000,
+ [EXT_CFG_DATA] = 0x9004,
++ [PCIE_HARD_DEBUG] = 0x4204,
++ [INTR2_CPU] = 0x4300,
+ };
+
+ static const int pcie_offsets_bmips_7425[] = {
+ [RGR1_SW_INIT_1] = 0x8010,
+ [EXT_CFG_INDEX] = 0x8300,
+ [EXT_CFG_DATA] = 0x8304,
++ [PCIE_HARD_DEBUG] = 0x4204,
++ [INTR2_CPU] = 0x4300,
+ };
+
+ static const struct pcie_cfg_data generic_cfg = {
+@@ -1439,6 +1788,7 @@ static const struct pcie_cfg_data generi
+ .type = GENERIC,
+ .perst_set = brcm_pcie_perst_set_generic,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++ .rc_mode = brcm_pcie_rc_mode_generic,
+ };
+
+ static const struct pcie_cfg_data bcm7425_cfg = {
+@@ -1446,6 +1796,7 @@ static const struct pcie_cfg_data bcm742
+ .type = BCM7425,
+ .perst_set = brcm_pcie_perst_set_generic,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++ .rc_mode = brcm_pcie_rc_mode_generic,
+ };
+
+ static const struct pcie_cfg_data bcm7435_cfg = {
+@@ -1460,12 +1811,15 @@ static const struct pcie_cfg_data bcm490
+ .type = BCM4908,
+ .perst_set = brcm_pcie_perst_set_4908,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++ .rc_mode = brcm_pcie_rc_mode_generic,
+ };
+
+ static const int pcie_offset_bcm7278[] = {
+ [RGR1_SW_INIT_1] = 0xc010,
+ [EXT_CFG_INDEX] = 0x9000,
+ [EXT_CFG_DATA] = 0x9004,
++ [PCIE_HARD_DEBUG] = 0x4204,
++ [INTR2_CPU] = 0x4300,
+ };
+
+ static const struct pcie_cfg_data bcm7278_cfg = {
+@@ -1473,6 +1827,7 @@ static const struct pcie_cfg_data bcm727
+ .type = BCM7278,
+ .perst_set = brcm_pcie_perst_set_7278,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
++ .rc_mode = brcm_pcie_rc_mode_generic,
+ };
+
+ static const struct pcie_cfg_data bcm2711_cfg = {
+@@ -1480,10 +1835,27 @@ static const struct pcie_cfg_data bcm271
+ .type = BCM2711,
+ .perst_set = brcm_pcie_perst_set_generic,
+ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
++ .rc_mode = brcm_pcie_rc_mode_generic,
++};
++
++static const int pcie_offsets_bcm2712[] = {
++ [EXT_CFG_INDEX] = 0x9000,
++ [EXT_CFG_DATA] = 0x9004,
++ [PCIE_HARD_DEBUG] = 0x4304,
++ [INTR2_CPU] = 0x4400,
++};
++
++static const struct pcie_cfg_data bcm2712_cfg = {
++ .offsets = pcie_offsets_bcm2712,
++ .type = BCM2712,
++ .perst_set = brcm_pcie_perst_set_2712,
++ .bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_2712,
++ .rc_mode = brcm_pcie_rc_mode_2712,
+ };
+
+ static const struct of_device_id brcm_pcie_match[] = {
+ { .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
++ { .compatible = "brcm,bcm2712-pcie", .data = &bcm2712_cfg },
+ { .compatible = "brcm,bcm4908-pcie", .data = &bcm4908_cfg },
+ { .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
+ { .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },
+@@ -1524,7 +1896,7 @@ static int brcm_pcie_probe(struct platfo
+
+ data = of_device_get_match_data(&pdev->dev);
+ if (!data) {
+- pr_err("failed to look up compatible string\n");
++ dev_err(&pdev->dev, "failed to look up compatible string\n");
+ return -EINVAL;
+ }
+
+@@ -1535,6 +1907,7 @@ static int brcm_pcie_probe(struct platfo
+ pcie->type = data->type;
+ pcie->perst_set = data->perst_set;
+ pcie->bridge_sw_init_set = data->bridge_sw_init_set;
++ pcie->rc_mode = data->rc_mode;
+
+ pcie->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(pcie->base))
+@@ -1549,6 +1922,7 @@ static int brcm_pcie_probe(struct platfo
+
+ pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
+ pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss");
++ pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb");
+
+ ret = clk_prepare_enable(pcie->clk);
+ if (ret) {
+@@ -1565,14 +1939,20 @@ static int brcm_pcie_probe(struct platfo
+ clk_disable_unprepare(pcie->clk);
+ return PTR_ERR(pcie->perst_reset);
+ }
++ pcie->bridge_reset =
++ devm_reset_control_get_optional_exclusive(&pdev->dev, "bridge");
++ if (IS_ERR(pcie->bridge_reset)) {
++ clk_disable_unprepare(pcie->clk);
++ return PTR_ERR(pcie->bridge_reset);
++ }
+
+- ret = reset_control_reset(pcie->rescal);
++ ret = reset_control_deassert(pcie->rescal);
+ if (ret)
+ dev_err(&pdev->dev, "failed to deassert 'rescal'\n");
+
+ ret = brcm_phy_start(pcie);
+ if (ret) {
+- reset_control_rearm(pcie->rescal);
++ reset_control_assert(pcie->rescal);
+ clk_disable_unprepare(pcie->clk);
+ return ret;
+ }
+@@ -1595,6 +1975,33 @@ static int brcm_pcie_probe(struct platfo
+ dev_err(pcie->dev, "probe of internal MSI failed");
+ goto fail;
+ }
++ } else if (pci_msi_enabled() && msi_np != pcie->np) {
++ /* Use RC_BAR1 for MIP access */
++ u64 msi_pci_addr;
++ u64 msi_phys_addr;
++
++ if (of_property_read_u64(msi_np, "brcm,msi-pci-addr", &msi_pci_addr)) {
++ dev_err(pcie->dev, "Unable to find MSI PCI address\n");
++ ret = -EINVAL;
++ goto fail;
++ }
++
++ if (of_property_read_u64(msi_np, "reg", &msi_phys_addr)) {
++ dev_err(pcie->dev, "Unable to find MSI physical address\n");
++ ret = -EINVAL;
++ goto fail;
++ }
++
++ writel(lower_32_bits(msi_pci_addr) | brcm_pcie_encode_ibar_size(0x1000),
++ pcie->base + PCIE_MISC_RC_BAR1_CONFIG_LO);
++ writel(upper_32_bits(msi_pci_addr),
++ pcie->base + PCIE_MISC_RC_BAR1_CONFIG_HI);
++
++ writel(lower_32_bits(msi_phys_addr) |
++ PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_ACCESS_ENABLE_MASK,
++ pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP);
++ writel(upper_32_bits(msi_phys_addr),
++ pcie->base + PCIE_MISC_UBUS_BAR1_CONFIG_REMAP_HI);
+ }
+
+ bridge->ops = pcie->type == BCM7425 ? &brcm7425_pcie_ops : &brcm_pcie_ops;
+@@ -1611,6 +2018,8 @@ static int brcm_pcie_probe(struct platfo
+ return ret;
+ }
+
++ brcm_pcie_config_clkreq(pcie);
++
+ return 0;
+
+ fail:
--- /dev/null
+From 9a11300e46344917226b986a8740e7581d66adf3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 7 Feb 2022 09:20:49 +0000
+Subject: [PATCH] V4L2: Add PiSP opaque formats to V4L2
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 4 +++-
+ include/uapi/linux/videodev2.h | 7 +++++++
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1452,7 +1452,9 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_NV12M_10BE_8L128: descr = "10-bit NV12M (8x128 Linear, BE)"; break;
+ case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break;
+ case V4L2_META_FMT_BCM2835_ISP_STATS: descr = "BCM2835 ISP Image Statistics"; break;
+- case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP Config format"; break;
++ case V4L2_META_FMT_RPI_BE_CFG: descr = "PiSP BE Config format"; break;
++ case V4L2_META_FMT_RPI_FE_CFG: descr = "PiSP FE Config format"; break;
++ case V4L2_META_FMT_RPI_FE_STATS: descr = "PiSP FE Statistics format"; break;
+
+ default:
+ /* Compressed formats */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -826,8 +826,15 @@ struct v4l2_pix_format {
+ #define V4L2_META_FMT_RK_ISP1_STAT_3A v4l2_fourcc('R', 'K', '1', 'S') /* Rockchip ISP1 3A Statistics */
+
+ /* The metadata format identifier for our configuration buffers. */
++/* The metadata format identifier for BE configuration buffers. */
+ #define V4L2_META_FMT_RPI_BE_CFG v4l2_fourcc('R', 'P', 'B', 'C')
+
++/* The metadata format identifier for FE configuration buffers. */
++#define V4L2_META_FMT_RPI_FE_CFG v4l2_fourcc('R', 'P', 'F', 'C')
++
++/* The metadata format identifier for FE configuration buffers. */
++#define V4L2_META_FMT_RPI_FE_STATS v4l2_fourcc('R', 'P', 'F', 'S')
++
+ /* priv field value to indicates that subsequent fields are valid. */
+ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe
+
--- /dev/null
+From 01f31f4145d49a30eb553c65ea755dde8dba1de0 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 2 Mar 2022 16:10:50 +0000
+Subject: [PATCH] V4L2: Add PiSP compressed formats to V4L2
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++++
+ include/uapi/linux/videodev2.h | 6 +++++-
+ 2 files changed, 9 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1507,6 +1507,10 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_QC08C: descr = "QCOM Compressed 8-bit Format"; break;
+ case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
+ case V4L2_PIX_FMT_RPI_BE: descr = "PiSP Opaque Format"; break;
++ case V4L2_PIX_FMT_PISP_COMP_RGGB:
++ case V4L2_PIX_FMT_PISP_COMP_GRBG:
++ case V4L2_PIX_FMT_PISP_COMP_GBRG:
++ case V4L2_PIX_FMT_PISP_COMP_BGGR: descr = "PiSP Bayer Compressed Format"; break;
+ default:
+ if (fmt->description[0])
+ return;
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -794,7 +794,11 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_IPU3_SRGGB10 v4l2_fourcc('i', 'p', '3', 'r') /* IPU3 packed 10-bit RGGB bayer */
+
+ /* The pixel format for all our buffers (the precise format is found in the config buffer). */
+-#define V4L2_PIX_FMT_RPI_BE v4l2_fourcc('R', 'P', 'B', 'P')
++#define V4L2_PIX_FMT_RPI_BE v4l2_fourcc('R', 'P', 'B', 'P')
++#define V4L2_PIX_FMT_PISP_COMP_RGGB v4l2_fourcc('P', 'C', 'R', 'G')
++#define V4L2_PIX_FMT_PISP_COMP_GRBG v4l2_fourcc('P', 'C', 'G', 'R')
++#define V4L2_PIX_FMT_PISP_COMP_GBRG v4l2_fourcc('P', 'C', 'G', 'B')
++#define V4L2_PIX_FMT_PISP_COMP_BGGR v4l2_fourcc('P', 'C', 'B', 'G')
+
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
--- /dev/null
+From c93f469dabdbed822e5abeb5283d79fc9faa858c Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 28 Oct 2022 14:10:34 +0100
+Subject: [PATCH] dt-binding: mfd: Add binding for Raspberry Pi RP1
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ include/dt-bindings/mfd/rp1.h | 235 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 235 insertions(+)
+ create mode 100644 include/dt-bindings/mfd/rp1.h
+
+--- /dev/null
++++ b/include/dt-bindings/mfd/rp1.h
+@@ -0,0 +1,235 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * This header provides constants for the PY MFD.
++ */
++
++#ifndef _RP1_H
++#define _RP1_H
++
++/* Address map */
++#define RP1_SYSINFO_BASE 0x000000
++#define RP1_TBMAN_BASE 0x004000
++#define RP1_SYSCFG_BASE 0x008000
++#define RP1_OTP_BASE 0x00c000
++#define RP1_POWER_BASE 0x010000
++#define RP1_RESETS_BASE 0x014000
++#define RP1_CLOCKS_BANK_DEFAULT_BASE 0x018000
++#define RP1_CLOCKS_BANK_VIDEO_BASE 0x01c000
++#define RP1_PLL_SYS_BASE 0x020000
++#define RP1_PLL_AUDIO_BASE 0x024000
++#define RP1_PLL_VIDEO_BASE 0x028000
++#define RP1_UART0_BASE 0x030000
++#define RP1_UART1_BASE 0x034000
++#define RP1_UART2_BASE 0x038000
++#define RP1_UART3_BASE 0x03c000
++#define RP1_UART4_BASE 0x040000
++#define RP1_UART5_BASE 0x044000
++#define RP1_SPI8_BASE 0x04c000
++#define RP1_SPI0_BASE 0x050000
++#define RP1_SPI1_BASE 0x054000
++#define RP1_SPI2_BASE 0x058000
++#define RP1_SPI3_BASE 0x05c000
++#define RP1_SPI4_BASE 0x060000
++#define RP1_SPI5_BASE 0x064000
++#define RP1_SPI6_BASE 0x068000
++#define RP1_SPI7_BASE 0x06c000
++#define RP1_I2C0_BASE 0x070000
++#define RP1_I2C1_BASE 0x074000
++#define RP1_I2C2_BASE 0x078000
++#define RP1_I2C3_BASE 0x07c000
++#define RP1_I2C4_BASE 0x080000
++#define RP1_I2C5_BASE 0x084000
++#define RP1_I2C6_BASE 0x088000
++#define RP1_AUDIO_IN_BASE 0x090000
++#define RP1_AUDIO_OUT_BASE 0x094000
++#define RP1_PWM0_BASE 0x098000
++#define RP1_PWM1_BASE 0x09c000
++#define RP1_I2S0_BASE 0x0a0000
++#define RP1_I2S1_BASE 0x0a4000
++#define RP1_I2S2_BASE 0x0a8000
++#define RP1_TIMER_BASE 0x0ac000
++#define RP1_SDIO0_APBS_BASE 0x0b0000
++#define RP1_SDIO1_APBS_BASE 0x0b4000
++#define RP1_BUSFABRIC_MONITOR_BASE 0x0c0000
++#define RP1_BUSFABRIC_AXISHIM_BASE 0x0c4000
++#define RP1_ADC_BASE 0x0c8000
++#define RP1_IO_BANK0_BASE 0x0d0000
++#define RP1_IO_BANK1_BASE 0x0d4000
++#define RP1_IO_BANK2_BASE 0x0d8000
++#define RP1_SYS_RIO0_BASE 0x0e0000
++#define RP1_SYS_RIO1_BASE 0x0e4000
++#define RP1_SYS_RIO2_BASE 0x0e8000
++#define RP1_PADS_BANK0_BASE 0x0f0000
++#define RP1_PADS_BANK1_BASE 0x0f4000
++#define RP1_PADS_BANK2_BASE 0x0f8000
++#define RP1_PADS_ETH_BASE 0x0fc000
++#define RP1_ETH_IP_BASE 0x100000
++#define RP1_ETH_CFG_BASE 0x104000
++#define RP1_PCIE_APBS_BASE 0x108000
++#define RP1_MIPI0_CSIDMA_BASE 0x110000
++#define RP1_MIPI0_CSIHOST_BASE 0x114000
++#define RP1_MIPI0_DSIDMA_BASE 0x118000
++#define RP1_MIPI0_DSIHOST_BASE 0x11c000
++#define RP1_MIPI0_MIPICFG_BASE 0x120000
++#define RP1_MIPI0_ISP_BASE 0x124000
++#define RP1_MIPI1_CSIDMA_BASE 0x128000
++#define RP1_MIPI1_CSIHOST_BASE 0x12c000
++#define RP1_MIPI1_DSIDMA_BASE 0x130000
++#define RP1_MIPI1_DSIHOST_BASE 0x134000
++#define RP1_MIPI1_MIPICFG_BASE 0x138000
++#define RP1_MIPI1_ISP_BASE 0x13c000
++#define RP1_VIDEO_OUT_CFG_BASE 0x140000
++#define RP1_VIDEO_OUT_VEC_BASE 0x144000
++#define RP1_VIDEO_OUT_DPI_BASE 0x148000
++#define RP1_XOSC_BASE 0x150000
++#define RP1_WATCHDOG_BASE 0x154000
++#define RP1_DMA_TICK_BASE 0x158000
++#define RP1_SDIO_CLOCKS_BASE 0x15c000
++#define RP1_USBHOST0_APBS_BASE 0x160000
++#define RP1_USBHOST1_APBS_BASE 0x164000
++#define RP1_ROSC0_BASE 0x168000
++#define RP1_ROSC1_BASE 0x16c000
++#define RP1_VBUSCTRL_BASE 0x170000
++#define RP1_TICKS_BASE 0x174000
++#define RP1_PIO_APBS_BASE 0x178000
++#define RP1_SDIO0_AHBLS_BASE 0x180000
++#define RP1_SDIO1_AHBLS_BASE 0x184000
++#define RP1_DMA_BASE 0x188000
++#define RP1_RAM_BASE 0x1c0000
++#define RP1_RAM_SIZE 0x020000
++#define RP1_USBHOST0_AXIS_BASE 0x200000
++#define RP1_USBHOST1_AXIS_BASE 0x300000
++#define RP1_EXAC_BASE 0x400000
++
++/* Interrupts */
++
++#define RP1_INT_IO_BANK0 0
++#define RP1_INT_IO_BANK1 1
++#define RP1_INT_IO_BANK2 2
++#define RP1_INT_AUDIO_IN 3
++#define RP1_INT_AUDIO_OUT 4
++#define RP1_INT_PWM0 5
++#define RP1_INT_ETH 6
++#define RP1_INT_I2C0 7
++#define RP1_INT_I2C1 8
++#define RP1_INT_I2C2 9
++#define RP1_INT_I2C3 10
++#define RP1_INT_I2C4 11
++#define RP1_INT_I2C5 12
++#define RP1_INT_I2C6 13
++#define RP1_INT_I2S0 14
++#define RP1_INT_I2S1 15
++#define RP1_INT_I2S2 16
++#define RP1_INT_SDIO0 17
++#define RP1_INT_SDIO1 18
++#define RP1_INT_SPI0 19
++#define RP1_INT_SPI1 20
++#define RP1_INT_SPI2 21
++#define RP1_INT_SPI3 22
++#define RP1_INT_SPI4 23
++#define RP1_INT_SPI5 24
++#define RP1_INT_UART0 25
++#define RP1_INT_TIMER_0 26
++#define RP1_INT_TIMER_1 27
++#define RP1_INT_TIMER_2 28
++#define RP1_INT_TIMER_3 29
++#define RP1_INT_USBHOST0 30
++#define RP1_INT_USBHOST0_0 31
++#define RP1_INT_USBHOST0_1 32
++#define RP1_INT_USBHOST0_2 33
++#define RP1_INT_USBHOST0_3 34
++#define RP1_INT_USBHOST1 35
++#define RP1_INT_USBHOST1_0 36
++#define RP1_INT_USBHOST1_1 37
++#define RP1_INT_USBHOST1_2 38
++#define RP1_INT_USBHOST1_3 39
++#define RP1_INT_DMA 40
++#define RP1_INT_PWM1 41
++#define RP1_INT_UART1 42
++#define RP1_INT_UART2 43
++#define RP1_INT_UART3 44
++#define RP1_INT_UART4 45
++#define RP1_INT_UART5 46
++#define RP1_INT_MIPI0 47
++#define RP1_INT_MIPI1 48
++#define RP1_INT_VIDEO_OUT 49
++#define RP1_INT_PIO_0 50
++#define RP1_INT_PIO_1 51
++#define RP1_INT_ADC_FIFO 52
++#define RP1_INT_PCIE_OUT 53
++#define RP1_INT_SPI6 54
++#define RP1_INT_SPI7 55
++#define RP1_INT_SPI8 56
++#define RP1_INT_SYSCFG 58
++#define RP1_INT_CLOCKS_DEFAULT 59
++#define RP1_INT_VBUSCTRL 60
++#define RP1_INT_PROC_MISC 57
++#define RP1_INT_END 61
++
++/* DMA peripherals (for pacing) */
++#define RP1_DMA_I2C0_RX 0x0
++#define RP1_DMA_I2C0_TX 0x1
++#define RP1_DMA_I2C1_RX 0x2
++#define RP1_DMA_I2C1_TX 0x3
++#define RP1_DMA_I2C2_RX 0x4
++#define RP1_DMA_I2C2_TX 0x5
++#define RP1_DMA_I2C3_RX 0x6
++#define RP1_DMA_I2C3_TX 0x7
++#define RP1_DMA_I2C4_RX 0x8
++#define RP1_DMA_I2C4_TX 0x9
++#define RP1_DMA_I2C5_RX 0xa
++#define RP1_DMA_I2C5_TX 0xb
++#define RP1_DMA_SPI0_RX 0xc
++#define RP1_DMA_SPI0_TX 0xd
++#define RP1_DMA_SPI1_RX 0xe
++#define RP1_DMA_SPI1_TX 0xf
++#define RP1_DMA_SPI2_RX 0x10
++#define RP1_DMA_SPI2_TX 0x11
++#define RP1_DMA_SPI3_RX 0x12
++#define RP1_DMA_SPI3_TX 0x13
++#define RP1_DMA_SPI4_RX 0x14
++#define RP1_DMA_SPI4_TX 0x15
++#define RP1_DMA_SPI5_RX 0x16
++#define RP1_DMA_SPI5_TX 0x17
++#define RP1_DMA_PWM0 0x18
++#define RP1_DMA_UART0_RX 0x19
++#define RP1_DMA_UART0_TX 0x1a
++#define RP1_DMA_AUDIO_IN_CH0 0x1b
++#define RP1_DMA_AUDIO_IN_CH1 0x1c
++#define RP1_DMA_AUDIO_OUT 0x1d
++#define RP1_DMA_PWM1 0x1e
++#define RP1_DMA_I2S0_RX 0x1f
++#define RP1_DMA_I2S0_TX 0x20
++#define RP1_DMA_I2S1_RX 0x21
++#define RP1_DMA_I2S1_TX 0x22
++#define RP1_DMA_I2S2_RX 0x23
++#define RP1_DMA_I2S2_TX 0x24
++#define RP1_DMA_UART1_RX 0x25
++#define RP1_DMA_UART1_TX 0x26
++#define RP1_DMA_UART2_RX 0x27
++#define RP1_DMA_UART2_TX 0x28
++#define RP1_DMA_UART3_RX 0x29
++#define RP1_DMA_UART3_TX 0x2a
++#define RP1_DMA_UART4_RX 0x2b
++#define RP1_DMA_UART4_TX 0x2c
++#define RP1_DMA_UART5_RX 0x2d
++#define RP1_DMA_UART5_TX 0x2e
++#define RP1_DMA_ADC 0x2f
++#define RP1_DMA_DMA_TICK_TICK0 0x30
++#define RP1_DMA_DMA_TICK_TICK1 0x31
++#define RP1_DMA_SPI6_RX 0x32
++#define RP1_DMA_SPI6_TX 0x33
++#define RP1_DMA_SPI7_RX 0x34
++#define RP1_DMA_SPI7_TX 0x35
++#define RP1_DMA_SPI8_RX 0x36
++#define RP1_DMA_SPI8_TX 0x37
++#define RP1_DMA_PIO_CH0_TX 0x38
++#define RP1_DMA_PIO_CH0_RX 0x39
++#define RP1_DMA_PIO_CH1_TX 0x3a
++#define RP1_DMA_PIO_CH1_RX 0x3b
++#define RP1_DMA_PIO_CH2_TX 0x3c
++#define RP1_DMA_PIO_CH2_RX 0x3d
++#define RP1_DMA_PIO_CH3_TX 0x3e
++#define RP1_DMA_PIO_CH3_RX 0x3f
++
++#endif
--- /dev/null
+From 7196a12b94e90225686e6c34cdf65a583214f7a5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 10 Oct 2022 14:21:50 +0100
+Subject: [PATCH] mfd: Add rp1 driver
+
+RP1 is a multifunction PCIe device that exposes a range of
+peripherals.
+Add the parent driver to manage these.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/mfd/Kconfig | 11 ++
+ drivers/mfd/Makefile | 1 +
+ drivers/mfd/rp1.c | 367 +++++++++++++++++++++++++++++++++++
+ include/linux/rp1_platform.h | 20 ++
+ 4 files changed, 399 insertions(+)
+ create mode 100644 drivers/mfd/rp1.c
+ create mode 100644 include/linux/rp1_platform.h
+
+--- a/drivers/mfd/Kconfig
++++ b/drivers/mfd/Kconfig
+@@ -2252,6 +2252,17 @@ config MFD_INTEL_M10_BMC
+ additional drivers must be enabled in order to use the functionality
+ of the device.
+
++config MFD_RP1
++ tristate "RP1 MFD driver"
++ depends on PCI
++ select MFD_CORE
++ help
++ Support for the RP1 peripheral chip.
++
++ This driver provides support for the Raspberry Pi RP1 peripheral chip.
++ It is responsible for enabling the Device Tree node once the PCIe endpoint
++ has been configured, and handling interrupts.
++
+ config MFD_RSMU_I2C
+ tristate "Renesas Synchronization Management Unit with I2C"
+ depends on I2C && OF
+--- a/drivers/mfd/Makefile
++++ b/drivers/mfd/Makefile
+@@ -273,6 +273,7 @@ obj-$(CONFIG_MFD_RPISENSE_CORE) += rpise
+ obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
+ obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
+ obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
++obj-$(CONFIG_MFD_RP1) += rp1.o
+
+ obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
+ obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o
+--- /dev/null
++++ b/drivers/mfd/rp1.c
+@@ -0,0 +1,367 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Copyright (c) 2018-22 Raspberry Pi Ltd.
++ * All rights reserved.
++ */
++
++#include <linux/clk.h>
++#include <linux/clkdev.h>
++#include <linux/clk-provider.h>
++#include <linux/completion.h>
++#include <linux/etherdevice.h>
++#include <linux/err.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/irqchip/chained_irq.h>
++#include <linux/irqdomain.h>
++#include <linux/mfd/core.h>
++#include <linux/mmc/host.h>
++#include <linux/module.h>
++#include <linux/msi.h>
++#include <linux/of_platform.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/rp1_platform.h>
++#include <linux/reset.h>
++#include <linux/slab.h>
++
++#include <dt-bindings/mfd/rp1.h>
++
++/* TO DO:
++ * 1. Occasional shutdown crash - RP1 being closed before its children?
++ * 2. DT mode interrupt handling.
++ */
++
++#define RP1_DRIVER_NAME "rp1"
++
++#define PCI_VENDOR_ID_RPI 0x1de4
++#define PCI_DEVICE_ID_RP1_C0 0x0001
++#define PCI_DEVICE_REV_RP1_C0 2
++
++#define RP1_ACTUAL_IRQS RP1_INT_END
++#define RP1_IRQS RP1_ACTUAL_IRQS
++
++#define RP1_SYSCLK_RATE 200000000
++#define RP1_SYSCLK_FPGA_RATE 60000000
++
++// Don't want to include the whole sysinfo reg header
++#define SYSINFO_CHIP_ID_OFFSET 0x00000000
++#define SYSINFO_PLATFORM_OFFSET 0x00000004
++
++#define REG_RW 0x000
++#define REG_SET 0x800
++#define REG_CLR 0xc00
++
++// MSIX CFG registers start at 0x8
++#define MSIX_CFG(x) (0x8 + (4 * (x)))
++
++#define MSIX_CFG_IACK_EN BIT(3)
++#define MSIX_CFG_IACK BIT(2)
++#define MSIX_CFG_TEST BIT(1)
++#define MSIX_CFG_ENABLE BIT(0)
++
++#define INTSTATL 0x108
++#define INTSTATH 0x10c
++
++struct rp1_dev {
++ struct pci_dev *pdev;
++ struct device *dev;
++ resource_size_t bar_start;
++ resource_size_t bar_end;
++ struct clk *sys_clk;
++ struct irq_domain *domain;
++ struct irq_data *pcie_irqds[64];
++ void __iomem *msix_cfg_regs;
++};
++
++static bool rp1_level_triggered_irq[RP1_ACTUAL_IRQS] = { 0 };
++
++static struct rp1_dev *g_rp1;
++static u32 g_chip_id, g_platform;
++
++static void dump_bar(struct pci_dev *pdev, unsigned int bar)
++{
++ dev_info(&pdev->dev,
++ "bar%d len 0x%llx, start 0x%llx, end 0x%llx, flags, 0x%lx\n",
++ bar,
++ pci_resource_len(pdev, bar),
++ pci_resource_start(pdev, bar),
++ pci_resource_end(pdev, bar),
++ pci_resource_flags(pdev, bar));
++}
++
++static void msix_cfg_set(struct rp1_dev *rp1, unsigned int hwirq, u32 value)
++{
++ writel(value, rp1->msix_cfg_regs + REG_SET + MSIX_CFG(hwirq));
++}
++
++static void msix_cfg_clr(struct rp1_dev *rp1, unsigned int hwirq, u32 value)
++{
++ writel(value, rp1->msix_cfg_regs + REG_CLR + MSIX_CFG(hwirq));
++}
++
++static void rp1_mask_irq(struct irq_data *irqd)
++{
++ struct rp1_dev *rp1 = irqd->domain->host_data;
++ struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++
++ pci_msi_mask_irq(pcie_irqd);
++}
++
++static void rp1_unmask_irq(struct irq_data *irqd)
++{
++ struct rp1_dev *rp1 = irqd->domain->host_data;
++ struct irq_data *pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++
++ pci_msi_unmask_irq(pcie_irqd);
++}
++
++static int rp1_irq_set_type(struct irq_data *irqd, unsigned int type)
++{
++ struct rp1_dev *rp1 = irqd->domain->host_data;
++ unsigned int hwirq = (unsigned int)irqd->hwirq;
++ int ret = 0;
++
++ switch (type) {
++ case IRQ_TYPE_LEVEL_HIGH:
++ dev_dbg(rp1->dev, "MSIX IACK EN for irq %d\n", hwirq);
++ msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK_EN);
++ rp1_level_triggered_irq[hwirq] = true;
++ break;
++ case IRQ_TYPE_EDGE_RISING:
++ msix_cfg_clr(rp1, hwirq, MSIX_CFG_IACK_EN);
++ rp1_level_triggered_irq[hwirq] = false;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++ return ret;
++}
++
++static struct irq_chip rp1_irq_chip = {
++ .name = "rp1_irq_chip",
++ .irq_mask = rp1_mask_irq,
++ .irq_unmask = rp1_unmask_irq,
++ .irq_set_type = rp1_irq_set_type,
++};
++
++static void rp1_chained_handle_irq(struct irq_desc *desc)
++{
++ struct irq_chip *chip = irq_desc_get_chip(desc);
++ struct rp1_dev *rp1 = desc->irq_data.chip_data;
++ unsigned int hwirq = desc->irq_data.hwirq & 0x3f;
++ int new_irq;
++
++ rp1 = g_rp1;
++
++ chained_irq_enter(chip, desc);
++
++ new_irq = irq_linear_revmap(rp1->domain, hwirq);
++ generic_handle_irq(new_irq);
++ if (rp1_level_triggered_irq[hwirq])
++ msix_cfg_set(rp1, hwirq, MSIX_CFG_IACK);
++
++ chained_irq_exit(chip, desc);
++}
++
++static int rp1_irq_xlate(struct irq_domain *d, struct device_node *node,
++ const u32 *intspec, unsigned int intsize,
++ unsigned long *out_hwirq, unsigned int *out_type)
++{
++ struct rp1_dev *rp1 = d->host_data;
++ struct irq_data *pcie_irqd;
++ unsigned long hwirq;
++ int pcie_irq;
++ int ret;
++
++ ret = irq_domain_xlate_twocell(d, node, intspec, intsize,
++ &hwirq, out_type);
++ if (!ret) {
++ pcie_irq = pci_irq_vector(rp1->pdev, hwirq);
++ pcie_irqd = irq_get_irq_data(pcie_irq);
++ rp1->pcie_irqds[hwirq] = pcie_irqd;
++ *out_hwirq = hwirq;
++ }
++ return ret;
++}
++
++static int rp1_irq_activate(struct irq_domain *d, struct irq_data *irqd,
++ bool reserve)
++{
++ struct rp1_dev *rp1 = d->host_data;
++ struct irq_data *pcie_irqd;
++
++ pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++ msix_cfg_set(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE);
++ return irq_domain_activate_irq(pcie_irqd, reserve);
++}
++
++static void rp1_irq_deactivate(struct irq_domain *d, struct irq_data *irqd)
++{
++ struct rp1_dev *rp1 = d->host_data;
++ struct irq_data *pcie_irqd;
++
++ pcie_irqd = rp1->pcie_irqds[irqd->hwirq];
++ msix_cfg_clr(rp1, (unsigned int)irqd->hwirq, MSIX_CFG_ENABLE);
++ return irq_domain_deactivate_irq(pcie_irqd);
++}
++
++static const struct irq_domain_ops rp1_domain_ops = {
++ .xlate = rp1_irq_xlate,
++ .activate = rp1_irq_activate,
++ .deactivate = rp1_irq_deactivate,
++};
++
++static inline dma_addr_t rp1_io_to_phys(struct rp1_dev *rp1, unsigned int offset)
++{
++ return rp1->bar_start + offset;
++}
++
++static u32 rp1_reg_read(struct rp1_dev *rp1, unsigned int base_addr, u32 offset)
++{
++ dma_addr_t phys = rp1_io_to_phys(rp1, base_addr);
++ void __iomem *regblock = ioremap(phys, 0x1000);
++ u32 value = readl(regblock + offset);
++
++ iounmap(regblock);
++ return value;
++}
++
++void rp1_get_platform(u32 *chip_id, u32 *platform)
++{
++ if (chip_id)
++ *chip_id = g_chip_id;
++ if (platform)
++ *platform = g_platform;
++}
++EXPORT_SYMBOL_GPL(rp1_get_platform);
++
++static int rp1_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++ struct reset_control *reset;
++ struct platform_device *pcie_pdev;
++ struct device_node *rp1_node;
++ struct rp1_dev *rp1;
++ int err = 0;
++ int i;
++
++ reset = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL);
++ if (IS_ERR(reset))
++ return PTR_ERR(reset);
++ reset_control_reset(reset);
++
++ dump_bar(pdev, 0);
++ dump_bar(pdev, 1);
++
++ if (pci_resource_len(pdev, 1) <= 0x10000) {
++ dev_err(&pdev->dev,
++ "Not initialised - is the firmware running?\n");
++ return -EINVAL;
++ }
++
++ /* enable pci device */
++ err = pcim_enable_device(pdev);
++ if (err < 0) {
++ dev_err(&pdev->dev, "Enabling PCI device has failed: %d",
++ err);
++ return err;
++ }
++
++ pci_set_master(pdev);
++
++ err = pci_alloc_irq_vectors(pdev, RP1_IRQS, RP1_IRQS,
++ PCI_IRQ_MSIX);
++ if (err != RP1_IRQS) {
++ dev_err(&pdev->dev, "pci_alloc_irq_vectors failed - %d\n", err);
++ return err;
++ }
++
++ rp1 = devm_kzalloc(&pdev->dev, sizeof(*rp1), GFP_KERNEL);
++ if (!rp1)
++ return -ENOMEM;
++
++ rp1->pdev = pdev;
++ rp1->dev = &pdev->dev;
++
++ pci_set_drvdata(pdev, rp1);
++
++ rp1->bar_start = pci_resource_start(pdev, 1);
++ rp1->bar_end = pci_resource_end(pdev, 1);
++
++ // Get chip id
++ g_chip_id = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_CHIP_ID_OFFSET);
++ g_platform = rp1_reg_read(rp1, RP1_SYSINFO_BASE, SYSINFO_PLATFORM_OFFSET);
++ dev_info(&pdev->dev, "chip_id 0x%x%s\n", g_chip_id,
++ (g_platform & RP1_PLATFORM_FPGA) ? " FPGA" : "");
++ if (g_chip_id != RP1_C0_CHIP_ID) {
++ dev_err(&pdev->dev, "wrong chip id (%x)\n", g_chip_id);
++ return -EINVAL;
++ }
++
++ rp1_node = of_find_node_by_name(NULL, "rp1");
++ if (!rp1_node) {
++ dev_err(&pdev->dev, "failed to find RP1 DT node\n");
++ return -EINVAL;
++ }
++
++ pcie_pdev = of_find_device_by_node(rp1_node->parent);
++ rp1->domain = irq_domain_add_linear(rp1_node, RP1_IRQS,
++ &rp1_domain_ops, rp1);
++
++ g_rp1 = rp1;
++
++ /* TODO can this go in the rp1 device tree entry? */
++ rp1->msix_cfg_regs = ioremap(rp1_io_to_phys(rp1, RP1_PCIE_APBS_BASE), 0x1000);
++
++ for (i = 0; i < RP1_IRQS; i++) {
++ int irq = irq_create_mapping(rp1->domain, i);
++
++ if (irq < 0) {
++ dev_err(&pdev->dev, "failed to create irq mapping\n");
++ return irq;
++ }
++
++ irq_set_chip_data(irq, rp1);
++ irq_set_chip_and_handler(irq, &rp1_irq_chip, handle_level_irq);
++ irq_set_probe(irq);
++ irq_set_chained_handler(pci_irq_vector(pdev, i),
++ rp1_chained_handle_irq);
++ }
++
++ if (rp1_node)
++ of_platform_populate(rp1_node, NULL, NULL, &pcie_pdev->dev);
++
++ of_node_put(rp1_node);
++
++ return 0;
++}
++
++static void rp1_remove(struct pci_dev *pdev)
++{
++ struct rp1_dev *rp1 = pci_get_drvdata(pdev);
++
++ mfd_remove_devices(&pdev->dev);
++
++ clk_unregister(rp1->sys_clk);
++}
++
++static const struct pci_device_id dev_id_table[] = {
++ { PCI_DEVICE(PCI_VENDOR_ID_RPI, PCI_DEVICE_ID_RP1_C0), },
++ { 0, }
++};
++
++static struct pci_driver rp1_driver = {
++ .name = RP1_DRIVER_NAME,
++ .id_table = dev_id_table,
++ .probe = rp1_probe,
++ .remove = rp1_remove,
++};
++
++module_pci_driver(rp1_driver);
++
++MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
++MODULE_DESCRIPTION("RP1 wrapper");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/include/linux/rp1_platform.h
+@@ -0,0 +1,20 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2021-2022 Raspberry Pi Ltd.
++ * All rights reserved.
++ */
++
++#ifndef _RP1_PLATFORM_H
++#define _RP1_PLATFORM_H
++
++#include <vdso/bits.h>
++
++#define RP1_B0_CHIP_ID 0x10001927
++#define RP1_C0_CHIP_ID 0x20001927
++
++#define RP1_PLATFORM_ASIC BIT(1)
++#define RP1_PLATFORM_FPGA BIT(0)
++
++void rp1_get_platform(u32 *chip_id, u32 *platform);
++
++#endif
--- /dev/null
+From 00ff2819eb852b54fe22e7181646e40d560576dc Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 28 Oct 2022 14:12:18 +0100
+Subject: [PATCH] dt-bindings: clock: Add bindings for Raspberry Pi RP1
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ include/dt-bindings/clock/rp1.h | 51 +++++++++++++++++++++++++++++++++
+ 1 file changed, 51 insertions(+)
+ create mode 100644 include/dt-bindings/clock/rp1.h
+
+--- /dev/null
++++ b/include/dt-bindings/clock/rp1.h
+@@ -0,0 +1,51 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (C) 2021 Raspberry Pi Ltd.
++ */
++
++#define RP1_PLL_SYS_CORE 0
++#define RP1_PLL_AUDIO_CORE 1
++#define RP1_PLL_VIDEO_CORE 2
++
++#define RP1_PLL_SYS 3
++#define RP1_PLL_AUDIO 4
++#define RP1_PLL_VIDEO 5
++
++#define RP1_PLL_SYS_PRI_PH 6
++#define RP1_PLL_SYS_SEC_PH 7
++
++#define RP1_PLL_SYS_SEC 8
++#define RP1_PLL_AUDIO_SEC 9
++#define RP1_PLL_VIDEO_SEC 10
++
++#define RP1_CLK_SYS 11
++#define RP1_CLK_SLOW_SYS 12
++#define RP1_CLK_DMA 13
++#define RP1_CLK_UART 14
++#define RP1_CLK_ETH 15
++#define RP1_CLK_PWM0 16
++#define RP1_CLK_PWM1 17
++#define RP1_CLK_AUDIO_IN 18
++#define RP1_CLK_AUDIO_OUT 19
++#define RP1_CLK_I2S 20
++#define RP1_CLK_MIPI0_CFG 21
++#define RP1_CLK_MIPI1_CFG 22
++#define RP1_CLK_PCIE_AUX 23
++#define RP1_CLK_USBH0_MICROFRAME 24
++#define RP1_CLK_USBH1_MICROFRAME 25
++#define RP1_CLK_USBH0_SUSPEND 26
++#define RP1_CLK_USBH1_SUSPEND 27
++#define RP1_CLK_ETH_TSU 28
++#define RP1_CLK_ADC 29
++#define RP1_CLK_SDIO_TIMER 30
++#define RP1_CLK_SDIO_ALT_SRC 31
++#define RP1_CLK_GP0 32
++#define RP1_CLK_GP1 33
++#define RP1_CLK_GP2 34
++#define RP1_CLK_GP3 35
++#define RP1_CLK_GP4 36
++#define RP1_CLK_GP5 37
++#define RP1_CLK_VEC 38
++#define RP1_CLK_DPI 39
++#define RP1_CLK_MIPI0_DPI 40
++#define RP1_CLK_MIPI1_DPI 41
--- /dev/null
+From 66517cdfea750b89d86f78af55ef773cbd3e005f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 10 Oct 2022 14:25:38 +0100
+Subject: [PATCH] clk: Add rp1 clock driver
+
+RP1 contains various PLLs and clocks for driving the hardware
+blocks, so add a driver to configure these.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/clk/Kconfig | 7 +
+ drivers/clk/Makefile | 1 +
+ drivers/clk/clk-rp1.c | 2085 +++++++++++++++++++++++++++++++
+ include/dt-bindings/clock/rp1.h | 69 +-
+ 4 files changed, 2128 insertions(+), 34 deletions(-)
+ create mode 100644 drivers/clk/clk-rp1.c
+
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -89,6 +89,13 @@ config COMMON_CLK_RK808
+ These multi-function devices have two fixed-rate oscillators, clocked at 32KHz each.
+ Clkout1 is always on, Clkout2 can off by control register.
+
++config COMMON_CLK_RP1
++ tristate "Raspberry Pi RP1-based clock support"
++ depends on PCI || COMPILE_TEST
++ depends on COMMON_CLK
++ help
++ Enable common clock framework support for Raspberry Pi RP1
++
+ config COMMON_CLK_HI655X
+ tristate "Clock driver for Hi655x" if EXPERT
+ depends on (MFD_HI655X_PMIC || COMPILE_TEST)
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -58,6 +58,7 @@ obj-$(CONFIG_CLK_LS1028A_PLLDIG) += clk-
+ obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm.o
+ obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
+ obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
++obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o
+ obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
+ obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
+ obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
+--- /dev/null
++++ b/drivers/clk/clk-rp1.c
+@@ -0,0 +1,2085 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ *
++ * Clock driver for RP1 PCIe multifunction chip.
++ */
++
++#include <linux/clk-provider.h>
++#include <linux/clkdev.h>
++#include <linux/clk.h>
++#include <linux/debugfs.h>
++#include <linux/delay.h>
++#include <linux/io.h>
++#include <linux/math64.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/rp1_platform.h>
++#include <linux/slab.h>
++
++#include <asm/div64.h>
++
++#include <dt-bindings/clock/rp1.h>
++
++#define PLL_SYS_CS 0x08000
++#define PLL_SYS_PWR 0x08004
++#define PLL_SYS_FBDIV_INT 0x08008
++#define PLL_SYS_FBDIV_FRAC 0x0800c
++#define PLL_SYS_PRIM 0x08010
++#define PLL_SYS_SEC 0x08014
++
++#define PLL_AUDIO_CS 0x0c000
++#define PLL_AUDIO_PWR 0x0c004
++#define PLL_AUDIO_FBDIV_INT 0x0c008
++#define PLL_AUDIO_FBDIV_FRAC 0x0c00c
++#define PLL_AUDIO_PRIM 0x0c010
++#define PLL_AUDIO_SEC 0x0c014
++
++#define PLL_VIDEO_CS 0x10000
++#define PLL_VIDEO_PWR 0x10004
++#define PLL_VIDEO_FBDIV_INT 0x10008
++#define PLL_VIDEO_FBDIV_FRAC 0x1000c
++#define PLL_VIDEO_PRIM 0x10010
++#define PLL_VIDEO_SEC 0x10014
++
++#define CLK_SYS_CTRL 0x00014
++#define CLK_SYS_DIV_INT 0x00018
++#define CLK_SYS_SEL 0x00020
++
++#define CLK_SLOW_SYS_CTRL 0x00024
++#define CLK_SLOW_SYS_DIV_INT 0x00028
++#define CLK_SLOW_SYS_SEL 0x00030
++
++#define CLK_DMA_CTRL 0x00044
++#define CLK_DMA_DIV_INT 0x00048
++#define CLK_DMA_SEL 0x00050
++
++#define CLK_UART_CTRL 0x00054
++#define CLK_UART_DIV_INT 0x00058
++#define CLK_UART_SEL 0x00060
++
++#define CLK_ETH_CTRL 0x00064
++#define CLK_ETH_DIV_INT 0x00068
++#define CLK_ETH_SEL 0x00070
++
++#define CLK_PWM0_CTRL 0x00074
++#define CLK_PWM0_DIV_INT 0x00078
++#define CLK_PWM0_DIV_FRAC 0x0007c
++#define CLK_PWM0_SEL 0x00080
++
++#define CLK_PWM1_CTRL 0x00084
++#define CLK_PWM1_DIV_INT 0x00088
++#define CLK_PWM1_DIV_FRAC 0x0008c
++#define CLK_PWM1_SEL 0x00090
++
++#define CLK_AUDIO_IN_CTRL 0x00094
++#define CLK_AUDIO_IN_DIV_INT 0x00098
++#define CLK_AUDIO_IN_SEL 0x000a0
++
++#define CLK_AUDIO_OUT_CTRL 0x000a4
++#define CLK_AUDIO_OUT_DIV_INT 0x000a8
++#define CLK_AUDIO_OUT_SEL 0x000b0
++
++#define CLK_I2S_CTRL 0x000b4
++#define CLK_I2S_DIV_INT 0x000b8
++#define CLK_I2S_SEL 0x000c0
++
++#define CLK_MIPI0_CFG_CTRL 0x000c4
++#define CLK_MIPI0_CFG_DIV_INT 0x000c8
++#define CLK_MIPI0_CFG_SEL 0x000d0
++
++#define CLK_MIPI1_CFG_CTRL 0x000d4
++#define CLK_MIPI1_CFG_DIV_INT 0x000d8
++#define CLK_MIPI1_CFG_SEL 0x000e0
++
++#define CLK_PCIE_AUX_CTRL 0x000e4
++#define CLK_PCIE_AUX_DIV_INT 0x000e8
++#define CLK_PCIE_AUX_SEL 0x000f0
++
++#define CLK_USBH0_MICROFRAME_CTRL 0x000f4
++#define CLK_USBH0_MICROFRAME_DIV_INT 0x000f8
++#define CLK_USBH0_MICROFRAME_SEL 0x00100
++
++#define CLK_USBH1_MICROFRAME_CTRL 0x00104
++#define CLK_USBH1_MICROFRAME_DIV_INT 0x00108
++#define CLK_USBH1_MICROFRAME_SEL 0x00110
++
++#define CLK_USBH0_SUSPEND_CTRL 0x00114
++#define CLK_USBH0_SUSPEND_DIV_INT 0x00118
++#define CLK_USBH0_SUSPEND_SEL 0x00120
++
++#define CLK_USBH1_SUSPEND_CTRL 0x00124
++#define CLK_USBH1_SUSPEND_DIV_INT 0x00128
++#define CLK_USBH1_SUSPEND_SEL 0x00130
++
++#define CLK_ETH_TSU_CTRL 0x00134
++#define CLK_ETH_TSU_DIV_INT 0x00138
++#define CLK_ETH_TSU_SEL 0x00140
++
++#define CLK_ADC_CTRL 0x00144
++#define CLK_ADC_DIV_INT 0x00148
++#define CLK_ADC_SEL 0x00150
++
++#define CLK_SDIO_TIMER_CTRL 0x00154
++#define CLK_SDIO_TIMER_DIV_INT 0x00158
++#define CLK_SDIO_TIMER_SEL 0x00160
++
++#define CLK_SDIO_ALT_SRC_CTRL 0x00164
++#define CLK_SDIO_ALT_SRC_DIV_INT 0x00168
++#define CLK_SDIO_ALT_SRC_SEL 0x00170
++
++#define CLK_GP0_CTRL 0x00174
++#define CLK_GP0_DIV_INT 0x00178
++#define CLK_GP0_DIV_FRAC 0x0017c
++#define CLK_GP0_SEL 0x00180
++
++#define CLK_GP1_CTRL 0x00184
++#define CLK_GP1_DIV_INT 0x00188
++#define CLK_GP1_DIV_FRAC 0x0018c
++#define CLK_GP1_SEL 0x00190
++
++#define CLK_GP2_CTRL 0x00194
++#define CLK_GP2_DIV_INT 0x00198
++#define CLK_GP2_DIV_FRAC 0x0019c
++#define CLK_GP2_SEL 0x001a0
++
++#define CLK_GP3_CTRL 0x001a4
++#define CLK_GP3_DIV_INT 0x001a8
++#define CLK_GP3_DIV_FRAC 0x001ac
++#define CLK_GP3_SEL 0x001b0
++
++#define CLK_GP4_CTRL 0x001b4
++#define CLK_GP4_DIV_INT 0x001b8
++#define CLK_GP4_DIV_FRAC 0x001bc
++#define CLK_GP4_SEL 0x001c0
++
++#define CLK_GP5_CTRL 0x001c4
++#define CLK_GP5_DIV_INT 0x001c8
++#define CLK_GP5_DIV_FRAC 0x001cc
++#define CLK_GP5_SEL 0x001d0
++
++#define CLK_SYS_RESUS_CTRL 0x0020c
++
++#define CLK_SLOW_SYS_RESUS_CTRL 0x00214
++
++#define FC0_REF_KHZ 0x0021c
++#define FC0_MIN_KHZ 0x00220
++#define FC0_MAX_KHZ 0x00224
++#define FC0_DELAY 0x00228
++#define FC0_INTERVAL 0x0022c
++#define FC0_SRC 0x00230
++#define FC0_STATUS 0x00234
++#define FC0_RESULT 0x00238
++#define FC_SIZE 0x20
++#define FC_COUNT 8
++#define FC_NUM(idx, off) ((idx) * 32 + (off))
++
++#define AUX_SEL 1
++
++#define VIDEO_CLOCKS_OFFSET 0x4000
++#define VIDEO_CLK_VEC_CTRL (VIDEO_CLOCKS_OFFSET + 0x0000)
++#define VIDEO_CLK_VEC_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0004)
++#define VIDEO_CLK_VEC_SEL (VIDEO_CLOCKS_OFFSET + 0x000c)
++#define VIDEO_CLK_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0010)
++#define VIDEO_CLK_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0014)
++#define VIDEO_CLK_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x001c)
++#define VIDEO_CLK_MIPI0_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0020)
++#define VIDEO_CLK_MIPI0_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0024)
++#define VIDEO_CLK_MIPI0_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0028)
++#define VIDEO_CLK_MIPI0_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x002c)
++#define VIDEO_CLK_MIPI1_DPI_CTRL (VIDEO_CLOCKS_OFFSET + 0x0030)
++#define VIDEO_CLK_MIPI1_DPI_DIV_INT (VIDEO_CLOCKS_OFFSET + 0x0034)
++#define VIDEO_CLK_MIPI1_DPI_DIV_FRAC (VIDEO_CLOCKS_OFFSET + 0x0038)
++#define VIDEO_CLK_MIPI1_DPI_SEL (VIDEO_CLOCKS_OFFSET + 0x003c)
++
++#define DIV_INT_8BIT_MAX 0x000000ffu /* max divide for most clocks */
++#define DIV_INT_16BIT_MAX 0x0000ffffu /* max divide for GPx, PWM */
++#define DIV_INT_24BIT_MAX 0x00ffffffu /* max divide for CLK_SYS */
++
++#define FC0_STATUS_DONE BIT(4)
++#define FC0_STATUS_RUNNING BIT(8)
++#define FC0_RESULT_FRAC_SHIFT 5
++
++#define PLL_PRIM_DIV1_SHIFT 16
++#define PLL_PRIM_DIV1_MASK 0x00070000
++#define PLL_PRIM_DIV2_SHIFT 12
++#define PLL_PRIM_DIV2_MASK 0x00007000
++
++#define PLL_SEC_DIV_SHIFT 8
++#define PLL_SEC_DIV_WIDTH 5
++#define PLL_SEC_DIV_MASK 0x00001f00
++
++#define PLL_CS_LOCK BIT(31)
++#define PLL_CS_REFDIV_SHIFT 0
++
++#define PLL_PWR_PD BIT(0)
++#define PLL_PWR_DACPD BIT(1)
++#define PLL_PWR_DSMPD BIT(2)
++#define PLL_PWR_POSTDIVPD BIT(3)
++#define PLL_PWR_4PHASEPD BIT(4)
++#define PLL_PWR_VCOPD BIT(5)
++#define PLL_PWR_MASK 0x0000003f
++
++#define PLL_SEC_RST BIT(16)
++#define PLL_SEC_IMPL BIT(31)
++
++/* PLL phase output for both PRI and SEC */
++#define PLL_PH_EN BIT(4)
++#define PLL_PH_PHASE_SHIFT 0
++
++#define RP1_PLL_PHASE_0 0
++#define RP1_PLL_PHASE_90 1
++#define RP1_PLL_PHASE_180 2
++#define RP1_PLL_PHASE_270 3
++
++/* Clock fields for all clocks */
++#define CLK_CTRL_ENABLE BIT(11)
++#define CLK_CTRL_AUXSRC_MASK 0x000003e0
++#define CLK_CTRL_AUXSRC_SHIFT 5
++#define CLK_CTRL_SRC_SHIFT 0
++#define CLK_DIV_FRAC_BITS 16
++
++#define KHz 1000
++#define MHz (KHz * KHz)
++#define LOCK_TIMEOUT_NS 100000000
++#define FC_TIMEOUT_NS 100000000
++
++#define MAX_CLK_PARENTS 8
++
++#define MEASURE_CLOCK_RATE
++const char * const fc0_ref_clk_name = "clk_slow_sys";
++
++#define ABS_DIFF(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
++#define DIV_U64_NEAREST(a, b) div_u64(((a) + ((b) >> 1)), (b))
++
++/*
++ * Names of the reference clock for the pll cores. This name must match
++ * the DT reference clock-output-name.
++ */
++static const char *const ref_clock = "xosc";
++
++/*
++ * Secondary PLL channel output divider table.
++ * Divider values range from 8 to 19.
++ * Invalid values default to 19
++ */
++static const struct clk_div_table pll_sec_div_table[] = {
++ { 0x00, 19 },
++ { 0x01, 19 },
++ { 0x02, 19 },
++ { 0x03, 19 },
++ { 0x04, 19 },
++ { 0x05, 19 },
++ { 0x06, 19 },
++ { 0x07, 19 },
++ { 0x08, 8 },
++ { 0x09, 9 },
++ { 0x0a, 10 },
++ { 0x0b, 11 },
++ { 0x0c, 12 },
++ { 0x0d, 13 },
++ { 0x0e, 14 },
++ { 0x0f, 15 },
++ { 0x10, 16 },
++ { 0x11, 17 },
++ { 0x12, 18 },
++ { 0x13, 19 },
++ { 0x14, 19 },
++ { 0x15, 19 },
++ { 0x16, 19 },
++ { 0x17, 19 },
++ { 0x18, 19 },
++ { 0x19, 19 },
++ { 0x1a, 19 },
++ { 0x1b, 19 },
++ { 0x1c, 19 },
++ { 0x1d, 19 },
++ { 0x1e, 19 },
++ { 0x1f, 19 },
++ { 0 }
++};
++
++struct rp1_clockman {
++ struct device *dev;
++ void __iomem *regs;
++ spinlock_t regs_lock; /* spinlock for all clocks */
++
++ /* Must be last */
++ struct clk_hw_onecell_data onecell;
++};
++
++struct rp1_pll_core_data {
++ const char *name;
++ u32 cs_reg;
++ u32 pwr_reg;
++ u32 fbdiv_int_reg;
++ u32 fbdiv_frac_reg;
++ unsigned long flags;
++ u32 fc0_src;
++};
++
++struct rp1_pll_data {
++ const char *name;
++ const char *source_pll;
++ u32 ctrl_reg;
++ unsigned long flags;
++ u32 fc0_src;
++};
++
++struct rp1_pll_ph_data {
++ const char *name;
++ const char *source_pll;
++ unsigned int phase;
++ unsigned int fixed_divider;
++ u32 ph_reg;
++ unsigned long flags;
++ u32 fc0_src;
++};
++
++struct rp1_pll_divider_data {
++ const char *name;
++ const char *source_pll;
++ u32 sec_reg;
++ unsigned long flags;
++ u32 fc0_src;
++};
++
++struct rp1_clock_data {
++ const char *name;
++ const char *const parents[MAX_CLK_PARENTS];
++ int num_std_parents;
++ int num_aux_parents;
++ unsigned long flags;
++ u32 clk_src_mask;
++ u32 ctrl_reg;
++ u32 div_int_reg;
++ u32 div_frac_reg;
++ u32 sel_reg;
++ u32 div_int_max;
++ u32 fc0_src;
++};
++
++struct rp1_pll_core {
++ struct clk_hw hw;
++ struct rp1_clockman *clockman;
++ const struct rp1_pll_core_data *data;
++ unsigned long cached_rate;
++};
++
++struct rp1_pll {
++ struct clk_hw hw;
++ struct clk_divider div;
++ struct rp1_clockman *clockman;
++ const struct rp1_pll_data *data;
++ unsigned long cached_rate;
++};
++
++struct rp1_pll_ph {
++ struct clk_hw hw;
++ struct rp1_clockman *clockman;
++ const struct rp1_pll_ph_data *data;
++};
++
++struct rp1_clock {
++ struct clk_hw hw;
++ struct rp1_clockman *clockman;
++ const struct rp1_clock_data *data;
++ unsigned long cached_rate;
++};
++
++static void rp1_debugfs_regset(struct rp1_clockman *clockman, u32 base,
++ const struct debugfs_reg32 *regs,
++ size_t nregs, struct dentry *dentry)
++{
++ struct debugfs_regset32 *regset;
++
++ regset = devm_kzalloc(clockman->dev, sizeof(*regset), GFP_KERNEL);
++ if (!regset)
++ return;
++
++ regset->regs = regs;
++ regset->nregs = nregs;
++ regset->base = clockman->regs + base;
++
++ debugfs_create_regset32("regdump", 0444, dentry, regset);
++}
++
++static inline u32 set_register_field(u32 reg, u32 val, u32 mask, u32 shift)
++{
++ reg &= ~mask;
++ reg |= (val << shift) & mask;
++ return reg;
++}
++
++static inline
++void clockman_write(struct rp1_clockman *clockman, u32 reg, u32 val)
++{
++ writel(val, clockman->regs + reg);
++}
++
++static inline u32 clockman_read(struct rp1_clockman *clockman, u32 reg)
++{
++ return readl(clockman->regs + reg);
++}
++
++#ifdef MEASURE_CLOCK_RATE
++static unsigned long clockman_measure_clock(struct rp1_clockman *clockman,
++ const char *clk_name,
++ unsigned int fc0_src)
++{
++ struct clk *ref_clk = __clk_lookup(fc0_ref_clk_name);
++ unsigned long result;
++ ktime_t timeout;
++ unsigned int fc_idx, fc_offset, fc_src;
++
++ fc_idx = fc0_src / 32;
++ fc_src = fc0_src % 32;
++
++ /* fc_src == 0 is invalid. */
++ if (!fc_src || fc_idx >= FC_COUNT)
++ return 0;
++
++ fc_offset = fc_idx * FC_SIZE;
++
++ /* Ensure the frequency counter is idle. */
++ timeout = ktime_add_ns(ktime_get(), FC_TIMEOUT_NS);
++ while (clockman_read(clockman, fc_offset + FC0_STATUS) & FC0_STATUS_RUNNING) {
++ if (ktime_after(ktime_get(), timeout)) {
++ dev_err(clockman->dev, "%s: FC0 busy timeout\n",
++ clk_name);
++ return 0;
++ }
++ cpu_relax();
++ }
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, fc_offset + FC0_REF_KHZ,
++ clk_get_rate(ref_clk) / KHz);
++ clockman_write(clockman, fc_offset + FC0_MIN_KHZ, 0);
++ clockman_write(clockman, fc_offset + FC0_MAX_KHZ, 0x1ffffff);
++ clockman_write(clockman, fc_offset + FC0_INTERVAL, 8);
++ clockman_write(clockman, fc_offset + FC0_DELAY, 7);
++ clockman_write(clockman, fc_offset + FC0_SRC, fc_src);
++ spin_unlock(&clockman->regs_lock);
++
++ /* Ensure the frequency counter is idle. */
++ timeout = ktime_add_ns(ktime_get(), FC_TIMEOUT_NS);
++ while (!(clockman_read(clockman, fc_offset + FC0_STATUS) & FC0_STATUS_DONE)) {
++ if (ktime_after(ktime_get(), timeout)) {
++ dev_err(clockman->dev, "%s: FC0 wait timeout\n",
++ clk_name);
++ return 0;
++ }
++ cpu_relax();
++ }
++
++ result = clockman_read(clockman, fc_offset + FC0_RESULT);
++
++ /* Disable FC0 */
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, fc_offset + FC0_SRC, 0);
++ spin_unlock(&clockman->regs_lock);
++
++ return result;
++}
++#endif
++
++static int rp1_pll_core_is_on(struct clk_hw *hw)
++{
++ struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++ struct rp1_clockman *clockman = pll_core->clockman;
++ const struct rp1_pll_core_data *data = pll_core->data;
++ u32 pwr = clockman_read(clockman, data->pwr_reg);
++
++ return (pwr & PLL_PWR_PD) || (pwr & PLL_PWR_POSTDIVPD);
++}
++
++static int rp1_pll_core_on(struct clk_hw *hw)
++{
++ struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++ struct rp1_clockman *clockman = pll_core->clockman;
++ const struct rp1_pll_core_data *data = pll_core->data;
++ u32 fbdiv_frac;
++ ktime_t timeout;
++
++ spin_lock(&clockman->regs_lock);
++
++ if (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) {
++ /* Reset to a known state. */
++ clockman_write(clockman, data->pwr_reg, PLL_PWR_MASK);
++ clockman_write(clockman, data->fbdiv_int_reg, 20);
++ clockman_write(clockman, data->fbdiv_frac_reg, 0);
++ clockman_write(clockman, data->cs_reg, 1 << PLL_CS_REFDIV_SHIFT);
++ }
++
++ /* Come out of reset. */
++ fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg);
++ clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD);
++ spin_unlock(&clockman->regs_lock);
++
++ /* Wait for the PLL to lock. */
++ timeout = ktime_add_ns(ktime_get(), LOCK_TIMEOUT_NS);
++ while (!(clockman_read(clockman, data->cs_reg) & PLL_CS_LOCK)) {
++ if (ktime_after(ktime_get(), timeout)) {
++ dev_err(clockman->dev, "%s: can't lock PLL\n",
++ clk_hw_get_name(hw));
++ return -ETIMEDOUT;
++ }
++ cpu_relax();
++ }
++
++ return 0;
++}
++
++static void rp1_pll_core_off(struct clk_hw *hw)
++{
++ struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++ struct rp1_clockman *clockman = pll_core->clockman;
++ const struct rp1_pll_core_data *data = pll_core->data;
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->pwr_reg, 0);
++ spin_unlock(&clockman->regs_lock);
++}
++
++static inline unsigned long get_pll_core_divider(struct clk_hw *hw,
++ unsigned long rate,
++ unsigned long parent_rate,
++ u32 *div_int, u32 *div_frac)
++{
++ unsigned long calc_rate;
++ u32 fbdiv_int, fbdiv_frac;
++ u64 div_fp64; /* 32.32 fixed point fraction. */
++
++ /* Factor of reference clock to VCO frequency. */
++ div_fp64 = (u64)(rate) << 32;
++ div_fp64 = DIV_U64_NEAREST(div_fp64, parent_rate);
++
++ /* Round the fractional component at 24 bits. */
++ div_fp64 += 1 << (32 - 24 - 1);
++
++ fbdiv_int = div_fp64 >> 32;
++ fbdiv_frac = (div_fp64 >> (32 - 24)) & 0xffffff;
++
++ calc_rate =
++ ((u64)parent_rate * (((u64)fbdiv_int << 24) + fbdiv_frac) + (1 << 23)) >> 24;
++
++ *div_int = fbdiv_int;
++ *div_frac = fbdiv_frac;
++
++ return calc_rate;
++}
++
++static int rp1_pll_core_set_rate(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate)
++{
++ struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++ struct rp1_clockman *clockman = pll_core->clockman;
++ const struct rp1_pll_core_data *data = pll_core->data;
++ unsigned long calc_rate;
++ u32 fbdiv_int, fbdiv_frac;
++
++ // todo: is this needed??
++ //rp1_pll_off(hw);
++
++ /* Disable dividers to start with. */
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->fbdiv_int_reg, 0);
++ clockman_write(clockman, data->fbdiv_frac_reg, 0);
++ spin_unlock(&clockman->regs_lock);
++
++ calc_rate = get_pll_core_divider(hw, rate, parent_rate,
++ &fbdiv_int, &fbdiv_frac);
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->pwr_reg, fbdiv_frac ? 0 : PLL_PWR_DSMPD);
++ clockman_write(clockman, data->fbdiv_int_reg, fbdiv_int);
++ clockman_write(clockman, data->fbdiv_frac_reg, fbdiv_frac);
++ spin_unlock(&clockman->regs_lock);
++
++ /* Check that reference frequency is no greater than VCO / 16. */
++ BUG_ON(parent_rate > (rate / 16));
++
++ pll_core->cached_rate = calc_rate;
++
++ spin_lock(&clockman->regs_lock);
++ /* Don't need to divide ref unless parent_rate > (output freq / 16) */
++ clockman_write(clockman, data->cs_reg,
++ clockman_read(clockman, data->cs_reg) |
++ (1 << PLL_CS_REFDIV_SHIFT));
++ spin_unlock(&clockman->regs_lock);
++
++ return 0;
++}
++
++static unsigned long rp1_pll_core_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++ struct rp1_clockman *clockman = pll_core->clockman;
++ const struct rp1_pll_core_data *data = pll_core->data;
++ u32 fbdiv_int, fbdiv_frac;
++ unsigned long calc_rate;
++
++ fbdiv_int = clockman_read(clockman, data->fbdiv_int_reg);
++ fbdiv_frac = clockman_read(clockman, data->fbdiv_frac_reg);
++ calc_rate =
++ ((u64)parent_rate * (((u64)fbdiv_int << 24) + fbdiv_frac) + (1 << 23)) >> 24;
++
++ return calc_rate;
++}
++
++static long rp1_pll_core_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ u32 fbdiv_int, fbdiv_frac;
++ long calc_rate;
++
++ calc_rate = get_pll_core_divider(hw, rate, *parent_rate,
++ &fbdiv_int, &fbdiv_frac);
++ return calc_rate;
++}
++
++static void rp1_pll_core_debug_init(struct clk_hw *hw, struct dentry *dentry)
++{
++ struct rp1_pll_core *pll_core = container_of(hw, struct rp1_pll_core, hw);
++ struct rp1_clockman *clockman = pll_core->clockman;
++ const struct rp1_pll_core_data *data = pll_core->data;
++ struct debugfs_reg32 *regs;
++
++ regs = devm_kcalloc(clockman->dev, 4, sizeof(*regs), GFP_KERNEL);
++ if (!regs)
++ return;
++
++ regs[0].name = "cs";
++ regs[0].offset = data->cs_reg;
++ regs[1].name = "pwr";
++ regs[1].offset = data->pwr_reg;
++ regs[2].name = "fbdiv_int";
++ regs[2].offset = data->fbdiv_int_reg;
++ regs[3].name = "fbdiv_frac";
++ regs[3].offset = data->fbdiv_frac_reg;
++
++ rp1_debugfs_regset(clockman, 0, regs, 4, dentry);
++}
++
++static void get_pll_prim_dividers(unsigned long rate, unsigned long parent_rate,
++ u32 *divider1, u32 *divider2)
++{
++ unsigned int div1, div2;
++ unsigned int best_div1 = 7, best_div2 = 7;
++ unsigned long best_rate_diff =
++ ABS_DIFF(DIV_ROUND_CLOSEST(parent_rate, best_div1 * best_div2), rate);
++ long rate_diff, calc_rate;
++
++ for (div1 = 1; div1 <= 7; div1++) {
++ for (div2 = 1; div2 <= div1; div2++) {
++ calc_rate = DIV_ROUND_CLOSEST(parent_rate, div1 * div2);
++ rate_diff = ABS_DIFF(calc_rate, rate);
++
++ if (calc_rate == rate) {
++ best_div1 = div1;
++ best_div2 = div2;
++ goto done;
++ } else if (rate_diff < best_rate_diff) {
++ best_div1 = div1;
++ best_div2 = div2;
++ best_rate_diff = rate_diff;
++ }
++ }
++ }
++
++done:
++ *divider1 = best_div1;
++ *divider2 = best_div2;
++}
++
++static int rp1_pll_set_rate(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate)
++{
++ struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw);
++ struct rp1_clockman *clockman = pll->clockman;
++ const struct rp1_pll_data *data = pll->data;
++ u32 prim, prim_div1, prim_div2;
++
++ get_pll_prim_dividers(rate, parent_rate, &prim_div1, &prim_div2);
++
++ spin_lock(&clockman->regs_lock);
++ prim = clockman_read(clockman, data->ctrl_reg);
++ prim = set_register_field(prim, prim_div1, PLL_PRIM_DIV1_MASK,
++ PLL_PRIM_DIV1_SHIFT);
++ prim = set_register_field(prim, prim_div2, PLL_PRIM_DIV2_MASK,
++ PLL_PRIM_DIV2_SHIFT);
++ clockman_write(clockman, data->ctrl_reg, prim);
++ spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static unsigned long rp1_pll_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw);
++ struct rp1_clockman *clockman = pll->clockman;
++ const struct rp1_pll_data *data = pll->data;
++ u32 prim, prim_div1, prim_div2;
++
++ prim = clockman_read(clockman, data->ctrl_reg);
++ prim_div1 = (prim & PLL_PRIM_DIV1_MASK) >> PLL_PRIM_DIV1_SHIFT;
++ prim_div2 = (prim & PLL_PRIM_DIV2_MASK) >> PLL_PRIM_DIV2_SHIFT;
++
++ if (!prim_div1 || !prim_div2) {
++ dev_err(clockman->dev, "%s: (%s) zero divider value\n",
++ __func__, data->name);
++ return 0;
++ }
++
++ return DIV_ROUND_CLOSEST(parent_rate, prim_div1 * prim_div2);
++}
++
++static long rp1_pll_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ u32 div1, div2;
++
++ get_pll_prim_dividers(rate, *parent_rate, &div1, &div2);
++
++ return DIV_ROUND_CLOSEST(*parent_rate, div1 * div2);
++}
++
++static void rp1_pll_debug_init(struct clk_hw *hw,
++ struct dentry *dentry)
++{
++ struct rp1_pll *pll = container_of(hw, struct rp1_pll, hw);
++ struct rp1_clockman *clockman = pll->clockman;
++ const struct rp1_pll_data *data = pll->data;
++ struct debugfs_reg32 *regs;
++
++ regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL);
++ if (!regs)
++ return;
++
++ regs[0].name = "prim";
++ regs[0].offset = data->ctrl_reg;
++
++ rp1_debugfs_regset(clockman, 0, regs, 1, dentry);
++}
++
++static int rp1_pll_ph_is_on(struct clk_hw *hw)
++{
++ struct rp1_pll_ph *pll = container_of(hw, struct rp1_pll_ph, hw);
++ struct rp1_clockman *clockman = pll->clockman;
++ const struct rp1_pll_ph_data *data = pll->data;
++
++ return !!(clockman_read(clockman, data->ph_reg) & PLL_PH_EN);
++}
++
++static int rp1_pll_ph_on(struct clk_hw *hw)
++{
++ struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++ struct rp1_clockman *clockman = pll_ph->clockman;
++ const struct rp1_pll_ph_data *data = pll_ph->data;
++ u32 ph_reg;
++
++ /* todo: ensure pri/sec is enabled! */
++ spin_lock(&clockman->regs_lock);
++ ph_reg = clockman_read(clockman, data->ph_reg);
++ ph_reg |= data->phase << PLL_PH_PHASE_SHIFT;
++ ph_reg |= PLL_PH_EN;
++ clockman_write(clockman, data->ph_reg, ph_reg);
++ spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static void rp1_pll_ph_off(struct clk_hw *hw)
++{
++ struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++ struct rp1_clockman *clockman = pll_ph->clockman;
++ const struct rp1_pll_ph_data *data = pll_ph->data;
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->ph_reg,
++ clockman_read(clockman, data->ph_reg) & ~PLL_PH_EN);
++ spin_unlock(&clockman->regs_lock);
++}
++
++static int rp1_pll_ph_set_rate(struct clk_hw *hw,
++ unsigned long rate, unsigned long parent_rate)
++{
++ struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++ const struct rp1_pll_ph_data *data = pll_ph->data;
++ struct rp1_clockman *clockman = pll_ph->clockman;
++
++ /* Nothing really to do here! */
++ WARN_ON(data->fixed_divider != 1 && data->fixed_divider != 2);
++ WARN_ON(rate != parent_rate / data->fixed_divider);
++
++#ifdef MEASURE_CLOCK_RATE
++ if (rp1_pll_ph_is_on(hw))
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static unsigned long rp1_pll_ph_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++ const struct rp1_pll_ph_data *data = pll_ph->data;
++
++ return parent_rate / data->fixed_divider;
++}
++
++static long rp1_pll_ph_round_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long *parent_rate)
++{
++ struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++ const struct rp1_pll_ph_data *data = pll_ph->data;
++
++ return *parent_rate / data->fixed_divider;
++}
++
++static void rp1_pll_ph_debug_init(struct clk_hw *hw,
++ struct dentry *dentry)
++{
++ struct rp1_pll_ph *pll_ph = container_of(hw, struct rp1_pll_ph, hw);
++ const struct rp1_pll_ph_data *data = pll_ph->data;
++ struct rp1_clockman *clockman = pll_ph->clockman;
++ struct debugfs_reg32 *regs;
++
++ regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL);
++ if (!regs)
++ return;
++
++ regs[0].name = "ph_reg";
++ regs[0].offset = data->ph_reg;
++
++ rp1_debugfs_regset(clockman, 0, regs, 1, dentry);
++}
++
++static int rp1_pll_divider_is_on(struct clk_hw *hw)
++{
++ struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++ struct rp1_clockman *clockman = divider->clockman;
++ const struct rp1_pll_data *data = divider->data;
++
++ return !(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_RST);
++}
++
++static int rp1_pll_divider_on(struct clk_hw *hw)
++{
++ struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++ struct rp1_clockman *clockman = divider->clockman;
++ const struct rp1_pll_data *data = divider->data;
++
++ spin_lock(&clockman->regs_lock);
++ /* Check the implementation bit is set! */
++ WARN_ON(!(clockman_read(clockman, data->ctrl_reg) & PLL_SEC_IMPL));
++ clockman_write(clockman, data->ctrl_reg,
++ clockman_read(clockman, data->ctrl_reg) & ~PLL_SEC_RST);
++ spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static void rp1_pll_divider_off(struct clk_hw *hw)
++{
++ struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++ struct rp1_clockman *clockman = divider->clockman;
++ const struct rp1_pll_data *data = divider->data;
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->ctrl_reg, PLL_SEC_RST);
++ spin_unlock(&clockman->regs_lock);
++}
++
++static int rp1_pll_divider_set_rate(struct clk_hw *hw,
++ unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++ struct rp1_clockman *clockman = divider->clockman;
++ const struct rp1_pll_data *data = divider->data;
++ u32 div, sec;
++
++ div = DIV_ROUND_UP_ULL(parent_rate, rate);
++ div = clamp(div, 8u, 19u);
++
++ spin_lock(&clockman->regs_lock);
++ sec = clockman_read(clockman, data->ctrl_reg);
++ sec = set_register_field(sec, div, PLL_SEC_DIV_MASK, PLL_SEC_DIV_SHIFT);
++
++ /* Must keep the divider in reset to change the value. */
++ sec |= PLL_SEC_RST;
++ clockman_write(clockman, data->ctrl_reg, sec);
++
++ // todo: must sleep 10 pll vco cycles
++ sec &= ~PLL_SEC_RST;
++ clockman_write(clockman, data->ctrl_reg, sec);
++ spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++ if (rp1_pll_divider_is_on(hw))
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static unsigned long rp1_pll_divider_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ return clk_divider_ops.recalc_rate(hw, parent_rate);
++}
++
++static long rp1_pll_divider_round_rate(struct clk_hw *hw,
++ unsigned long rate,
++ unsigned long *parent_rate)
++{
++ return clk_divider_ops.round_rate(hw, rate, parent_rate);
++}
++
++static void rp1_pll_divider_debug_init(struct clk_hw *hw, struct dentry *dentry)
++{
++ struct rp1_pll *divider = container_of(hw, struct rp1_pll, div.hw);
++ struct rp1_clockman *clockman = divider->clockman;
++ const struct rp1_pll_data *data = divider->data;
++ struct debugfs_reg32 *regs;
++
++ regs = devm_kcalloc(clockman->dev, 1, sizeof(*regs), GFP_KERNEL);
++ if (!regs)
++ return;
++
++ regs[0].name = "sec";
++ regs[0].offset = data->ctrl_reg;
++
++ rp1_debugfs_regset(clockman, 0, regs, 1, dentry);
++}
++
++static int rp1_clock_is_on(struct clk_hw *hw)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++
++ return !!(clockman_read(clockman, data->ctrl_reg) & CLK_CTRL_ENABLE);
++}
++
++static unsigned long rp1_clock_recalc_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++ u64 calc_rate;
++ u64 div;
++
++ u32 frac;
++
++ div = clockman_read(clockman, data->div_int_reg);
++ frac = (data->div_frac_reg != 0) ?
++ clockman_read(clockman, data->div_frac_reg) : 0;
++
++ /* If the integer portion of the divider is 0, treat it as 2^16 */
++ if (!div)
++ div = 1 << 16;
++
++ div = (div << CLK_DIV_FRAC_BITS) | (frac >> (32 - CLK_DIV_FRAC_BITS));
++
++ calc_rate = (u64)parent_rate << CLK_DIV_FRAC_BITS;
++ calc_rate = div64_u64(calc_rate, div);
++
++ return calc_rate;
++}
++
++static int rp1_clock_on(struct clk_hw *hw)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->ctrl_reg,
++ clockman_read(clockman, data->ctrl_reg) | CLK_CTRL_ENABLE);
++ spin_unlock(&clockman->regs_lock);
++
++#ifdef MEASURE_CLOCK_RATE
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static void rp1_clock_off(struct clk_hw *hw)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++
++ spin_lock(&clockman->regs_lock);
++ clockman_write(clockman, data->ctrl_reg,
++ clockman_read(clockman, data->ctrl_reg) & ~CLK_CTRL_ENABLE);
++ spin_unlock(&clockman->regs_lock);
++}
++
++static u32 rp1_clock_choose_div(unsigned long rate, unsigned long parent_rate,
++ const struct rp1_clock_data *data)
++{
++ u64 div;
++
++ /*
++ * Due to earlier rounding, calculated parent_rate may differ from
++ * expected value. Don't fail on a small discrepancy near unity divide.
++ */
++ if (!rate || rate > parent_rate + (parent_rate >> CLK_DIV_FRAC_BITS))
++ return 0;
++
++ /*
++ * Always express div in fixed-point format for fractional division;
++ * If no fractional divider is present, the fraction part will be zero.
++ */
++ if (data->div_frac_reg) {
++ div = (u64)parent_rate << CLK_DIV_FRAC_BITS;
++ div = DIV_U64_NEAREST(div, rate);
++ } else {
++ div = DIV_U64_NEAREST(parent_rate, rate);
++ div <<= CLK_DIV_FRAC_BITS;
++ }
++
++ div = clamp(div,
++ 1ull << CLK_DIV_FRAC_BITS,
++ (u64)data->div_int_max << CLK_DIV_FRAC_BITS);
++
++ return div;
++}
++
++static u8 rp1_clock_get_parent(struct clk_hw *hw)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++ u32 sel, ctrl;
++ u8 parent;
++
++ /* Sel is one-hot, so find the first bit set */
++ sel = clockman_read(clockman, data->sel_reg);
++ parent = ffs(sel) - 1;
++
++ /* sel == 0 implies the parent clock is not enabled yet. */
++ if (!sel) {
++ /* Read the clock src from the CTRL register instead */
++ ctrl = clockman_read(clockman, data->ctrl_reg);
++ parent = (ctrl & data->clk_src_mask) >> CLK_CTRL_SRC_SHIFT;
++ }
++
++ if (parent >= data->num_std_parents)
++ parent = AUX_SEL;
++
++ if (parent == AUX_SEL) {
++ /*
++ * Clock parent is an auxiliary source, so get the parent from
++ * the AUXSRC register field.
++ */
++ ctrl = clockman_read(clockman, data->ctrl_reg);
++ parent = (ctrl & CLK_CTRL_AUXSRC_MASK) >> CLK_CTRL_AUXSRC_SHIFT;
++ parent += data->num_std_parents;
++ }
++
++ return parent;
++}
++
++static int rp1_clock_set_parent(struct clk_hw *hw, u8 index)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++ u32 ctrl, sel;
++
++ spin_lock(&clockman->regs_lock);
++ ctrl = clockman_read(clockman, data->ctrl_reg);
++
++ if (index >= data->num_std_parents) {
++ /* This is an aux source request */
++ if (index >= data->num_std_parents + data->num_aux_parents)
++ return -EINVAL;
++
++ /* Select parent from aux list */
++ ctrl = set_register_field(ctrl, index - data->num_std_parents,
++ CLK_CTRL_AUXSRC_MASK,
++ CLK_CTRL_AUXSRC_SHIFT);
++ /* Set src to aux list */
++ ctrl = set_register_field(ctrl, AUX_SEL, data->clk_src_mask,
++ CLK_CTRL_SRC_SHIFT);
++ } else {
++ ctrl = set_register_field(ctrl, index, data->clk_src_mask,
++ CLK_CTRL_SRC_SHIFT);
++ }
++
++ clockman_write(clockman, data->ctrl_reg, ctrl);
++ spin_unlock(&clockman->regs_lock);
++
++ sel = rp1_clock_get_parent(hw);
++ WARN(sel != index, "(%s): Parent index req %u returned back %u\n",
++ data->name, index, sel);
++
++ return 0;
++}
++
++static int rp1_clock_set_rate_and_parent(struct clk_hw *hw,
++ unsigned long rate,
++ unsigned long parent_rate,
++ u8 parent)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++ u32 div = rp1_clock_choose_div(rate, parent_rate, data);
++
++ WARN(rate > 4000000000ll, "rate is -ve (%d)\n", (int)rate);
++
++ if (WARN(!div,
++ "clk divider calculated as 0! (%s, rate %ld, parent rate %ld)\n",
++ data->name, rate, parent_rate))
++ div = 1 << CLK_DIV_FRAC_BITS;
++
++ spin_lock(&clockman->regs_lock);
++
++ clockman_write(clockman, data->div_int_reg, div >> CLK_DIV_FRAC_BITS);
++ if (data->div_frac_reg)
++ clockman_write(clockman, data->div_frac_reg, div << (32 - CLK_DIV_FRAC_BITS));
++
++ spin_unlock(&clockman->regs_lock);
++
++ if (parent != 0xff)
++ rp1_clock_set_parent(hw, parent);
++
++#ifdef MEASURE_CLOCK_RATE
++ if (rp1_clock_is_on(hw))
++ clockman_measure_clock(clockman, data->name, data->fc0_src);
++#endif
++ return 0;
++}
++
++static int rp1_clock_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ return rp1_clock_set_rate_and_parent(hw, rate, parent_rate, 0xff);
++}
++
++static void rp1_clock_choose_div_and_prate(struct clk_hw *hw,
++ int parent_idx,
++ unsigned long rate,
++ unsigned long *prate,
++ unsigned long *calc_rate)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ const struct rp1_clock_data *data = clock->data;
++ struct clk_hw *parent;
++ u32 div;
++ u64 tmp;
++
++ parent = clk_hw_get_parent_by_index(hw, parent_idx);
++ *prate = clk_hw_get_rate(parent);
++ div = rp1_clock_choose_div(rate, *prate, data);
++
++ if (!div) {
++ *calc_rate = 0;
++ return;
++ }
++
++ /* Recalculate to account for rounding errors */
++ tmp = (u64)*prate << CLK_DIV_FRAC_BITS;
++ tmp = div_u64(tmp, div);
++ *calc_rate = tmp;
++}
++
++static int rp1_clock_determine_rate(struct clk_hw *hw,
++ struct clk_rate_request *req)
++{
++ struct clk_hw *parent, *best_parent = NULL;
++ unsigned long best_rate = 0;
++ unsigned long best_prate = 0;
++ unsigned long best_rate_diff = ULONG_MAX;
++ unsigned long prate, calc_rate;
++ size_t i;
++
++ /*
++ * If the NO_REPARENT flag is set, try to use existing parent.
++ */
++ if ((clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT)) {
++ i = rp1_clock_get_parent(hw);
++ parent = clk_hw_get_parent_by_index(hw, i);
++ if (parent) {
++ rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate,
++ &calc_rate);
++ if (calc_rate > 0) {
++ req->best_parent_hw = parent;
++ req->best_parent_rate = prate;
++ req->rate = calc_rate;
++ return 0;
++ }
++ }
++ }
++
++ /*
++ * Select parent clock that results in the closest rate (lower or
++ * higher)
++ */
++ for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
++ parent = clk_hw_get_parent_by_index(hw, i);
++ if (!parent)
++ continue;
++
++ rp1_clock_choose_div_and_prate(hw, i, req->rate, &prate,
++ &calc_rate);
++
++ if (ABS_DIFF(calc_rate, req->rate) < best_rate_diff) {
++ best_parent = parent;
++ best_prate = prate;
++ best_rate = calc_rate;
++ best_rate_diff = ABS_DIFF(calc_rate, req->rate);
++
++ if (best_rate_diff == 0)
++ break;
++ }
++ }
++
++ if (best_rate == 0)
++ return -EINVAL;
++
++ req->best_parent_hw = best_parent;
++ req->best_parent_rate = best_prate;
++ req->rate = best_rate;
++
++ return 0;
++}
++
++static void rp1_clk_debug_init(struct clk_hw *hw, struct dentry *dentry)
++{
++ struct rp1_clock *clock = container_of(hw, struct rp1_clock, hw);
++ struct rp1_clockman *clockman = clock->clockman;
++ const struct rp1_clock_data *data = clock->data;
++ struct debugfs_reg32 *regs;
++ int i;
++
++ regs = devm_kcalloc(clockman->dev, 4, sizeof(*regs), GFP_KERNEL);
++ if (!regs)
++ return;
++
++ i = 0;
++ regs[i].name = "ctrl";
++ regs[i++].offset = data->ctrl_reg;
++ regs[i].name = "div_int";
++ regs[i++].offset = data->div_int_reg;
++ regs[i].name = "div_frac";
++ regs[i++].offset = data->div_frac_reg;
++ regs[i].name = "sel";
++ regs[i++].offset = data->sel_reg;
++
++ rp1_debugfs_regset(clockman, 0, regs, i, dentry);
++}
++
++static const struct clk_ops rp1_pll_core_ops = {
++ .is_prepared = rp1_pll_core_is_on,
++ .prepare = rp1_pll_core_on,
++ .unprepare = rp1_pll_core_off,
++ .set_rate = rp1_pll_core_set_rate,
++ .recalc_rate = rp1_pll_core_recalc_rate,
++ .round_rate = rp1_pll_core_round_rate,
++ .debug_init = rp1_pll_core_debug_init,
++};
++
++static const struct clk_ops rp1_pll_ops = {
++ .set_rate = rp1_pll_set_rate,
++ .recalc_rate = rp1_pll_recalc_rate,
++ .round_rate = rp1_pll_round_rate,
++ .debug_init = rp1_pll_debug_init,
++};
++
++static const struct clk_ops rp1_pll_ph_ops = {
++ .is_prepared = rp1_pll_ph_is_on,
++ .prepare = rp1_pll_ph_on,
++ .unprepare = rp1_pll_ph_off,
++ .set_rate = rp1_pll_ph_set_rate,
++ .recalc_rate = rp1_pll_ph_recalc_rate,
++ .round_rate = rp1_pll_ph_round_rate,
++ .debug_init = rp1_pll_ph_debug_init,
++};
++
++static const struct clk_ops rp1_pll_divider_ops = {
++ .is_prepared = rp1_pll_divider_is_on,
++ .prepare = rp1_pll_divider_on,
++ .unprepare = rp1_pll_divider_off,
++ .set_rate = rp1_pll_divider_set_rate,
++ .recalc_rate = rp1_pll_divider_recalc_rate,
++ .round_rate = rp1_pll_divider_round_rate,
++ .debug_init = rp1_pll_divider_debug_init,
++};
++
++static const struct clk_ops rp1_clk_ops = {
++ .is_prepared = rp1_clock_is_on,
++ .prepare = rp1_clock_on,
++ .unprepare = rp1_clock_off,
++ .recalc_rate = rp1_clock_recalc_rate,
++ .get_parent = rp1_clock_get_parent,
++ .set_parent = rp1_clock_set_parent,
++ .set_rate_and_parent = rp1_clock_set_rate_and_parent,
++ .set_rate = rp1_clock_set_rate,
++ .determine_rate = rp1_clock_determine_rate,
++ .debug_init = rp1_clk_debug_init,
++};
++
++static bool rp1_clk_is_claimed(const char *name);
++
++static struct clk_hw *rp1_register_pll_core(struct rp1_clockman *clockman,
++ const void *data)
++{
++ const struct rp1_pll_core_data *pll_core_data = data;
++ struct rp1_pll_core *pll_core;
++ struct clk_init_data init;
++ int ret;
++
++ memset(&init, 0, sizeof(init));
++
++ /* All of the PLL cores derive from the external oscillator. */
++ init.parent_names = &ref_clock;
++ init.num_parents = 1;
++ init.name = pll_core_data->name;
++ init.ops = &rp1_pll_core_ops;
++ init.flags = pll_core_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL;
++
++ pll_core = kzalloc(sizeof(*pll_core), GFP_KERNEL);
++ if (!pll_core)
++ return NULL;
++
++ pll_core->clockman = clockman;
++ pll_core->data = pll_core_data;
++ pll_core->hw.init = &init;
++
++ ret = devm_clk_hw_register(clockman->dev, &pll_core->hw);
++ if (ret) {
++ kfree(pll_core);
++ return NULL;
++ }
++
++ return &pll_core->hw;
++}
++
++static struct clk_hw *rp1_register_pll(struct rp1_clockman *clockman,
++ const void *data)
++{
++ const struct rp1_pll_data *pll_data = data;
++ struct rp1_pll *pll;
++ struct clk_init_data init;
++ int ret;
++
++ memset(&init, 0, sizeof(init));
++
++ init.parent_names = &pll_data->source_pll;
++ init.num_parents = 1;
++ init.name = pll_data->name;
++ init.ops = &rp1_pll_ops;
++ init.flags = pll_data->flags | CLK_IGNORE_UNUSED | CLK_IS_CRITICAL;
++
++ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
++ if (!pll)
++ return NULL;
++
++ pll->clockman = clockman;
++ pll->data = pll_data;
++ pll->hw.init = &init;
++
++ ret = devm_clk_hw_register(clockman->dev, &pll->hw);
++ if (ret) {
++ kfree(pll);
++ return NULL;
++ }
++
++ return &pll->hw;
++}
++
++static struct clk_hw *rp1_register_pll_ph(struct rp1_clockman *clockman,
++ const void *data)
++{
++ const struct rp1_pll_ph_data *ph_data = data;
++ struct rp1_pll_ph *ph;
++ struct clk_init_data init;
++ int ret;
++
++ memset(&init, 0, sizeof(init));
++
++ /* All of the PLLs derive from the external oscillator. */
++ init.parent_names = &ph_data->source_pll;
++ init.num_parents = 1;
++ init.name = ph_data->name;
++ init.ops = &rp1_pll_ph_ops;
++ init.flags = ph_data->flags | CLK_IGNORE_UNUSED;
++
++ ph = kzalloc(sizeof(*ph), GFP_KERNEL);
++ if (!ph)
++ return NULL;
++
++ ph->clockman = clockman;
++ ph->data = ph_data;
++ ph->hw.init = &init;
++
++ ret = devm_clk_hw_register(clockman->dev, &ph->hw);
++ if (ret) {
++ kfree(ph);
++ return NULL;
++ }
++
++ return &ph->hw;
++}
++
++static struct clk_hw *rp1_register_pll_divider(struct rp1_clockman *clockman,
++ const void *data)
++{
++ const struct rp1_pll_data *divider_data = data;
++ struct rp1_pll *divider;
++ struct clk_init_data init;
++ int ret;
++
++ memset(&init, 0, sizeof(init));
++
++ init.parent_names = ÷r_data->source_pll;
++ init.num_parents = 1;
++ init.name = divider_data->name;
++ init.ops = &rp1_pll_divider_ops;
++ init.flags = divider_data->flags | CLK_IGNORE_UNUSED;
++
++ divider = devm_kzalloc(clockman->dev, sizeof(*divider), GFP_KERNEL);
++ if (!divider)
++ return NULL;
++
++ divider->div.reg = clockman->regs + divider_data->ctrl_reg;
++ divider->div.shift = PLL_SEC_DIV_SHIFT;
++ divider->div.width = PLL_SEC_DIV_WIDTH;
++ divider->div.flags = CLK_DIVIDER_ROUND_CLOSEST;
++ divider->div.lock = &clockman->regs_lock;
++ divider->div.hw.init = &init;
++ divider->div.table = pll_sec_div_table;
++
++ if (!rp1_clk_is_claimed(divider_data->source_pll))
++ init.flags |= CLK_IS_CRITICAL;
++ if (!rp1_clk_is_claimed(divider_data->name))
++ divider->div.flags |= CLK_IS_CRITICAL;
++
++ divider->clockman = clockman;
++ divider->data = divider_data;
++
++ ret = devm_clk_hw_register(clockman->dev, ÷r->div.hw);
++ if (ret)
++ return ERR_PTR(ret);
++
++ return ÷r->div.hw;
++}
++
++static struct clk_hw *rp1_register_clock(struct rp1_clockman *clockman,
++ const void *data)
++{
++ const struct rp1_clock_data *clock_data = data;
++ struct rp1_clock *clock;
++ struct clk_init_data init;
++ int ret;
++
++ BUG_ON(MAX_CLK_PARENTS <
++ clock_data->num_std_parents + clock_data->num_aux_parents);
++ /* There must be a gap for the AUX selector */
++ BUG_ON((clock_data->num_std_parents > AUX_SEL) &&
++ strcmp("-", clock_data->parents[AUX_SEL]));
++
++ memset(&init, 0, sizeof(init));
++ init.parent_names = clock_data->parents;
++ init.num_parents =
++ clock_data->num_std_parents + clock_data->num_aux_parents;
++ init.name = clock_data->name;
++ init.flags = clock_data->flags | CLK_IGNORE_UNUSED;
++ init.ops = &rp1_clk_ops;
++
++ clock = devm_kzalloc(clockman->dev, sizeof(*clock), GFP_KERNEL);
++ if (!clock)
++ return NULL;
++
++ clock->clockman = clockman;
++ clock->data = clock_data;
++ clock->hw.init = &init;
++
++ ret = devm_clk_hw_register(clockman->dev, &clock->hw);
++ if (ret)
++ return ERR_PTR(ret);
++
++ return &clock->hw;
++}
++
++struct rp1_clk_desc {
++ struct clk_hw *(*clk_register)(struct rp1_clockman *clockman,
++ const void *data);
++ const void *data;
++};
++
++/* Assignment helper macros for different clock types. */
++#define _REGISTER(f, ...) { .clk_register = f, .data = __VA_ARGS__ }
++
++#define REGISTER_PLL_CORE(...) _REGISTER(&rp1_register_pll_core, \
++ &(struct rp1_pll_core_data) \
++ {__VA_ARGS__})
++
++#define REGISTER_PLL(...) _REGISTER(&rp1_register_pll, \
++ &(struct rp1_pll_data) \
++ {__VA_ARGS__})
++
++#define REGISTER_PLL_PH(...) _REGISTER(&rp1_register_pll_ph, \
++ &(struct rp1_pll_ph_data) \
++ {__VA_ARGS__})
++
++#define REGISTER_PLL_DIV(...) _REGISTER(&rp1_register_pll_divider, \
++ &(struct rp1_pll_data) \
++ {__VA_ARGS__})
++
++#define REGISTER_CLK(...) _REGISTER(&rp1_register_clock, \
++ &(struct rp1_clock_data) \
++ {__VA_ARGS__})
++
++static const struct rp1_clk_desc clk_desc_array[] = {
++ [RP1_PLL_SYS_CORE] = REGISTER_PLL_CORE(
++ .name = "pll_sys_core",
++ .cs_reg = PLL_SYS_CS,
++ .pwr_reg = PLL_SYS_PWR,
++ .fbdiv_int_reg = PLL_SYS_FBDIV_INT,
++ .fbdiv_frac_reg = PLL_SYS_FBDIV_FRAC,
++ ),
++
++ [RP1_PLL_AUDIO_CORE] = REGISTER_PLL_CORE(
++ .name = "pll_audio_core",
++ .cs_reg = PLL_AUDIO_CS,
++ .pwr_reg = PLL_AUDIO_PWR,
++ .fbdiv_int_reg = PLL_AUDIO_FBDIV_INT,
++ .fbdiv_frac_reg = PLL_AUDIO_FBDIV_FRAC,
++ ),
++
++ [RP1_PLL_VIDEO_CORE] = REGISTER_PLL_CORE(
++ .name = "pll_video_core",
++ .cs_reg = PLL_VIDEO_CS,
++ .pwr_reg = PLL_VIDEO_PWR,
++ .fbdiv_int_reg = PLL_VIDEO_FBDIV_INT,
++ .fbdiv_frac_reg = PLL_VIDEO_FBDIV_FRAC,
++ ),
++
++ [RP1_PLL_SYS] = REGISTER_PLL(
++ .name = "pll_sys",
++ .source_pll = "pll_sys_core",
++ .ctrl_reg = PLL_SYS_PRIM,
++ .fc0_src = FC_NUM(0, 2),
++ ),
++
++ [RP1_PLL_AUDIO] = REGISTER_PLL(
++ .name = "pll_audio",
++ .source_pll = "pll_audio_core",
++ .ctrl_reg = PLL_AUDIO_PRIM,
++ .fc0_src = FC_NUM(4, 2),
++ ),
++
++ [RP1_PLL_VIDEO] = REGISTER_PLL(
++ .name = "pll_video",
++ .source_pll = "pll_video_core",
++ .ctrl_reg = PLL_VIDEO_PRIM,
++ .fc0_src = FC_NUM(3, 2),
++ ),
++
++ [RP1_PLL_SYS_PRI_PH] = REGISTER_PLL_PH(
++ .name = "pll_sys_pri_ph",
++ .source_pll = "pll_sys",
++ .ph_reg = PLL_SYS_PRIM,
++ .fixed_divider = 2,
++ .phase = RP1_PLL_PHASE_0,
++ .fc0_src = FC_NUM(1, 2),
++ ),
++
++ [RP1_PLL_AUDIO_PRI_PH] = REGISTER_PLL_PH(
++ .name = "pll_audio_pri_ph",
++ .source_pll = "pll_audio",
++ .ph_reg = PLL_AUDIO_PRIM,
++ .fixed_divider = 2,
++ .phase = RP1_PLL_PHASE_0,
++ .fc0_src = FC_NUM(5, 1),
++ ),
++
++ [RP1_PLL_SYS_SEC] = REGISTER_PLL_DIV(
++ .name = "pll_sys_sec",
++ .source_pll = "pll_sys_core",
++ .ctrl_reg = PLL_SYS_SEC,
++ .fc0_src = FC_NUM(2, 2),
++ ),
++
++ [RP1_PLL_AUDIO_SEC] = REGISTER_PLL_DIV(
++ .name = "pll_audio_sec",
++ .source_pll = "pll_audio_core",
++ .ctrl_reg = PLL_AUDIO_SEC,
++ .fc0_src = FC_NUM(6, 2),
++ ),
++
++ [RP1_PLL_VIDEO_SEC] = REGISTER_PLL_DIV(
++ .name = "pll_video_sec",
++ .source_pll = "pll_video_core",
++ .ctrl_reg = PLL_VIDEO_SEC,
++ .fc0_src = FC_NUM(5, 3),
++ ),
++
++ [RP1_CLK_SYS] = REGISTER_CLK(
++ .name = "clk_sys",
++ .parents = {"xosc", "-", "pll_sys"},
++ .num_std_parents = 3,
++ .num_aux_parents = 0,
++ .ctrl_reg = CLK_SYS_CTRL,
++ .div_int_reg = CLK_SYS_DIV_INT,
++ .sel_reg = CLK_SYS_SEL,
++ .div_int_max = DIV_INT_24BIT_MAX,
++ .fc0_src = FC_NUM(0, 4),
++ .clk_src_mask = 0x3,
++ ),
++
++ [RP1_CLK_SLOW_SYS] = REGISTER_CLK(
++ .name = "clk_slow_sys",
++ .parents = {"xosc"},
++ .num_std_parents = 1,
++ .num_aux_parents = 0,
++ .ctrl_reg = CLK_SLOW_SYS_CTRL,
++ .div_int_reg = CLK_SLOW_SYS_DIV_INT,
++ .sel_reg = CLK_SLOW_SYS_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(1, 4),
++ .clk_src_mask = 0x1,
++ ),
++
++ [RP1_CLK_UART] = REGISTER_CLK(
++ .name = "clk_uart",
++ .parents = {"pll_sys_pri_ph",
++ "pll_video",
++ "xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 3,
++ .ctrl_reg = CLK_UART_CTRL,
++ .div_int_reg = CLK_UART_DIV_INT,
++ .sel_reg = CLK_UART_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(6, 7),
++ ),
++
++ [RP1_CLK_ETH] = REGISTER_CLK(
++ .name = "clk_eth",
++ .parents = {"-"},
++ .num_std_parents = 1,
++ .num_aux_parents = 0,
++ .ctrl_reg = CLK_ETH_CTRL,
++ .div_int_reg = CLK_ETH_DIV_INT,
++ .sel_reg = CLK_ETH_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(4, 6),
++ ),
++
++ [RP1_CLK_PWM0] = REGISTER_CLK(
++ .name = "clk_pwm0",
++ .parents = {"pll_audio_pri_ph",
++ "pll_video_sec",
++ "xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 3,
++ .ctrl_reg = CLK_PWM0_CTRL,
++ .div_int_reg = CLK_PWM0_DIV_INT,
++ .div_frac_reg = CLK_PWM0_DIV_FRAC,
++ .sel_reg = CLK_PWM0_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(0, 5),
++ ),
++
++ [RP1_CLK_PWM1] = REGISTER_CLK(
++ .name = "clk_pwm1",
++ .parents = {"pll_audio_pri_ph",
++ "pll_video_sec",
++ "xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 3,
++ .ctrl_reg = CLK_PWM1_CTRL,
++ .div_int_reg = CLK_PWM1_DIV_INT,
++ .div_frac_reg = CLK_PWM1_DIV_FRAC,
++ .sel_reg = CLK_PWM1_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(1, 5),
++ ),
++
++ [RP1_CLK_AUDIO_IN] = REGISTER_CLK(
++ .name = "clk_audio_in",
++ .parents = {"-"},
++ .num_std_parents = 1,
++ .num_aux_parents = 0,
++ .ctrl_reg = CLK_AUDIO_IN_CTRL,
++ .div_int_reg = CLK_AUDIO_IN_DIV_INT,
++ .sel_reg = CLK_AUDIO_IN_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(2, 5),
++ ),
++
++ [RP1_CLK_AUDIO_OUT] = REGISTER_CLK(
++ .name = "clk_audio_out",
++ .parents = {"-"},
++ .num_std_parents = 1,
++ .num_aux_parents = 0,
++ .ctrl_reg = CLK_AUDIO_OUT_CTRL,
++ .div_int_reg = CLK_AUDIO_OUT_DIV_INT,
++ .sel_reg = CLK_AUDIO_OUT_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(3, 5),
++ ),
++
++ [RP1_CLK_I2S] = REGISTER_CLK(
++ .name = "clk_i2s",
++ .parents = {"xosc",
++ "pll_audio",
++ "pll_audio_sec"},
++ .num_std_parents = 0,
++ .num_aux_parents = 3,
++ .ctrl_reg = CLK_I2S_CTRL,
++ .div_int_reg = CLK_I2S_DIV_INT,
++ .sel_reg = CLK_I2S_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(4, 4),
++ ),
++
++ [RP1_CLK_MIPI0_CFG] = REGISTER_CLK(
++ .name = "clk_mipi0_cfg",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_MIPI0_CFG_CTRL,
++ .div_int_reg = CLK_MIPI0_CFG_DIV_INT,
++ .sel_reg = CLK_MIPI0_CFG_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(4, 5),
++ ),
++
++ [RP1_CLK_MIPI1_CFG] = REGISTER_CLK(
++ .name = "clk_mipi1_cfg",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_MIPI1_CFG_CTRL,
++ .div_int_reg = CLK_MIPI1_CFG_DIV_INT,
++ .sel_reg = CLK_MIPI1_CFG_SEL,
++ .clk_src_mask = 1,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(5, 6),
++ ),
++
++ [RP1_CLK_ETH_TSU] = REGISTER_CLK(
++ .name = "clk_eth_tsu",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_ETH_TSU_CTRL,
++ .div_int_reg = CLK_ETH_TSU_DIV_INT,
++ .sel_reg = CLK_ETH_TSU_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(5, 7),
++ ),
++
++ [RP1_CLK_ADC] = REGISTER_CLK(
++ .name = "clk_adc",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_ADC_CTRL,
++ .div_int_reg = CLK_ADC_DIV_INT,
++ .sel_reg = CLK_ADC_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(5, 5),
++ ),
++
++ [RP1_CLK_SDIO_TIMER] = REGISTER_CLK(
++ .name = "clk_sdio_timer",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_SDIO_TIMER_CTRL,
++ .div_int_reg = CLK_SDIO_TIMER_DIV_INT,
++ .sel_reg = CLK_SDIO_TIMER_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(3, 4),
++ ),
++
++ [RP1_CLK_SDIO_ALT_SRC] = REGISTER_CLK(
++ .name = "clk_sdio_alt_src",
++ .parents = {"pll_sys"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_SDIO_ALT_SRC_CTRL,
++ .div_int_reg = CLK_SDIO_ALT_SRC_DIV_INT,
++ .sel_reg = CLK_SDIO_ALT_SRC_SEL,
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(5, 4),
++ ),
++
++ [RP1_CLK_GP0] = REGISTER_CLK(
++ .name = "clk_gp0",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_GP0_CTRL,
++ .div_int_reg = CLK_GP0_DIV_INT,
++ .div_frac_reg = CLK_GP0_DIV_FRAC,
++ .sel_reg = CLK_GP0_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(0, 1),
++ ),
++
++ [RP1_CLK_GP1] = REGISTER_CLK(
++ .name = "clk_gp1",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_GP1_CTRL,
++ .div_int_reg = CLK_GP1_DIV_INT,
++ .div_frac_reg = CLK_GP1_DIV_FRAC,
++ .sel_reg = CLK_GP1_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(1, 1),
++ ),
++
++ [RP1_CLK_GP2] = REGISTER_CLK(
++ .name = "clk_gp2",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_GP2_CTRL,
++ .div_int_reg = CLK_GP2_DIV_INT,
++ .div_frac_reg = CLK_GP2_DIV_FRAC,
++ .sel_reg = CLK_GP2_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(2, 1),
++ ),
++
++ [RP1_CLK_GP3] = REGISTER_CLK(
++ .name = "clk_gp3",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_GP3_CTRL,
++ .div_int_reg = CLK_GP3_DIV_INT,
++ .div_frac_reg = CLK_GP3_DIV_FRAC,
++ .sel_reg = CLK_GP3_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(3, 1),
++ ),
++
++ [RP1_CLK_GP4] = REGISTER_CLK(
++ .name = "clk_gp4",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_GP4_CTRL,
++ .div_int_reg = CLK_GP4_DIV_INT,
++ .div_frac_reg = CLK_GP4_DIV_FRAC,
++ .sel_reg = CLK_GP4_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(4, 1),
++ ),
++
++ [RP1_CLK_GP5] = REGISTER_CLK(
++ .name = "clk_gp5",
++ .parents = {"xosc"},
++ .num_std_parents = 0,
++ .num_aux_parents = 1,
++ .ctrl_reg = CLK_GP5_CTRL,
++ .div_int_reg = CLK_GP5_DIV_INT,
++ .div_frac_reg = CLK_GP5_DIV_FRAC,
++ .sel_reg = CLK_GP5_SEL,
++ .div_int_max = DIV_INT_16BIT_MAX,
++ .fc0_src = FC_NUM(5, 1),
++ ),
++
++ [RP1_CLK_VEC] = REGISTER_CLK(
++ .name = "clk_vec",
++ .parents = {"pll_sys_pri_ph",
++ "pll_video_sec",
++ "pll_video",
++ "clk_gp0",
++ "clk_gp1",
++ "clk_gp2",
++ "clk_gp3",
++ "clk_gp4"},
++ .num_std_parents = 0,
++ .num_aux_parents = 8, /* XXX in fact there are more than 8 */
++ .ctrl_reg = VIDEO_CLK_VEC_CTRL,
++ .div_int_reg = VIDEO_CLK_VEC_DIV_INT,
++ .sel_reg = VIDEO_CLK_VEC_SEL,
++ .flags = CLK_SET_RATE_NO_REPARENT, /* Let VEC driver set parent */
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(0, 6),
++ ),
++
++ [RP1_CLK_DPI] = REGISTER_CLK(
++ .name = "clk_dpi",
++ .parents = {"pll_sys",
++ "pll_video_sec",
++ "pll_video",
++ "clk_gp0",
++ "clk_gp1",
++ "clk_gp2",
++ "clk_gp3",
++ "clk_gp4"},
++ .num_std_parents = 0,
++ .num_aux_parents = 8, /* XXX in fact there are more than 8 */
++ .ctrl_reg = VIDEO_CLK_DPI_CTRL,
++ .div_int_reg = VIDEO_CLK_DPI_DIV_INT,
++ .sel_reg = VIDEO_CLK_DPI_SEL,
++ .flags = CLK_SET_RATE_NO_REPARENT, /* Let DPI driver set parent */
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(1, 6),
++ ),
++
++ [RP1_CLK_MIPI0_DPI] = REGISTER_CLK(
++ .name = "clk_mipi0_dpi",
++ .parents = {"pll_sys",
++ "pll_video_sec",
++ "pll_video",
++ "clksrc_mipi0_dsi_byteclk",
++ "clk_gp0",
++ "clk_gp1",
++ "clk_gp2",
++ "clk_gp3"},
++ .num_std_parents = 0,
++ .num_aux_parents = 8, /* XXX in fact there are more than 8 */
++ .ctrl_reg = VIDEO_CLK_MIPI0_DPI_CTRL,
++ .div_int_reg = VIDEO_CLK_MIPI0_DPI_DIV_INT,
++ .div_frac_reg = VIDEO_CLK_MIPI0_DPI_DIV_FRAC,
++ .sel_reg = VIDEO_CLK_MIPI0_DPI_SEL,
++ .flags = CLK_SET_RATE_NO_REPARENT, /* Let DSI driver set parent */
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(2, 6),
++ ),
++
++ [RP1_CLK_MIPI1_DPI] = REGISTER_CLK(
++ .name = "clk_mipi1_dpi",
++ .parents = {"pll_sys",
++ "pll_video_sec",
++ "pll_video",
++ "clksrc_mipi1_dsi_byteclk",
++ "clk_gp0",
++ "clk_gp1",
++ "clk_gp2",
++ "clk_gp3"},
++ .num_std_parents = 0,
++ .num_aux_parents = 8, /* XXX in fact there are more than 8 */
++ .ctrl_reg = VIDEO_CLK_MIPI1_DPI_CTRL,
++ .div_int_reg = VIDEO_CLK_MIPI1_DPI_DIV_INT,
++ .div_frac_reg = VIDEO_CLK_MIPI1_DPI_DIV_FRAC,
++ .sel_reg = VIDEO_CLK_MIPI1_DPI_SEL,
++ .flags = CLK_SET_RATE_NO_REPARENT, /* Let DSI driver set parent */
++ .div_int_max = DIV_INT_8BIT_MAX,
++ .fc0_src = FC_NUM(3, 6),
++ ),
++};
++
++static bool rp1_clk_claimed[ARRAY_SIZE(clk_desc_array)];
++
++static bool rp1_clk_is_claimed(const char *name)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(clk_desc_array); i++) {
++ if (clk_desc_array[i].data) {
++ const char *clk_name = *(const char **)(clk_desc_array[i].data);
++
++ if (!strcmp(name, clk_name))
++ return rp1_clk_claimed[i];
++ }
++ }
++
++ return false;
++}
++
++static int rp1_clk_probe(struct platform_device *pdev)
++{
++ const struct rp1_clk_desc *desc;
++ struct device *dev = &pdev->dev;
++ struct rp1_clockman *clockman;
++ struct resource *res;
++ struct clk_hw **hws;
++ const size_t asize = ARRAY_SIZE(clk_desc_array);
++ u32 chip_id, platform;
++ unsigned int i;
++ u32 clk_id;
++ int ret;
++
++ clockman = devm_kzalloc(dev, struct_size(clockman, onecell.hws, asize),
++ GFP_KERNEL);
++ if (!clockman)
++ return -ENOMEM;
++
++ rp1_get_platform(&chip_id, &platform);
++
++ spin_lock_init(&clockman->regs_lock);
++ clockman->dev = dev;
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ clockman->regs = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(clockman->regs))
++ return PTR_ERR(clockman->regs);
++
++ memset(rp1_clk_claimed, 0, sizeof(rp1_clk_claimed));
++ for (i = 0;
++ !of_property_read_u32_index(pdev->dev.of_node, "claim-clocks",
++ i, &clk_id);
++ i++)
++ rp1_clk_claimed[clk_id] = true;
++
++ platform_set_drvdata(pdev, clockman);
++
++ clockman->onecell.num = asize;
++ hws = clockman->onecell.hws;
++
++ for (i = 0; i < asize; i++) {
++ desc = &clk_desc_array[i];
++ if (desc->clk_register && desc->data)
++ hws[i] = desc->clk_register(clockman, desc->data);
++ }
++
++ ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
++ &clockman->onecell);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static const struct of_device_id rp1_clk_of_match[] = {
++ { .compatible = "raspberrypi,rp1-clocks" },
++ {}
++};
++MODULE_DEVICE_TABLE(of, rp1_clk_of_match);
++
++static struct platform_driver rp1_clk_driver = {
++ .driver = {
++ .name = "rp1-clk",
++ .of_match_table = rp1_clk_of_match,
++ },
++ .probe = rp1_clk_probe,
++};
++
++static int __init __rp1_clk_driver_init(void)
++{
++ return platform_driver_register(&rp1_clk_driver);
++}
++postcore_initcall(__rp1_clk_driver_init);
++
++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
++MODULE_DESCRIPTION("RP1 clock driver");
++MODULE_LICENSE("GPL");
+--- a/include/dt-bindings/clock/rp1.h
++++ b/include/dt-bindings/clock/rp1.h
+@@ -13,39 +13,40 @@
+
+ #define RP1_PLL_SYS_PRI_PH 6
+ #define RP1_PLL_SYS_SEC_PH 7
++#define RP1_PLL_AUDIO_PRI_PH 8
+
+-#define RP1_PLL_SYS_SEC 8
+-#define RP1_PLL_AUDIO_SEC 9
+-#define RP1_PLL_VIDEO_SEC 10
++#define RP1_PLL_SYS_SEC 9
++#define RP1_PLL_AUDIO_SEC 10
++#define RP1_PLL_VIDEO_SEC 11
+
+-#define RP1_CLK_SYS 11
+-#define RP1_CLK_SLOW_SYS 12
+-#define RP1_CLK_DMA 13
+-#define RP1_CLK_UART 14
+-#define RP1_CLK_ETH 15
+-#define RP1_CLK_PWM0 16
+-#define RP1_CLK_PWM1 17
+-#define RP1_CLK_AUDIO_IN 18
+-#define RP1_CLK_AUDIO_OUT 19
+-#define RP1_CLK_I2S 20
+-#define RP1_CLK_MIPI0_CFG 21
+-#define RP1_CLK_MIPI1_CFG 22
+-#define RP1_CLK_PCIE_AUX 23
+-#define RP1_CLK_USBH0_MICROFRAME 24
+-#define RP1_CLK_USBH1_MICROFRAME 25
+-#define RP1_CLK_USBH0_SUSPEND 26
+-#define RP1_CLK_USBH1_SUSPEND 27
+-#define RP1_CLK_ETH_TSU 28
+-#define RP1_CLK_ADC 29
+-#define RP1_CLK_SDIO_TIMER 30
+-#define RP1_CLK_SDIO_ALT_SRC 31
+-#define RP1_CLK_GP0 32
+-#define RP1_CLK_GP1 33
+-#define RP1_CLK_GP2 34
+-#define RP1_CLK_GP3 35
+-#define RP1_CLK_GP4 36
+-#define RP1_CLK_GP5 37
+-#define RP1_CLK_VEC 38
+-#define RP1_CLK_DPI 39
+-#define RP1_CLK_MIPI0_DPI 40
+-#define RP1_CLK_MIPI1_DPI 41
++#define RP1_CLK_SYS 12
++#define RP1_CLK_SLOW_SYS 13
++#define RP1_CLK_DMA 14
++#define RP1_CLK_UART 15
++#define RP1_CLK_ETH 16
++#define RP1_CLK_PWM0 17
++#define RP1_CLK_PWM1 18
++#define RP1_CLK_AUDIO_IN 19
++#define RP1_CLK_AUDIO_OUT 20
++#define RP1_CLK_I2S 21
++#define RP1_CLK_MIPI0_CFG 22
++#define RP1_CLK_MIPI1_CFG 23
++#define RP1_CLK_PCIE_AUX 24
++#define RP1_CLK_USBH0_MICROFRAME 25
++#define RP1_CLK_USBH1_MICROFRAME 26
++#define RP1_CLK_USBH0_SUSPEND 27
++#define RP1_CLK_USBH1_SUSPEND 28
++#define RP1_CLK_ETH_TSU 29
++#define RP1_CLK_ADC 30
++#define RP1_CLK_SDIO_TIMER 31
++#define RP1_CLK_SDIO_ALT_SRC 32
++#define RP1_CLK_GP0 33
++#define RP1_CLK_GP1 34
++#define RP1_CLK_GP2 35
++#define RP1_CLK_GP3 36
++#define RP1_CLK_GP4 37
++#define RP1_CLK_GP5 38
++#define RP1_CLK_VEC 39
++#define RP1_CLK_DPI 40
++#define RP1_CLK_MIPI0_DPI 41
++#define RP1_CLK_MIPI1_DPI 42
--- /dev/null
+From 19b934ce3763c9465c5c80302f7c142d30b75869 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 28 Oct 2022 14:13:30 +0100
+Subject: [PATCH] dt-bindings: pinctrl: Add bindings for Raspberry Pi RP1
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ include/dt-bindings/pinctrl/rp1.h | 46 +++++++++++++++++++++++++++++++
+ 1 file changed, 46 insertions(+)
+ create mode 100644 include/dt-bindings/pinctrl/rp1.h
+
+--- /dev/null
++++ b/include/dt-bindings/pinctrl/rp1.h
+@@ -0,0 +1,46 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Header providing constants for RP1 pinctrl bindings.
++ *
++ * Copyright (C) 2019-2022 Raspberry Pi Ltd.
++ */
++
++#ifndef __DT_BINDINGS_PINCTRL_RP1_H__
++#define __DT_BINDINGS_PINCTRL_RP1_H__
++
++/* brcm,function property */
++#define RP1_FSEL_GPIO_IN 0
++#define RP1_FSEL_GPIO_OUT 1
++#define RP1_FSEL_ALT0_LEGACY 4
++#define RP1_FSEL_ALT1_LEGACY 5
++#define RP1_FSEL_ALT2_LEGACY 6
++#define RP1_FSEL_ALT3_LEGACY 7
++#define RP1_FSEL_ALT4_LEGACY 3
++#define RP1_FSEL_ALT5_LEGACY 2
++#define RP1_FSEL_ALT0 0x08
++#define RP1_FSEL_ALT0INV 0x09
++#define RP1_FSEL_ALT1 0x0a
++#define RP1_FSEL_ALT1INV 0x0b
++#define RP1_FSEL_ALT2 0x0c
++#define RP1_FSEL_ALT2INV 0x0d
++#define RP1_FSEL_ALT3 0x0e
++#define RP1_FSEL_ALT3INV 0x0f
++#define RP1_FSEL_ALT4 0x10
++#define RP1_FSEL_ALT4INV 0x11
++#define RP1_FSEL_ALT5 0x12
++#define RP1_FSEL_ALT5INV 0x13
++#define RP1_FSEL_ALT6 0x14
++#define RP1_FSEL_ALT6INV 0x15
++#define RP1_FSEL_ALT7 0x16
++#define RP1_FSEL_ALT7INV 0x17
++#define RP1_FSEL_ALT8 0x18
++#define RP1_FSEL_ALT8INV 0x19
++#define RP1_FSEL_NONE 0x1a
++
++/* brcm,pull property */
++#define RP1_PUD_OFF 0
++#define RP1_PUD_DOWN 1
++#define RP1_PUD_UP 2
++#define RP1_PUD_KEEP 3
++
++#endif /* __DT_BINDINGS_PINCTRL_RP1_H__ */
--- /dev/null
+From 4d4cc5be473a7767052122a87393a83d10f9ed41 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 10 Oct 2022 14:21:11 +0100
+Subject: [PATCH] pinctrl: Add rp1 driver
+
+RP1 exposes GPIOs. Add a pinctrl driver to allow control of those.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/pinctrl/Kconfig | 7 +
+ drivers/pinctrl/Makefile | 1 +
+ drivers/pinctrl/pinctrl-rp1.c | 1571 +++++++++++++++++++++++++++++
+ include/dt-bindings/pinctrl/rp1.h | 46 -
+ 4 files changed, 1579 insertions(+), 46 deletions(-)
+ create mode 100644 drivers/pinctrl/pinctrl-rp1.c
+ delete mode 100644 include/dt-bindings/pinctrl/rp1.h
+
+--- a/drivers/pinctrl/Kconfig
++++ b/drivers/pinctrl/Kconfig
+@@ -512,6 +512,13 @@ config PINCTRL_ZYNQMP
+ This driver can also be built as a module. If so, the module
+ will be called pinctrl-zynqmp.
+
++config PINCTRL_RP1
++ bool "Pinctrl driver for RP1"
++ select PINMUX
++ select PINCONF
++ select GENERIC_PINCONF
++ select GPIOLIB_IRQCHIP
++
+ source "drivers/pinctrl/actions/Kconfig"
+ source "drivers/pinctrl/aspeed/Kconfig"
+ source "drivers/pinctrl/bcm/Kconfig"
+--- a/drivers/pinctrl/Makefile
++++ b/drivers/pinctrl/Makefile
+@@ -42,6 +42,7 @@ obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-p
+ obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
+ obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
+ obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
++obj-$(CONFIG_PINCTRL_RP1) += pinctrl-rp1.o
+ obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
+ obj-$(CONFIG_PINCTRL_ST) += pinctrl-st.o
+ obj-$(CONFIG_PINCTRL_STMFX) += pinctrl-stmfx.o
+--- /dev/null
++++ b/drivers/pinctrl/pinctrl-rp1.c
+@@ -0,0 +1,1571 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * Driver for Raspberry Pi RP1 GPIO unit (pinctrl + GPIO)
++ *
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ *
++ * This driver is inspired by:
++ * pinctrl-bcm2835.c, please see original file for copyright information
++ */
++
++#include <linux/bitmap.h>
++#include <linux/bitops.h>
++#include <linux/bug.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/gpio/driver.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/irqdesc.h>
++#include <linux/init.h>
++#include <linux/of_address.h>
++#include <linux/of.h>
++#include <linux/of_irq.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/pinctrl/machine.h>
++#include <linux/pinctrl/pinconf.h>
++#include <linux/pinctrl/pinctrl.h>
++#include <linux/pinctrl/pinmux.h>
++#include <linux/pinctrl/pinconf-generic.h>
++#include <linux/platform_device.h>
++#include <linux/seq_file.h>
++#include <linux/spinlock.h>
++#include <linux/types.h>
++#include "core.h"
++#include "pinconf.h"
++#include "pinctrl-utils.h"
++
++#define MODULE_NAME "pinctrl-rp1"
++#define RP1_NUM_GPIOS 54
++#define RP1_NUM_BANKS 3
++
++#define RP1_RW_OFFSET 0x0000
++#define RP1_XOR_OFFSET 0x1000
++#define RP1_SET_OFFSET 0x2000
++#define RP1_CLR_OFFSET 0x3000
++
++#define RP1_GPIO_STATUS 0x0000
++#define RP1_GPIO_CTRL 0x0004
++
++#define RP1_GPIO_PCIE_INTE 0x011c
++#define RP1_GPIO_PCIE_INTS 0x0124
++
++#define RP1_GPIO_EVENTS_SHIFT_RAW 20
++#define RP1_GPIO_STATUS_FALLING BIT(20)
++#define RP1_GPIO_STATUS_RISING BIT(21)
++#define RP1_GPIO_STATUS_LOW BIT(22)
++#define RP1_GPIO_STATUS_HIGH BIT(23)
++
++#define RP1_GPIO_EVENTS_SHIFT_FILTERED 24
++#define RP1_GPIO_STATUS_F_FALLING BIT(24)
++#define RP1_GPIO_STATUS_F_RISING BIT(25)
++#define RP1_GPIO_STATUS_F_LOW BIT(26)
++#define RP1_GPIO_STATUS_F_HIGH BIT(27)
++
++#define RP1_GPIO_CTRL_FUNCSEL_LSB 0
++#define RP1_GPIO_CTRL_FUNCSEL_MASK 0x0000001f
++#define RP1_GPIO_CTRL_OUTOVER_LSB 12
++#define RP1_GPIO_CTRL_OUTOVER_MASK 0x00003000
++#define RP1_GPIO_CTRL_OEOVER_LSB 14
++#define RP1_GPIO_CTRL_OEOVER_MASK 0x0000c000
++#define RP1_GPIO_CTRL_INOVER_LSB 16
++#define RP1_GPIO_CTRL_INOVER_MASK 0x00030000
++#define RP1_GPIO_CTRL_IRQEN_FALLING BIT(20)
++#define RP1_GPIO_CTRL_IRQEN_RISING BIT(21)
++#define RP1_GPIO_CTRL_IRQEN_LOW BIT(22)
++#define RP1_GPIO_CTRL_IRQEN_HIGH BIT(23)
++#define RP1_GPIO_CTRL_IRQEN_F_FALLING BIT(24)
++#define RP1_GPIO_CTRL_IRQEN_F_RISING BIT(25)
++#define RP1_GPIO_CTRL_IRQEN_F_LOW BIT(26)
++#define RP1_GPIO_CTRL_IRQEN_F_HIGH BIT(27)
++#define RP1_GPIO_CTRL_IRQRESET BIT(28)
++#define RP1_GPIO_CTRL_IRQOVER_LSB 30
++#define RP1_GPIO_CTRL_IRQOVER_MASK 0xc0000000
++
++#define RP1_INT_EDGE_FALLING BIT(0)
++#define RP1_INT_EDGE_RISING BIT(1)
++#define RP1_INT_LEVEL_LOW BIT(2)
++#define RP1_INT_LEVEL_HIGH BIT(3)
++#define RP1_INT_MASK 0xf
++
++#define RP1_INT_EDGE_BOTH (RP1_INT_EDGE_FALLING | \
++ RP1_INT_EDGE_RISING)
++#define RP1_PUD_OFF 0
++#define RP1_PUD_DOWN 1
++#define RP1_PUD_UP 2
++
++#define RP1_FSEL_COUNT 9
++
++#define RP1_FSEL_ALT0 0x00
++#define RP1_FSEL_GPIO 0x05
++#define RP1_FSEL_NONE 0x09
++#define RP1_FSEL_NONE_HW 0x1f
++
++#define RP1_DIR_OUTPUT 0
++#define RP1_DIR_INPUT 1
++
++#define RP1_OUTOVER_PERI 0
++#define RP1_OUTOVER_INVPERI 1
++#define RP1_OUTOVER_LOW 2
++#define RP1_OUTOVER_HIGH 3
++
++#define RP1_OEOVER_PERI 0
++#define RP1_OEOVER_INVPERI 1
++#define RP1_OEOVER_DISABLE 2
++#define RP1_OEOVER_ENABLE 3
++
++#define RP1_INOVER_PERI 0
++#define RP1_INOVER_INVPERI 1
++#define RP1_INOVER_LOW 2
++#define RP1_INOVER_HIGH 3
++
++#define RP1_RIO_OUT 0x00
++#define RP1_RIO_OE 0x04
++#define RP1_RIO_IN 0x08
++
++#define RP1_PAD_SLEWFAST_MASK 0x00000001
++#define RP1_PAD_SLEWFAST_LSB 0
++#define RP1_PAD_SCHMITT_MASK 0x00000002
++#define RP1_PAD_SCHMITT_LSB 1
++#define RP1_PAD_PULL_MASK 0x0000000c
++#define RP1_PAD_PULL_LSB 2
++#define RP1_PAD_DRIVE_MASK 0x00000030
++#define RP1_PAD_DRIVE_LSB 4
++#define RP1_PAD_IN_ENABLE_MASK 0x00000040
++#define RP1_PAD_IN_ENABLE_LSB 6
++#define RP1_PAD_OUT_DISABLE_MASK 0x00000080
++#define RP1_PAD_OUT_DISABLE_LSB 7
++
++#define RP1_PAD_DRIVE_2MA 0x00000000
++#define RP1_PAD_DRIVE_4MA 0x00000010
++#define RP1_PAD_DRIVE_8MA 0x00000020
++#define RP1_PAD_DRIVE_12MA 0x00000030
++
++#define FLD_GET(r, f) (((r) & (f ## _MASK)) >> (f ## _LSB))
++#define FLD_SET(r, f, v) r = (((r) & ~(f ## _MASK)) | ((v) << (f ## _LSB)))
++
++#define FUNC(f) \
++ [func_##f] = #f
++#define RP1_MAX_FSEL 8
++#define PIN(i, f0, f1, f2, f3, f4, f5, f6, f7, f8) \
++ [i] = { \
++ .funcs = { \
++ func_##f0, \
++ func_##f1, \
++ func_##f2, \
++ func_##f3, \
++ func_##f4, \
++ func_##f5, \
++ func_##f6, \
++ func_##f7, \
++ func_##f8, \
++ }, \
++ }
++
++#define LEGACY_MAP(n, f0, f1, f2, f3, f4, f5) \
++ [n] = { \
++ func_gpio, \
++ func_gpio, \
++ func_##f5, \
++ func_##f4, \
++ func_##f0, \
++ func_##f1, \
++ func_##f2, \
++ func_##f3, \
++ }
++
++struct rp1_iobank_desc {
++ int min_gpio;
++ int num_gpios;
++ int gpio_offset;
++ int inte_offset;
++ int ints_offset;
++ int rio_offset;
++ int pads_offset;
++};
++
++struct rp1_pin_info {
++ u8 num;
++ u8 bank;
++ u8 offset;
++ u8 fsel;
++ u8 irq_type;
++
++ void __iomem *gpio;
++ void __iomem *rio;
++ void __iomem *inte;
++ void __iomem *ints;
++ void __iomem *pad;
++};
++
++enum funcs {
++ func_alt0,
++ func_alt1,
++ func_alt2,
++ func_alt3,
++ func_alt4,
++ func_gpio,
++ func_alt6,
++ func_alt7,
++ func_alt8,
++ func_none,
++ func_aaud,
++ func_dcd0,
++ func_dpi,
++ func_dsi0_te_ext,
++ func_dsi1_te_ext,
++ func_dsr0,
++ func_dtr0,
++ func_gpclk0,
++ func_gpclk1,
++ func_gpclk2,
++ func_gpclk3,
++ func_gpclk4,
++ func_gpclk5,
++ func_i2c0,
++ func_i2c1,
++ func_i2c2,
++ func_i2c3,
++ func_i2c4,
++ func_i2c5,
++ func_i2c6,
++ func_i2s0,
++ func_i2s1,
++ func_i2s2,
++ func_ir,
++ func_mic,
++ func_pcie_clkreq_n,
++ func_pio,
++ func_proc_rio,
++ func_pwm0,
++ func_pwm1,
++ func_ri0,
++ func_sd0,
++ func_sd1,
++ func_spi0,
++ func_spi1,
++ func_spi2,
++ func_spi3,
++ func_spi4,
++ func_spi5,
++ func_spi6,
++ func_spi7,
++ func_spi8,
++ func_uart0,
++ func_uart1,
++ func_uart2,
++ func_uart3,
++ func_uart4,
++ func_uart5,
++ func_vbus0,
++ func_vbus1,
++ func_vbus2,
++ func_vbus3,
++ func__,
++ func_count = func__,
++ func_invalid = func__,
++};
++
++struct rp1_pin_funcs {
++ u8 funcs[RP1_FSEL_COUNT];
++};
++
++struct rp1_pinctrl {
++ struct device *dev;
++ void __iomem *gpio_base;
++ void __iomem *rio_base;
++ void __iomem *pads_base;
++ int irq[RP1_NUM_BANKS];
++ struct rp1_pin_info pins[RP1_NUM_GPIOS];
++
++ struct pinctrl_dev *pctl_dev;
++ struct gpio_chip gpio_chip;
++ struct pinctrl_gpio_range gpio_range;
++
++ raw_spinlock_t irq_lock[RP1_NUM_BANKS];
++};
++
++const struct rp1_iobank_desc rp1_iobanks[RP1_NUM_BANKS] = {
++ /* gpio inte ints rio pads */
++ { 0, 28, 0x0000, 0x011c, 0x0124, 0x0000, 0x0004 },
++ { 28, 6, 0x4000, 0x411c, 0x4124, 0x4000, 0x4004 },
++ { 34, 20, 0x8000, 0x811c, 0x8124, 0x8000, 0x8004 },
++};
++
++/* pins are just named GPIO0..GPIO53 */
++#define RP1_GPIO_PIN(a) PINCTRL_PIN(a, "gpio" #a)
++static struct pinctrl_pin_desc rp1_gpio_pins[] = {
++ RP1_GPIO_PIN(0),
++ RP1_GPIO_PIN(1),
++ RP1_GPIO_PIN(2),
++ RP1_GPIO_PIN(3),
++ RP1_GPIO_PIN(4),
++ RP1_GPIO_PIN(5),
++ RP1_GPIO_PIN(6),
++ RP1_GPIO_PIN(7),
++ RP1_GPIO_PIN(8),
++ RP1_GPIO_PIN(9),
++ RP1_GPIO_PIN(10),
++ RP1_GPIO_PIN(11),
++ RP1_GPIO_PIN(12),
++ RP1_GPIO_PIN(13),
++ RP1_GPIO_PIN(14),
++ RP1_GPIO_PIN(15),
++ RP1_GPIO_PIN(16),
++ RP1_GPIO_PIN(17),
++ RP1_GPIO_PIN(18),
++ RP1_GPIO_PIN(19),
++ RP1_GPIO_PIN(20),
++ RP1_GPIO_PIN(21),
++ RP1_GPIO_PIN(22),
++ RP1_GPIO_PIN(23),
++ RP1_GPIO_PIN(24),
++ RP1_GPIO_PIN(25),
++ RP1_GPIO_PIN(26),
++ RP1_GPIO_PIN(27),
++ RP1_GPIO_PIN(28),
++ RP1_GPIO_PIN(29),
++ RP1_GPIO_PIN(30),
++ RP1_GPIO_PIN(31),
++ RP1_GPIO_PIN(32),
++ RP1_GPIO_PIN(33),
++ RP1_GPIO_PIN(34),
++ RP1_GPIO_PIN(35),
++ RP1_GPIO_PIN(36),
++ RP1_GPIO_PIN(37),
++ RP1_GPIO_PIN(38),
++ RP1_GPIO_PIN(39),
++ RP1_GPIO_PIN(40),
++ RP1_GPIO_PIN(41),
++ RP1_GPIO_PIN(42),
++ RP1_GPIO_PIN(43),
++ RP1_GPIO_PIN(44),
++ RP1_GPIO_PIN(45),
++ RP1_GPIO_PIN(46),
++ RP1_GPIO_PIN(47),
++ RP1_GPIO_PIN(48),
++ RP1_GPIO_PIN(49),
++ RP1_GPIO_PIN(50),
++ RP1_GPIO_PIN(51),
++ RP1_GPIO_PIN(52),
++ RP1_GPIO_PIN(53),
++};
++
++/* one pin per group */
++static const char * const rp1_gpio_groups[] = {
++ "gpio0",
++ "gpio1",
++ "gpio2",
++ "gpio3",
++ "gpio4",
++ "gpio5",
++ "gpio6",
++ "gpio7",
++ "gpio8",
++ "gpio9",
++ "gpio10",
++ "gpio11",
++ "gpio12",
++ "gpio13",
++ "gpio14",
++ "gpio15",
++ "gpio16",
++ "gpio17",
++ "gpio18",
++ "gpio19",
++ "gpio20",
++ "gpio21",
++ "gpio22",
++ "gpio23",
++ "gpio24",
++ "gpio25",
++ "gpio26",
++ "gpio27",
++ "gpio28",
++ "gpio29",
++ "gpio30",
++ "gpio31",
++ "gpio32",
++ "gpio33",
++ "gpio34",
++ "gpio35",
++ "gpio36",
++ "gpio37",
++ "gpio38",
++ "gpio39",
++ "gpio40",
++ "gpio41",
++ "gpio42",
++ "gpio43",
++ "gpio44",
++ "gpio45",
++ "gpio46",
++ "gpio47",
++ "gpio48",
++ "gpio49",
++ "gpio50",
++ "gpio51",
++ "gpio52",
++ "gpio53",
++};
++
++static const char * const rp1_func_names[] = {
++ FUNC(alt0),
++ FUNC(alt1),
++ FUNC(alt2),
++ FUNC(alt3),
++ FUNC(alt4),
++ FUNC(gpio),
++ FUNC(alt6),
++ FUNC(alt7),
++ FUNC(alt8),
++ FUNC(none),
++ FUNC(aaud),
++ FUNC(dcd0),
++ FUNC(dpi),
++ FUNC(dsi0_te_ext),
++ FUNC(dsi1_te_ext),
++ FUNC(dsr0),
++ FUNC(dtr0),
++ FUNC(gpclk0),
++ FUNC(gpclk1),
++ FUNC(gpclk2),
++ FUNC(gpclk3),
++ FUNC(gpclk4),
++ FUNC(gpclk5),
++ FUNC(i2c0),
++ FUNC(i2c1),
++ FUNC(i2c2),
++ FUNC(i2c3),
++ FUNC(i2c4),
++ FUNC(i2c5),
++ FUNC(i2c6),
++ FUNC(i2s0),
++ FUNC(i2s1),
++ FUNC(i2s2),
++ FUNC(ir),
++ FUNC(mic),
++ FUNC(pcie_clkreq_n),
++ FUNC(pio),
++ FUNC(proc_rio),
++ FUNC(pwm0),
++ FUNC(pwm1),
++ FUNC(ri0),
++ FUNC(sd0),
++ FUNC(sd1),
++ FUNC(spi0),
++ FUNC(spi1),
++ FUNC(spi2),
++ FUNC(spi3),
++ FUNC(spi4),
++ FUNC(spi5),
++ FUNC(spi6),
++ FUNC(spi7),
++ FUNC(spi8),
++ FUNC(uart0),
++ FUNC(uart1),
++ FUNC(uart2),
++ FUNC(uart3),
++ FUNC(uart4),
++ FUNC(uart5),
++ FUNC(vbus0),
++ FUNC(vbus1),
++ FUNC(vbus2),
++ FUNC(vbus3),
++ [func_invalid] = "?"
++};
++
++static const struct rp1_pin_funcs rp1_gpio_pin_funcs[] = {
++ PIN(0, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2),
++ PIN(1, spi0, dpi, uart1, i2c0, _, gpio, proc_rio, pio, spi2),
++ PIN(2, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2),
++ PIN(3, spi0, dpi, uart1, i2c1, ir, gpio, proc_rio, pio, spi2),
++ PIN(4, gpclk0, dpi, uart2, i2c2, ri0, gpio, proc_rio, pio, spi3),
++ PIN(5, gpclk1, dpi, uart2, i2c2, dtr0, gpio, proc_rio, pio, spi3),
++ PIN(6, gpclk2, dpi, uart2, i2c3, dcd0, gpio, proc_rio, pio, spi3),
++ PIN(7, spi0, dpi, uart2, i2c3, dsr0, gpio, proc_rio, pio, spi3),
++ PIN(8, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4),
++ PIN(9, spi0, dpi, uart3, i2c0, _, gpio, proc_rio, pio, spi4),
++ PIN(10, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4),
++ PIN(11, spi0, dpi, uart3, i2c1, _, gpio, proc_rio, pio, spi4),
++ PIN(12, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5),
++ PIN(13, pwm0, dpi, uart4, i2c2, aaud, gpio, proc_rio, pio, spi5),
++ PIN(14, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5),
++ PIN(15, pwm0, dpi, uart4, i2c3, uart0, gpio, proc_rio, pio, spi5),
++ PIN(16, spi1, dpi, dsi0_te_ext, _, uart0, gpio, proc_rio, pio, _),
++ PIN(17, spi1, dpi, dsi1_te_ext, _, uart0, gpio, proc_rio, pio, _),
++ PIN(18, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, gpclk1),
++ PIN(19, spi1, dpi, i2s0, pwm0, i2s1, gpio, proc_rio, pio, _),
++ PIN(20, spi1, dpi, i2s0, gpclk0, i2s1, gpio, proc_rio, pio, _),
++ PIN(21, spi1, dpi, i2s0, gpclk1, i2s1, gpio, proc_rio, pio, _),
++ PIN(22, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _),
++ PIN(23, sd0, dpi, i2s0, i2c3, i2s1, gpio, proc_rio, pio, _),
++ PIN(24, sd0, dpi, i2s0, _, i2s1, gpio, proc_rio, pio, spi2),
++ PIN(25, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi3),
++ PIN(26, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi5),
++ PIN(27, sd0, dpi, i2s0, mic, i2s1, gpio, proc_rio, pio, spi1),
++ PIN(28, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _),
++ PIN(29, sd1, i2c4, i2s2, spi6, vbus0, gpio, proc_rio, _, _),
++ PIN(30, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++ PIN(31, sd1, i2c5, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++ PIN(32, sd1, gpclk3, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++ PIN(33, sd1, gpclk4, i2s2, spi6, uart5, gpio, proc_rio, _, _),
++ PIN(34, pwm1, gpclk3, vbus0, i2c4, mic, gpio, proc_rio, _, _),
++ PIN(35, spi8, pwm1, vbus0, i2c4, mic, gpio, proc_rio, _, _),
++ PIN(36, spi8, uart5, pcie_clkreq_n, i2c5, mic, gpio, proc_rio, _, _),
++ PIN(37, spi8, uart5, mic, i2c5, pcie_clkreq_n, gpio, proc_rio, _, _),
++ PIN(38, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi0_te_ext, _),
++ PIN(39, spi8, uart5, mic, i2c6, aaud, gpio, proc_rio, dsi1_te_ext, _),
++ PIN(40, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _),
++ PIN(41, pwm1, uart5, i2c4, spi6, aaud, gpio, proc_rio, _, _),
++ PIN(42, gpclk5, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _),
++ PIN(43, gpclk4, uart5, vbus1, spi6, i2s2, gpio, proc_rio, _, _),
++ PIN(44, gpclk5, i2c5, pwm1, spi6, i2s2, gpio, proc_rio, _, _),
++ PIN(45, pwm1, i2c5, spi7, spi6, i2s2, gpio, proc_rio, _, _),
++ PIN(46, gpclk3, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi0_te_ext, _),
++ PIN(47, gpclk5, i2c4, spi7, mic, i2s2, gpio, proc_rio, dsi1_te_ext, _),
++ PIN(48, pwm1, pcie_clkreq_n, spi7, mic, uart5, gpio, proc_rio, _, _),
++ PIN(49, spi8, spi7, i2c5, aaud, uart5, gpio, proc_rio, _, _),
++ PIN(50, spi8, spi7, i2c5, aaud, vbus2, gpio, proc_rio, _, _),
++ PIN(51, spi8, spi7, i2c6, aaud, vbus2, gpio, proc_rio, _, _),
++ PIN(52, spi8, _, i2c6, aaud, vbus3, gpio, proc_rio, _, _),
++ PIN(53, spi8, spi7, _, pcie_clkreq_n, vbus3, gpio, proc_rio, _, _),
++};
++
++static const u8 legacy_fsel_map[][8] = {
++ LEGACY_MAP(0, i2c0, _, dpi, spi2, uart1, _),
++ LEGACY_MAP(1, i2c0, _, dpi, spi2, uart1, _),
++ LEGACY_MAP(2, i2c1, _, dpi, spi2, uart1, _),
++ LEGACY_MAP(3, i2c1, _, dpi, spi2, uart1, _),
++ LEGACY_MAP(4, gpclk0, _, dpi, spi3, uart2, i2c2),
++ LEGACY_MAP(5, gpclk1, _, dpi, spi3, uart2, i2c2),
++ LEGACY_MAP(6, gpclk2, _, dpi, spi3, uart2, i2c3),
++ LEGACY_MAP(7, spi0, _, dpi, spi3, uart2, i2c3),
++ LEGACY_MAP(8, spi0, _, dpi, _, uart3, i2c0),
++ LEGACY_MAP(9, spi0, _, dpi, _, uart3, i2c0),
++ LEGACY_MAP(10, spi0, _, dpi, _, uart3, i2c1),
++ LEGACY_MAP(11, spi0, _, dpi, _, uart3, i2c1),
++ LEGACY_MAP(12, pwm0, _, dpi, spi5, uart4, i2c2),
++ LEGACY_MAP(13, pwm0, _, dpi, spi5, uart4, i2c2),
++ LEGACY_MAP(14, uart0, _, dpi, spi5, uart4, _),
++ LEGACY_MAP(15, uart0, _, dpi, spi5, uart4, _),
++ LEGACY_MAP(16, _, _, dpi, uart0, spi1, _),
++ LEGACY_MAP(17, _, _, dpi, uart0, spi1, _),
++ LEGACY_MAP(18, i2s0, _, dpi, _, spi1, pwm0),
++ LEGACY_MAP(19, i2s0, _, dpi, _, spi1, pwm0),
++ LEGACY_MAP(20, i2s0, _, dpi, _, spi1, gpclk0),
++ LEGACY_MAP(21, i2s0, _, dpi, _, spi1, gpclk1),
++ LEGACY_MAP(22, sd0, _, dpi, _, _, i2c3),
++ LEGACY_MAP(23, sd0, _, dpi, _, _, i2c3),
++ LEGACY_MAP(24, sd0, _, dpi, _, _, spi2),
++ LEGACY_MAP(25, sd0, _, dpi, _, _, spi3),
++ LEGACY_MAP(26, sd0, _, dpi, _, _, spi5),
++ LEGACY_MAP(27, sd0, _, dpi, _, _, _),
++};
++
++static const char * const irq_type_names[] = {
++ [IRQ_TYPE_NONE] = "none",
++ [IRQ_TYPE_EDGE_RISING] = "edge-rising",
++ [IRQ_TYPE_EDGE_FALLING] = "edge-falling",
++ [IRQ_TYPE_EDGE_BOTH] = "edge-both",
++ [IRQ_TYPE_LEVEL_HIGH] = "level-high",
++ [IRQ_TYPE_LEVEL_LOW] = "level-low",
++};
++
++static int rp1_pinconf_set(struct pinctrl_dev *pctldev,
++ unsigned int offset, unsigned long *configs,
++ unsigned int num_configs);
++
++static struct rp1_pin_info *rp1_get_pin(struct gpio_chip *chip,
++ unsigned int offset)
++{
++ struct rp1_pinctrl *pc = gpiochip_get_data(chip);
++
++ if (pc && offset < RP1_NUM_GPIOS)
++ return &pc->pins[offset];
++ return NULL;
++}
++
++static struct rp1_pin_info *rp1_get_pin_pctl(struct pinctrl_dev *pctldev,
++ unsigned int offset)
++{
++ struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++
++ if (pc && offset < RP1_NUM_GPIOS)
++ return &pc->pins[offset];
++ return NULL;
++}
++
++static void rp1_pad_update(struct rp1_pin_info *pin, u32 clr, u32 set)
++{
++ u32 padctrl = readl(pin->pad);
++
++ padctrl &= ~clr;
++ padctrl |= set;
++
++ writel(padctrl, pin->pad);
++}
++
++static void rp1_input_enable(struct rp1_pin_info *pin, int value)
++{
++ rp1_pad_update(pin, RP1_PAD_IN_ENABLE_MASK,
++ value ? RP1_PAD_IN_ENABLE_MASK : 0);
++}
++
++static void rp1_output_enable(struct rp1_pin_info *pin, int value)
++{
++ rp1_pad_update(pin, RP1_PAD_OUT_DISABLE_MASK,
++ value ? 0 : RP1_PAD_OUT_DISABLE_MASK);
++}
++
++static u32 rp1_get_fsel(struct rp1_pin_info *pin)
++{
++ u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
++ u32 oeover = FLD_GET(ctrl, RP1_GPIO_CTRL_OEOVER);
++ u32 fsel = FLD_GET(ctrl, RP1_GPIO_CTRL_FUNCSEL);
++
++ if (oeover != RP1_OEOVER_PERI || fsel >= RP1_FSEL_COUNT)
++ fsel = RP1_FSEL_NONE;
++
++ return fsel;
++}
++
++static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel)
++{
++ u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
++
++ if (fsel >= RP1_FSEL_COUNT)
++ fsel = RP1_FSEL_NONE_HW;
++
++ rp1_input_enable(pin, 1);
++ rp1_output_enable(pin, 1);
++
++ if (fsel == RP1_FSEL_NONE) {
++ FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_DISABLE);
++ } else {
++ FLD_SET(ctrl, RP1_GPIO_CTRL_OUTOVER, RP1_OUTOVER_PERI);
++ FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_PERI);
++ }
++ FLD_SET(ctrl, RP1_GPIO_CTRL_FUNCSEL, fsel);
++ writel(ctrl, pin->gpio + RP1_GPIO_CTRL);
++}
++
++static int rp1_get_dir(struct rp1_pin_info *pin)
++{
++ return !(readl(pin->rio + RP1_RIO_OE) & (1 << pin->offset)) ?
++ RP1_DIR_INPUT : RP1_DIR_OUTPUT;
++}
++
++static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input)
++{
++ int offset = is_input ? RP1_CLR_OFFSET : RP1_SET_OFFSET;
++
++ writel(1 << pin->offset, pin->rio + RP1_RIO_OE + offset);
++}
++
++static int rp1_get_value(struct rp1_pin_info *pin)
++{
++ return !!(readl(pin->rio + RP1_RIO_IN) & (1 << pin->offset));
++}
++
++static void rp1_set_value(struct rp1_pin_info *pin, int value)
++{
++ /* Assume the pin is already an output */
++ writel(1 << pin->offset,
++ pin->rio + RP1_RIO_OUT + (value ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
++}
++
++static int rp1_gpio_get(struct gpio_chip *chip, unsigned offset)
++{
++ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++ int ret;
++
++ if (!pin)
++ return -EINVAL;
++ ret = rp1_get_value(pin);
++ return ret;
++}
++
++static void rp1_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
++{
++ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++
++ if (pin)
++ rp1_set_value(pin, value);
++}
++
++static int rp1_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
++{
++ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++ u32 fsel;
++
++ if (!pin)
++ return -EINVAL;
++ fsel = rp1_get_fsel(pin);
++ if (fsel != RP1_FSEL_GPIO)
++ return -EINVAL;
++ return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ?
++ GPIO_LINE_DIRECTION_OUT :
++ GPIO_LINE_DIRECTION_IN;
++}
++
++static int rp1_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
++{
++ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++
++ if (!pin)
++ return -EINVAL;
++ rp1_set_dir(pin, RP1_DIR_INPUT);
++ rp1_set_fsel(pin, RP1_FSEL_GPIO);
++ return 0;
++}
++
++static int rp1_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
++ int value)
++{
++ struct rp1_pin_info *pin = rp1_get_pin(chip, offset);
++
++ if (!pin)
++ return -EINVAL;
++ rp1_set_value(pin, value);
++ rp1_set_dir(pin, RP1_DIR_OUTPUT);
++ rp1_set_fsel(pin, RP1_FSEL_GPIO);
++ return 0;
++}
++
++static int rp1_gpio_set_config(struct gpio_chip *gc, unsigned offset,
++ unsigned long config)
++{
++ struct rp1_pinctrl *pc = gpiochip_get_data(gc);
++ unsigned long configs[] = { config };
++
++ return rp1_pinconf_set(pc->pctl_dev, offset, configs,
++ ARRAY_SIZE(configs));
++}
++
++static const struct gpio_chip rp1_gpio_chip = {
++ .label = MODULE_NAME,
++ .owner = THIS_MODULE,
++ .request = gpiochip_generic_request,
++ .free = gpiochip_generic_free,
++ .direction_input = rp1_gpio_direction_input,
++ .direction_output = rp1_gpio_direction_output,
++ .get_direction = rp1_gpio_get_direction,
++ .get = rp1_gpio_get,
++ .set = rp1_gpio_set,
++ .base = -1,
++ .set_config = rp1_gpio_set_config,
++ .ngpio = RP1_NUM_GPIOS,
++ .can_sleep = false,
++};
++
++static void rp1_gpio_irq_handler(struct irq_desc *desc)
++{
++ struct gpio_chip *chip = irq_desc_get_handler_data(desc);
++ struct rp1_pinctrl *pc = gpiochip_get_data(chip);
++ struct irq_chip *host_chip = irq_desc_get_chip(desc);
++ const struct rp1_iobank_desc *bank;
++ int irq = irq_desc_get_irq(desc);
++ unsigned long ints;
++ int b;
++
++ if (pc->irq[0] == irq)
++ bank = &rp1_iobanks[0];
++ else if (pc->irq[1] == irq)
++ bank = &rp1_iobanks[1];
++ else
++ bank = &rp1_iobanks[2];
++
++ chained_irq_enter(host_chip, desc);
++
++ ints = readl(pc->gpio_base + bank->ints_offset);
++ for_each_set_bit(b, &ints, 32) {
++ struct rp1_pin_info *pin = rp1_get_pin(chip, b);
++
++ writel(RP1_GPIO_CTRL_IRQRESET,
++ pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++ generic_handle_irq(irq_linear_revmap(pc->gpio_chip.irq.domain,
++ bank->gpio_offset + b));
++ }
++
++ chained_irq_exit(host_chip, desc);
++}
++
++static void rp1_gpio_irq_config(struct rp1_pin_info *pin, bool enable)
++{
++ writel(1 << pin->offset,
++ pin->inte + (enable ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
++ if (!enable)
++ /* Clear any latched events */
++ writel(RP1_GPIO_CTRL_IRQRESET,
++ pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++}
++
++static void rp1_gpio_irq_enable(struct irq_data *data)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++ unsigned gpio = irqd_to_hwirq(data);
++ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++
++ rp1_gpio_irq_config(pin, true);
++}
++
++static void rp1_gpio_irq_disable(struct irq_data *data)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++ unsigned gpio = irqd_to_hwirq(data);
++ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++
++ rp1_gpio_irq_config(pin, false);
++}
++
++static int rp1_irq_set_type(struct rp1_pin_info *pin, unsigned int type)
++{
++ u32 irq_flags;
++
++ switch (type) {
++ case IRQ_TYPE_NONE:
++ irq_flags = 0;
++ break;
++ case IRQ_TYPE_EDGE_RISING:
++ irq_flags = RP1_INT_EDGE_RISING;
++ break;
++ case IRQ_TYPE_EDGE_FALLING:
++ irq_flags = RP1_INT_EDGE_FALLING;
++ break;
++ case IRQ_TYPE_EDGE_BOTH:
++ irq_flags = RP1_INT_EDGE_RISING | RP1_INT_EDGE_FALLING;
++ break;
++ case IRQ_TYPE_LEVEL_HIGH:
++ irq_flags = RP1_INT_LEVEL_HIGH;
++ break;
++ case IRQ_TYPE_LEVEL_LOW:
++ irq_flags = RP1_INT_LEVEL_LOW;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ /* Clear them all */
++ writel(RP1_INT_MASK << RP1_GPIO_EVENTS_SHIFT_RAW,
++ pin->gpio + RP1_CLR_OFFSET + RP1_GPIO_CTRL);
++ /* Set those that are needed */
++ writel(irq_flags << RP1_GPIO_EVENTS_SHIFT_RAW,
++ pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++ pin->irq_type = type;
++
++ return 0;
++}
++
++static int rp1_gpio_irq_set_type(struct irq_data *data, unsigned int type)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++ struct rp1_pinctrl *pc = gpiochip_get_data(chip);
++ unsigned gpio = irqd_to_hwirq(data);
++ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++ int bank = pin->bank;
++ unsigned long flags;
++ int ret;
++
++ raw_spin_lock_irqsave(&pc->irq_lock[bank], flags);
++
++ ret = rp1_irq_set_type(pin, type);
++ if (!ret) {
++ if (type & IRQ_TYPE_EDGE_BOTH)
++ irq_set_handler_locked(data, handle_edge_irq);
++ else
++ irq_set_handler_locked(data, handle_level_irq);
++ }
++
++ raw_spin_unlock_irqrestore(&pc->irq_lock[bank], flags);
++
++ return ret;
++}
++
++static void rp1_gpio_irq_ack(struct irq_data *data)
++{
++ struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
++ unsigned gpio = irqd_to_hwirq(data);
++ struct rp1_pin_info *pin = rp1_get_pin(chip, gpio);
++
++ /* Clear any latched events */
++ writel(RP1_GPIO_CTRL_IRQRESET, pin->gpio + RP1_SET_OFFSET + RP1_GPIO_CTRL);
++}
++
++static struct irq_chip rp1_gpio_irq_chip = {
++ .name = MODULE_NAME,
++ .irq_enable = rp1_gpio_irq_enable,
++ .irq_disable = rp1_gpio_irq_disable,
++ .irq_set_type = rp1_gpio_irq_set_type,
++ .irq_ack = rp1_gpio_irq_ack,
++ .irq_mask = rp1_gpio_irq_disable,
++ .irq_unmask = rp1_gpio_irq_enable,
++ .flags = IRQCHIP_IMMUTABLE,
++};
++
++static int rp1_pctl_get_groups_count(struct pinctrl_dev *pctldev)
++{
++ return ARRAY_SIZE(rp1_gpio_groups);
++}
++
++static const char *rp1_pctl_get_group_name(struct pinctrl_dev *pctldev,
++ unsigned selector)
++{
++ return rp1_gpio_groups[selector];
++}
++
++static enum funcs rp1_get_fsel_func(unsigned pin, unsigned fsel)
++{
++ if (pin < RP1_NUM_GPIOS) {
++ if (fsel < RP1_FSEL_COUNT)
++ return rp1_gpio_pin_funcs[pin].funcs[fsel];
++ else if (fsel == RP1_FSEL_NONE)
++ return func_none;
++ }
++ return func_invalid;
++}
++
++static int rp1_pctl_get_group_pins(struct pinctrl_dev *pctldev,
++ unsigned selector,
++ const unsigned **pins,
++ unsigned *num_pins)
++{
++ *pins = &rp1_gpio_pins[selector].number;
++ *num_pins = 1;
++
++ return 0;
++}
++
++static void rp1_pctl_pin_dbg_show(struct pinctrl_dev *pctldev,
++ struct seq_file *s,
++ unsigned offset)
++{
++ struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++ struct gpio_chip *chip = &pc->gpio_chip;
++ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++ u32 fsel = rp1_get_fsel(pin);
++ enum funcs func = rp1_get_fsel_func(offset, fsel);
++ int value = rp1_get_value(pin);
++ int irq = irq_find_mapping(chip->irq.domain, offset);
++
++ seq_printf(s, "function %s (%s) in %s; irq %d (%s)",
++ rp1_func_names[fsel], rp1_func_names[func],
++ value ? "hi" : "lo",
++ irq, irq_type_names[pin->irq_type]);
++}
++
++static void rp1_pctl_dt_free_map(struct pinctrl_dev *pctldev,
++ struct pinctrl_map *maps, unsigned num_maps)
++{
++ int i;
++
++ for (i = 0; i < num_maps; i++)
++ if (maps[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
++ kfree(maps[i].data.configs.configs);
++
++ kfree(maps);
++}
++
++static int rp1_pctl_legacy_map_func(struct rp1_pinctrl *pc,
++ struct device_node *np, u32 pin, u32 fnum,
++ struct pinctrl_map *maps,
++ unsigned int *num_maps)
++{
++ struct pinctrl_map *map = &maps[*num_maps];
++ enum funcs func;
++
++ if (fnum >= ARRAY_SIZE(legacy_fsel_map[0])) {
++ dev_err(pc->dev, "%pOF: invalid brcm,function %d\n", np, fnum);
++ return -EINVAL;
++ }
++
++ func = legacy_fsel_map[pin][fnum];
++ if (func == func_invalid) {
++ dev_err(pc->dev, "%pOF: brcm,function %d not supported on pin %d\n",
++ np, fnum, pin);
++ }
++
++ map->type = PIN_MAP_TYPE_MUX_GROUP;
++ map->data.mux.group = rp1_gpio_groups[pin];
++ map->data.mux.function = rp1_func_names[func];
++ (*num_maps)++;
++
++ return 0;
++}
++
++static int rp1_pctl_legacy_map_pull(struct rp1_pinctrl *pc,
++ struct device_node *np, u32 pin, u32 pull,
++ struct pinctrl_map *maps,
++ unsigned int *num_maps)
++{
++ struct pinctrl_map *map = &maps[*num_maps];
++ enum pin_config_param param;
++ unsigned long *configs;
++
++ switch (pull) {
++ case RP1_PUD_OFF:
++ param = PIN_CONFIG_BIAS_DISABLE;
++ break;
++ case RP1_PUD_DOWN:
++ param = PIN_CONFIG_BIAS_PULL_DOWN;
++ break;
++ case RP1_PUD_UP:
++ param = PIN_CONFIG_BIAS_PULL_UP;
++ break;
++ default:
++ dev_err(pc->dev, "%pOF: invalid brcm,pull %d\n", np, pull);
++ return -EINVAL;
++ }
++
++ configs = kzalloc(sizeof(*configs), GFP_KERNEL);
++ if (!configs)
++ return -ENOMEM;
++
++ configs[0] = pinconf_to_config_packed(param, 0);
++ map->type = PIN_MAP_TYPE_CONFIGS_PIN;
++ map->data.configs.group_or_pin = rp1_gpio_pins[pin].name;
++ map->data.configs.configs = configs;
++ map->data.configs.num_configs = 1;
++ (*num_maps)++;
++
++ return 0;
++}
++
++static int rp1_pctl_dt_node_to_map(struct pinctrl_dev *pctldev,
++ struct device_node *np,
++ struct pinctrl_map **map,
++ unsigned int *num_maps)
++{
++ struct rp1_pinctrl *pc = pinctrl_dev_get_drvdata(pctldev);
++ struct property *pins, *funcs, *pulls;
++ int num_pins, num_funcs, num_pulls, maps_per_pin;
++ struct pinctrl_map *maps;
++ unsigned long *configs = NULL;
++ const char *function = NULL;
++ unsigned int reserved_maps;
++ int num_configs = 0;
++ int i, err;
++ u32 pin, func, pull;
++
++ /* Check for legacy pin declaration */
++ pins = of_find_property(np, "brcm,pins", NULL);
++
++ if (!pins) /* Assume generic bindings in this node */
++ return pinconf_generic_dt_node_to_map_all(pctldev, np, map, num_maps);
++
++ funcs = of_find_property(np, "brcm,function", NULL);
++ if (!funcs)
++ of_property_read_string(np, "function", &function);
++
++ pulls = of_find_property(np, "brcm,pull", NULL);
++ if (!pulls)
++ pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
++
++ if (!function && !funcs && !num_configs && !pulls) {
++ dev_err(pc->dev,
++ "%pOF: no function, brcm,function, brcm,pull, etc.\n",
++ np);
++ return -EINVAL;
++ }
++
++ num_pins = pins->length / 4;
++ num_funcs = funcs ? (funcs->length / 4) : 0;
++ num_pulls = pulls ? (pulls->length / 4) : 0;
++
++ if (num_funcs > 1 && num_funcs != num_pins) {
++ dev_err(pc->dev,
++ "%pOF: brcm,function must have 1 or %d entries\n",
++ np, num_pins);
++ return -EINVAL;
++ }
++
++ if (num_pulls > 1 && num_pulls != num_pins) {
++ dev_err(pc->dev,
++ "%pOF: brcm,pull must have 1 or %d entries\n",
++ np, num_pins);
++ return -EINVAL;
++ }
++
++ maps_per_pin = 0;
++ if (function || num_funcs)
++ maps_per_pin++;
++ if (num_configs || num_pulls)
++ maps_per_pin++;
++ reserved_maps = num_pins * maps_per_pin;
++ maps = kcalloc(reserved_maps, sizeof(*maps), GFP_KERNEL);
++ if (!maps)
++ return -ENOMEM;
++
++ *num_maps = 0;
++
++ for (i = 0; i < num_pins; i++) {
++ err = of_property_read_u32_index(np, "brcm,pins", i, &pin);
++ if (err)
++ goto out;
++ if (pin >= ARRAY_SIZE(legacy_fsel_map)) {
++ dev_err(pc->dev, "%pOF: invalid brcm,pins value %d\n",
++ np, pin);
++ err = -EINVAL;
++ goto out;
++ }
++
++ if (num_funcs) {
++ err = of_property_read_u32_index(np, "brcm,function",
++ (num_funcs > 1) ? i : 0,
++ &func);
++ if (err)
++ goto out;
++ err = rp1_pctl_legacy_map_func(pc, np, pin, func,
++ maps, num_maps);
++ } else if (function) {
++ err = pinctrl_utils_add_map_mux(pctldev, &maps,
++ &reserved_maps, num_maps,
++ rp1_gpio_groups[pin],
++ function);
++ }
++
++ if (err)
++ goto out;
++
++ if (num_pulls) {
++ err = of_property_read_u32_index(np, "brcm,pull",
++ (num_pulls > 1) ? i : 0,
++ &pull);
++ if (err)
++ goto out;
++ err = rp1_pctl_legacy_map_pull(pc, np, pin, pull,
++ maps, num_maps);
++ } else if (num_configs) {
++ err = pinctrl_utils_add_map_configs(pctldev, &maps,
++ &reserved_maps, num_maps,
++ rp1_gpio_groups[pin],
++ configs, num_configs,
++ PIN_MAP_TYPE_CONFIGS_PIN);
++ }
++
++ if (err)
++ goto out;
++ }
++
++ *map = maps;
++
++ return 0;
++
++out:
++ rp1_pctl_dt_free_map(pctldev, maps, reserved_maps);
++ return err;
++}
++
++static const struct pinctrl_ops rp1_pctl_ops = {
++ .get_groups_count = rp1_pctl_get_groups_count,
++ .get_group_name = rp1_pctl_get_group_name,
++ .get_group_pins = rp1_pctl_get_group_pins,
++ .pin_dbg_show = rp1_pctl_pin_dbg_show,
++ .dt_node_to_map = rp1_pctl_dt_node_to_map,
++ .dt_free_map = rp1_pctl_dt_free_map,
++};
++
++static int rp1_pmx_free(struct pinctrl_dev *pctldev, unsigned offset)
++{
++ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++ u32 fsel = rp1_get_fsel(pin);
++
++ /* Return non-GPIOs to GPIO_IN */
++ if (fsel != RP1_FSEL_GPIO) {
++ rp1_set_dir(pin, RP1_DIR_INPUT);
++ rp1_set_fsel(pin, RP1_FSEL_GPIO);
++ }
++
++ return 0;
++}
++
++static int rp1_pmx_get_functions_count(struct pinctrl_dev *pctldev)
++{
++ return func_count;
++}
++
++static const char *rp1_pmx_get_function_name(struct pinctrl_dev *pctldev,
++ unsigned selector)
++{
++ return (selector < func_count) ? rp1_func_names[selector] : NULL;
++}
++
++static int rp1_pmx_get_function_groups(struct pinctrl_dev *pctldev,
++ unsigned selector,
++ const char * const **groups,
++ unsigned * const num_groups)
++{
++ /* every pin can do every function */
++ *groups = rp1_gpio_groups;
++ *num_groups = ARRAY_SIZE(rp1_gpio_groups);
++
++ return 0;
++}
++
++static int rp1_pmx_set(struct pinctrl_dev *pctldev, unsigned func_selector,
++ unsigned group_selector)
++{
++ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, group_selector);
++ const u8 *pin_funcs;
++ int fsel;
++
++ /* func_selector is an enum funcs, so needs translation */
++
++ if (func_selector >= RP1_FSEL_COUNT) {
++ /* Convert to an fsel number */
++ pin_funcs = rp1_gpio_pin_funcs[pin->num].funcs;
++ for (fsel = 0; fsel < RP1_FSEL_COUNT; fsel++) {
++ if (pin_funcs[fsel] == func_selector)
++ break;
++ }
++ } else {
++ fsel = (int)func_selector;
++ }
++
++ if (fsel >= RP1_FSEL_COUNT && fsel != RP1_FSEL_NONE)
++ return -EINVAL;
++
++ rp1_set_fsel(pin, fsel);
++
++ return 0;
++}
++
++static void rp1_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned offset)
++{
++ (void)rp1_pmx_free(pctldev, offset);
++}
++
++static int rp1_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
++ struct pinctrl_gpio_range *range,
++ unsigned offset,
++ bool input)
++{
++ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++
++ rp1_set_dir(pin, input);
++ rp1_set_fsel(pin, RP1_FSEL_GPIO);
++
++ return 0;
++}
++
++static const struct pinmux_ops rp1_pmx_ops = {
++ .free = rp1_pmx_free,
++ .get_functions_count = rp1_pmx_get_functions_count,
++ .get_function_name = rp1_pmx_get_function_name,
++ .get_function_groups = rp1_pmx_get_function_groups,
++ .set_mux = rp1_pmx_set,
++ .gpio_disable_free = rp1_pmx_gpio_disable_free,
++ .gpio_set_direction = rp1_pmx_gpio_set_direction,
++};
++
++static void rp1_pull_config_set(struct rp1_pin_info *pin, unsigned int arg)
++{
++ u32 padctrl = readl(pin->pad);
++
++ FLD_SET(padctrl, RP1_PAD_PULL, arg & 0x3);
++
++ writel(padctrl, pin->pad);
++}
++
++/* Generic pinconf methods */
++
++static int rp1_pinconf_set(struct pinctrl_dev *pctldev, unsigned int offset,
++ unsigned long *configs, unsigned int num_configs)
++{
++ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++ u32 param, arg;
++ int i;
++
++ if (!pin)
++ return -EINVAL;
++
++ for (i = 0; i < num_configs; i++) {
++ param = pinconf_to_config_param(configs[i]);
++ arg = pinconf_to_config_argument(configs[i]);
++
++ switch (param) {
++ case PIN_CONFIG_BIAS_DISABLE:
++ rp1_pull_config_set(pin, RP1_PUD_OFF);
++ break;
++
++ case PIN_CONFIG_BIAS_PULL_DOWN:
++ rp1_pull_config_set(pin, RP1_PUD_DOWN);
++ break;
++
++ case PIN_CONFIG_BIAS_PULL_UP:
++ rp1_pull_config_set(pin, RP1_PUD_UP);
++ break;
++
++ case PIN_CONFIG_INPUT_ENABLE:
++ rp1_input_enable(pin, arg);
++ break;
++
++ case PIN_CONFIG_OUTPUT_ENABLE:
++ rp1_output_enable(pin, arg);
++ break;
++
++ case PIN_CONFIG_OUTPUT:
++ rp1_set_value(pin, arg);
++ rp1_set_dir(pin, RP1_DIR_OUTPUT);
++ rp1_set_fsel(pin, RP1_FSEL_GPIO);
++ break;
++
++ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
++ rp1_pad_update(pin, RP1_PAD_SCHMITT_MASK,
++ arg ? RP1_PAD_SCHMITT_MASK : 0);
++ break;
++
++ case PIN_CONFIG_SLEW_RATE:
++ rp1_pad_update(pin, RP1_PAD_SLEWFAST_MASK,
++ arg ? RP1_PAD_SLEWFAST_MASK : 0);
++ break;
++
++ case PIN_CONFIG_DRIVE_STRENGTH:
++ switch (arg) {
++ case 2:
++ arg = RP1_PAD_DRIVE_2MA;
++ break;
++ case 4:
++ arg = RP1_PAD_DRIVE_4MA;
++ break;
++ case 8:
++ arg = RP1_PAD_DRIVE_8MA;
++ break;
++ case 12:
++ arg = RP1_PAD_DRIVE_12MA;
++ break;
++ default:
++ return -ENOTSUPP;
++ }
++ rp1_pad_update(pin, RP1_PAD_DRIVE_MASK, arg);
++ break;
++
++ default:
++ return -ENOTSUPP;
++
++ } /* switch param type */
++ } /* for each config */
++
++ return 0;
++}
++
++static int rp1_pinconf_get(struct pinctrl_dev *pctldev, unsigned offset,
++ unsigned long *config)
++{
++ struct rp1_pin_info *pin = rp1_get_pin_pctl(pctldev, offset);
++ enum pin_config_param param = pinconf_to_config_param(*config);
++ u32 padctrl;
++ u32 arg;
++
++ if (!pin)
++ return -EINVAL;
++
++ padctrl = readl(pin->pad);
++
++ switch (param) {
++ case PIN_CONFIG_INPUT_ENABLE:
++ arg = !!(padctrl & RP1_PAD_IN_ENABLE_MASK);
++ break;
++ case PIN_CONFIG_OUTPUT_ENABLE:
++ arg = !(padctrl & RP1_PAD_OUT_DISABLE_MASK);
++ break;
++ case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
++ arg = !!(padctrl & RP1_PAD_SCHMITT_MASK);
++ break;
++ case PIN_CONFIG_SLEW_RATE:
++ arg = !!(padctrl & RP1_PAD_SLEWFAST_MASK);
++ break;
++ case PIN_CONFIG_DRIVE_STRENGTH:
++ switch (padctrl & RP1_PAD_DRIVE_MASK) {
++ case RP1_PAD_DRIVE_2MA:
++ arg = 2;
++ break;
++ case RP1_PAD_DRIVE_4MA:
++ arg = 4;
++ break;
++ case RP1_PAD_DRIVE_8MA:
++ arg = 8;
++ break;
++ case RP1_PAD_DRIVE_12MA:
++ arg = 12;
++ break;
++ }
++ break;
++ case PIN_CONFIG_BIAS_DISABLE:
++ arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_OFF << RP1_PAD_PULL_LSB));
++ break;
++ case PIN_CONFIG_BIAS_PULL_DOWN:
++ arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_DOWN << RP1_PAD_PULL_LSB));
++ break;
++
++ case PIN_CONFIG_BIAS_PULL_UP:
++ arg = ((padctrl & RP1_PAD_PULL_MASK) == (RP1_PUD_UP << RP1_PAD_PULL_LSB));
++ break;
++ default:
++ return -ENOTSUPP;
++ }
++
++ *config = pinconf_to_config_packed(param, arg);
++
++ return 0;
++}
++
++static const struct pinconf_ops rp1_pinconf_ops = {
++ .is_generic = true,
++ .pin_config_get = rp1_pinconf_get,
++ .pin_config_set = rp1_pinconf_set,
++};
++
++static struct pinctrl_desc rp1_pinctrl_desc = {
++ .name = MODULE_NAME,
++ .pins = rp1_gpio_pins,
++ .npins = ARRAY_SIZE(rp1_gpio_pins),
++ .pctlops = &rp1_pctl_ops,
++ .pmxops = &rp1_pmx_ops,
++ .confops = &rp1_pinconf_ops,
++ .owner = THIS_MODULE,
++};
++
++static struct pinctrl_gpio_range rp1_pinctrl_gpio_range = {
++ .name = MODULE_NAME,
++ .npins = RP1_NUM_GPIOS,
++};
++
++static const struct of_device_id rp1_pinctrl_match[] = {
++ {
++ .compatible = "raspberrypi,rp1-gpio",
++ .data = &rp1_pinconf_ops,
++ },
++ {}
++};
++
++static inline void __iomem *devm_auto_iomap(struct platform_device *pdev,
++ unsigned int index)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++
++ if (np)
++ return devm_of_iomap(dev, np, (int)index, NULL);
++ else
++ return devm_platform_ioremap_resource(pdev, index);
++}
++
++static int rp1_pinctrl_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct rp1_pinctrl *pc;
++ struct gpio_irq_chip *girq;
++ int err, i;
++
++ BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_pins) != RP1_NUM_GPIOS);
++ BUILD_BUG_ON(ARRAY_SIZE(rp1_gpio_groups) != RP1_NUM_GPIOS);
++
++ pc = devm_kzalloc(dev, sizeof(*pc), GFP_KERNEL);
++ if (!pc)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, pc);
++ pc->dev = dev;
++
++ pc->gpio_base = devm_auto_iomap(pdev, 0);
++ if (IS_ERR(pc->gpio_base)) {
++ dev_err(dev, "could not get GPIO IO memory\n");
++ return PTR_ERR(pc->gpio_base);
++ }
++
++ pc->rio_base = devm_auto_iomap(pdev, 1);
++ if (IS_ERR(pc->rio_base)) {
++ dev_err(dev, "could not get RIO IO memory\n");
++ return PTR_ERR(pc->rio_base);
++ }
++
++ pc->pads_base = devm_auto_iomap(pdev, 2);
++ if (IS_ERR(pc->pads_base)) {
++ dev_err(dev, "could not get PADS IO memory\n");
++ return PTR_ERR(pc->pads_base);
++ }
++
++ pc->gpio_chip = rp1_gpio_chip;
++ pc->gpio_chip.parent = dev;
++
++ for (i = 0; i < RP1_NUM_BANKS; i++) {
++ const struct rp1_iobank_desc *bank = &rp1_iobanks[i];
++ int j;
++
++ for (j = 0; j < bank->num_gpios; j++) {
++ struct rp1_pin_info *pin =
++ &pc->pins[bank->min_gpio + j];
++
++ pin->num = bank->min_gpio + j;
++ pin->bank = i;
++ pin->offset = j;
++
++ pin->gpio = pc->gpio_base + bank->gpio_offset +
++ j * sizeof(u32) * 2;
++ pin->inte = pc->gpio_base + bank->inte_offset;
++ pin->ints = pc->gpio_base + bank->ints_offset;
++ pin->rio = pc->rio_base + bank->rio_offset;
++ pin->pad = pc->pads_base + bank->pads_offset +
++ j * sizeof(u32);
++ }
++
++ raw_spin_lock_init(&pc->irq_lock[i]);
++ }
++
++ pc->pctl_dev = devm_pinctrl_register(dev, &rp1_pinctrl_desc, pc);
++ if (IS_ERR(pc->pctl_dev))
++ return PTR_ERR(pc->pctl_dev);
++
++ girq = &pc->gpio_chip.irq;
++ girq->chip = &rp1_gpio_irq_chip;
++ girq->parent_handler = rp1_gpio_irq_handler;
++ girq->num_parents = RP1_NUM_BANKS;
++ girq->parents = pc->irq;
++
++ /*
++ * Use the same handler for all groups: this is necessary
++ * since we use one gpiochip to cover all lines - the
++ * irq handler then needs to figure out which group and
++ * bank that was firing the IRQ and look up the per-group
++ * and bank data.
++ */
++ for (i = 0; i < RP1_NUM_BANKS; i++) {
++ pc->irq[i] = irq_of_parse_and_map(np, i);
++ if (!pc->irq[i]) {
++ girq->num_parents = i;
++ break;
++ }
++ }
++
++ girq->default_type = IRQ_TYPE_NONE;
++ girq->handler = handle_level_irq;
++
++ err = devm_gpiochip_add_data(dev, &pc->gpio_chip, pc);
++ if (err) {
++ dev_err(dev, "could not add GPIO chip\n");
++ return err;
++ }
++
++ pc->gpio_range = rp1_pinctrl_gpio_range;
++ pc->gpio_range.base = pc->gpio_chip.base;
++ pc->gpio_range.gc = &pc->gpio_chip;
++ pinctrl_add_gpio_range(pc->pctl_dev, &pc->gpio_range);
++
++ return 0;
++}
++
++static struct platform_driver rp1_pinctrl_driver = {
++ .probe = rp1_pinctrl_probe,
++ .driver = {
++ .name = MODULE_NAME,
++ .of_match_table = rp1_pinctrl_match,
++ .suppress_bind_attrs = true,
++ },
++};
++builtin_platform_driver(rp1_pinctrl_driver);
+--- a/include/dt-bindings/pinctrl/rp1.h
++++ /dev/null
+@@ -1,46 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-/*
+- * Header providing constants for RP1 pinctrl bindings.
+- *
+- * Copyright (C) 2019-2022 Raspberry Pi Ltd.
+- */
+-
+-#ifndef __DT_BINDINGS_PINCTRL_RP1_H__
+-#define __DT_BINDINGS_PINCTRL_RP1_H__
+-
+-/* brcm,function property */
+-#define RP1_FSEL_GPIO_IN 0
+-#define RP1_FSEL_GPIO_OUT 1
+-#define RP1_FSEL_ALT0_LEGACY 4
+-#define RP1_FSEL_ALT1_LEGACY 5
+-#define RP1_FSEL_ALT2_LEGACY 6
+-#define RP1_FSEL_ALT3_LEGACY 7
+-#define RP1_FSEL_ALT4_LEGACY 3
+-#define RP1_FSEL_ALT5_LEGACY 2
+-#define RP1_FSEL_ALT0 0x08
+-#define RP1_FSEL_ALT0INV 0x09
+-#define RP1_FSEL_ALT1 0x0a
+-#define RP1_FSEL_ALT1INV 0x0b
+-#define RP1_FSEL_ALT2 0x0c
+-#define RP1_FSEL_ALT2INV 0x0d
+-#define RP1_FSEL_ALT3 0x0e
+-#define RP1_FSEL_ALT3INV 0x0f
+-#define RP1_FSEL_ALT4 0x10
+-#define RP1_FSEL_ALT4INV 0x11
+-#define RP1_FSEL_ALT5 0x12
+-#define RP1_FSEL_ALT5INV 0x13
+-#define RP1_FSEL_ALT6 0x14
+-#define RP1_FSEL_ALT6INV 0x15
+-#define RP1_FSEL_ALT7 0x16
+-#define RP1_FSEL_ALT7INV 0x17
+-#define RP1_FSEL_ALT8 0x18
+-#define RP1_FSEL_ALT8INV 0x19
+-#define RP1_FSEL_NONE 0x1a
+-
+-/* brcm,pull property */
+-#define RP1_PUD_OFF 0
+-#define RP1_PUD_DOWN 1
+-#define RP1_PUD_UP 2
+-#define RP1_PUD_KEEP 3
+-
+-#endif /* __DT_BINDINGS_PINCTRL_RP1_H__ */
--- /dev/null
+From f88da9e21d8eff58eeb9280ae96bf9593121d8eb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 12 Oct 2022 13:24:51 +0100
+Subject: [PATCH] serial: pl011: rp1 uart support
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/amba-pl011.c | 96 +++++++++++++++++++++++++++++++++
+ 1 file changed, 96 insertions(+)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -152,6 +152,20 @@ static const struct vendor_data vendor_s
+ .fixed_options = true,
+ };
+
++static struct vendor_data vendor_arm_axi = {
++ .reg_offset = pl011_std_offsets,
++ .ifls = UART011_IFLS_RX4_8 | UART011_IFLS_TX4_8,
++ .fr_busy = UART01x_FR_BUSY,
++ .fr_dsr = UART01x_FR_DSR,
++ .fr_cts = UART01x_FR_CTS,
++ .fr_ri = UART011_FR_RI,
++ .oversampling = false,
++ .dma_threshold = false,
++ .cts_event_workaround = false,
++ .always_enabled = false,
++ .fixed_options = false,
++};
++
+ #ifdef CONFIG_ACPI_SPCR_TABLE
+ static const struct vendor_data vendor_qdt_qdf2400_e44 = {
+ .reg_offset = pl011_std_offsets,
+@@ -2972,6 +2986,86 @@ static struct platform_driver arm_sbsa_u
+ },
+ };
+
++static int pl011_axi_probe(struct platform_device *pdev)
++{
++ struct uart_amba_port *uap;
++ struct vendor_data *vendor = &vendor_arm_axi;
++ struct resource *r;
++ unsigned int periphid;
++ int portnr, ret, irq;
++
++ portnr = pl011_find_free_port();
++ if (portnr < 0)
++ return portnr;
++
++ uap = devm_kzalloc(&pdev->dev, sizeof(struct uart_amba_port),
++ GFP_KERNEL);
++ if (!uap)
++ return -ENOMEM;
++
++ uap->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(uap->clk))
++ return PTR_ERR(uap->clk);
++
++ if (of_property_read_bool(pdev->dev.of_node, "cts-event-workaround")) {
++ vendor->cts_event_workaround = true;
++ dev_info(&pdev->dev, "cts_event_workaround enabled\n");
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return irq;
++
++ periphid = 0x00241011; /* A safe default */
++ of_property_read_u32(pdev->dev.of_node, "arm,primecell-periphid",
++ &periphid);
++
++ uap->reg_offset = vendor->reg_offset;
++ uap->vendor = vendor;
++ uap->fifosize = (AMBA_REV_BITS(periphid) < 3) ? 16 : 32;
++ uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
++ uap->port.irq = irq;
++ uap->port.ops = &amba_pl011_pops;
++
++ snprintf(uap->type, sizeof(uap->type), "PL011 AXI");
++
++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++ ret = pl011_setup_port(&pdev->dev, uap, r, portnr);
++ if (ret)
++ return ret;
++
++ platform_set_drvdata(pdev, uap);
++
++ return pl011_register_port(uap);
++}
++
++static int pl011_axi_remove(struct platform_device *pdev)
++{
++ struct uart_amba_port *uap = platform_get_drvdata(pdev);
++
++ uart_remove_one_port(&amba_reg, &uap->port);
++ pl011_unregister_port(uap);
++ return 0;
++}
++
++static const struct of_device_id pl011_axi_of_match[] = {
++ { .compatible = "arm,pl011-axi" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, pl011_axi_of_match);
++
++static struct platform_driver pl011_axi_platform_driver = {
++ .probe = pl011_axi_probe,
++ .remove = pl011_axi_remove,
++ .driver = {
++ .name = "pl011-axi",
++ .pm = &pl011_dev_pm_ops,
++ .of_match_table = of_match_ptr(pl011_axi_of_match),
++ .suppress_bind_attrs = IS_BUILTIN(CONFIG_SERIAL_AMBA_PL011),
++ },
++};
++
+ static const struct amba_id pl011_ids[] = {
+ {
+ .id = 0x00041011,
+@@ -3005,6 +3099,8 @@ static int __init pl011_init(void)
+
+ if (platform_driver_register(&arm_sbsa_uart_platform_driver))
+ pr_warn("could not register SBSA UART platform driver\n");
++ if (platform_driver_register(&pl011_axi_platform_driver))
++ pr_warn("could not register PL011 AXI platform driver\n");
+ return amba_driver_register(&pl011_driver);
+ }
+
--- /dev/null
+From 4a5ac516ca0a820e7c006ae408872009e37e114b Mon Sep 17 00:00:00 2001
+From: Liam Fraser <liam@raspberrypi.com>
+Date: Thu, 14 Mar 2019 16:01:26 +0000
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: define sdio timeout clocks
+
+Signed-off-by: Liam Fraser <liam@raspberrypi.com>
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 12 ++++++++++++
+ drivers/mmc/host/sdhci-pltfm.c | 8 ++++++++
+ drivers/mmc/host/sdhci-pltfm.h | 3 +++
+ 3 files changed, 23 insertions(+)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -330,6 +330,7 @@ static const struct sdhci_ops sdhci_dwcm
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = dwcmshc_set_uhs_signaling,
+ .get_max_clock = dwcmshc_get_max_clock,
++ .get_timeout_clock = sdhci_pltfm_clk_get_timeout_clock,
+ .reset = sdhci_reset,
+ .adma_write_desc = dwcmshc_adma_write_desc,
+ };
+@@ -500,6 +501,16 @@ static int dwcmshc_probe(struct platform
+ clk_prepare_enable(priv->bus_clk);
+ }
+
++ pltfm_host->timeout_clk = devm_clk_get(&pdev->dev, "timeout");
++ if (IS_ERR(pltfm_host->timeout_clk)) {
++ err = PTR_ERR(pltfm_host->timeout_clk);
++ dev_err(&pdev->dev, "failed to get timeout clk: %d\n", err);
++ goto free_pltfm;
++ }
++ err = clk_prepare_enable(pltfm_host->timeout_clk);
++ if (err)
++ goto free_pltfm;
++
+ err = mmc_of_parse(host->mmc);
+ if (err)
+ goto err_clk;
+@@ -550,6 +561,7 @@ err_setup_host:
+ sdhci_cleanup_host(host);
+ err_clk:
+ clk_disable_unprepare(pltfm_host->clk);
++ clk_disable_unprepare(pltfm_host->timeout_clk);
+ clk_disable_unprepare(priv->bus_clk);
+ if (rk_priv)
+ clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
+--- a/drivers/mmc/host/sdhci-pltfm.c
++++ b/drivers/mmc/host/sdhci-pltfm.c
+@@ -33,6 +33,14 @@ unsigned int sdhci_pltfm_clk_get_max_clo
+ }
+ EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_max_clock);
+
++unsigned int sdhci_pltfm_clk_get_timeout_clock(struct sdhci_host *host)
++{
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++
++ return clk_get_rate(pltfm_host->timeout_clk);
++}
++EXPORT_SYMBOL_GPL(sdhci_pltfm_clk_get_timeout_clock);
++
+ static const struct sdhci_ops sdhci_pltfm_ops = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+--- a/drivers/mmc/host/sdhci-pltfm.h
++++ b/drivers/mmc/host/sdhci-pltfm.h
+@@ -20,6 +20,7 @@ struct sdhci_pltfm_data {
+
+ struct sdhci_pltfm_host {
+ struct clk *clk;
++ struct clk *timeout_clk;
+
+ /* migrate from sdhci_of_host */
+ unsigned int clock;
+@@ -106,6 +107,8 @@ extern int sdhci_pltfm_unregister(struct
+
+ extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
+
++extern unsigned int sdhci_pltfm_clk_get_timeout_clock(struct sdhci_host *host);
++
+ static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
+ {
+ return host->private;
--- /dev/null
+From 14a43b3fd43bf9b230f93d1eba276d40aac969ba Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 12 Oct 2022 14:07:32 +0100
+Subject: [PATCH] mmc: sdhci-of-dwcmshc: rp1 sdio changes
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 29 ++++++++++++++++++++++++++---
+ 1 file changed, 26 insertions(+), 3 deletions(-)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -87,6 +87,7 @@ struct rk35xx_priv {
+
+ struct dwcmshc_priv {
+ struct clk *bus_clk;
++ struct clk *sdio_clk;
+ int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA reg */
+ void *priv; /* pointer to SoC private stuff */
+ };
+@@ -114,6 +115,17 @@ static void dwcmshc_adma_write_desc(stru
+ sdhci_adma_write_desc(host, desc, addr, len, cmd);
+ }
+
++static void dwcmshc_set_clock(struct sdhci_host *host, unsigned int clock)
++{
++ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
++ struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
++
++ if (priv->sdio_clk)
++ clk_set_rate(priv->sdio_clk, clock);
++
++ sdhci_set_clock(host, clock);
++}
++
+ static unsigned int dwcmshc_get_max_clock(struct sdhci_host *host)
+ {
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+@@ -326,7 +338,7 @@ static void rk35xx_sdhci_reset(struct sd
+ }
+
+ static const struct sdhci_ops sdhci_dwcmshc_ops = {
+- .set_clock = sdhci_set_clock,
++ .set_clock = dwcmshc_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .set_uhs_signaling = dwcmshc_set_uhs_signaling,
+ .get_max_clock = dwcmshc_get_max_clock,
+@@ -346,8 +358,10 @@ static const struct sdhci_ops sdhci_dwcm
+
+ static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
+ .ops = &sdhci_dwcmshc_ops,
+- .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+- .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN,
++ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
++ SDHCI_QUIRK_BROKEN_CARD_DETECTION,
++ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
++ SDHCI_QUIRK2_BROKEN_HS200,
+ };
+
+ #ifdef CONFIG_ACPI
+@@ -499,6 +513,14 @@ static int dwcmshc_probe(struct platform
+ priv->bus_clk = devm_clk_get(dev, "bus");
+ if (!IS_ERR(priv->bus_clk))
+ clk_prepare_enable(priv->bus_clk);
++
++ pltfm_host->timeout_clk = devm_clk_get(dev, "timeout");
++ if (!IS_ERR(pltfm_host->timeout_clk))
++ err = clk_prepare_enable(pltfm_host->timeout_clk);
++ if (err)
++ goto free_pltfm;
++
++ priv->sdio_clk = devm_clk_get_optional(&pdev->dev, "sdio");
+ }
+
+ pltfm_host->timeout_clk = devm_clk_get(&pdev->dev, "timeout");
+@@ -516,6 +538,7 @@ static int dwcmshc_probe(struct platform
+ goto err_clk;
+
+ sdhci_get_of_property(pdev);
++ sdhci_enable_v4_mode(host);
+
+ priv->vendor_specific_area1 =
+ sdhci_readl(host, DWCMSHC_P_VENDOR_AREA1) & DWCMSHC_AREA1_MASK;
--- /dev/null
+From b427cc1a83404f48b12dec2efbef076b38df6218 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 12 Oct 2022 14:20:07 +0100
+Subject: [PATCH] clk: rp1: Add sdio-clk driver
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/clk/Kconfig | 6 +
+ drivers/clk/Makefile | 1 +
+ drivers/clk/clk-rp1-sdio.c | 600 +++++++++++++++++++++++++++++++++++++
+ 3 files changed, 607 insertions(+)
+ create mode 100644 drivers/clk/clk-rp1-sdio.c
+
+--- a/drivers/clk/Kconfig
++++ b/drivers/clk/Kconfig
+@@ -96,6 +96,12 @@ config COMMON_CLK_RP1
+ help
+ Enable common clock framework support for Raspberry Pi RP1
+
++config COMMON_CLK_RP1_SDIO
++ tristate "Clock driver for the RP1 SDIO interfaces"
++ depends on MFD_RP1
++ help
++ SDIO clock driver for the RP1 support chip
++
+ config COMMON_CLK_HI655X
+ tristate "Clock driver for Hi655x" if EXPERT
+ depends on (MFD_HI655X_PMIC || COMPILE_TEST)
+--- a/drivers/clk/Makefile
++++ b/drivers/clk/Makefile
+@@ -59,6 +59,7 @@ obj-$(CONFIG_COMMON_CLK_PWM) += clk-pwm
+ obj-$(CONFIG_CLK_QORIQ) += clk-qoriq.o
+ obj-$(CONFIG_COMMON_CLK_RK808) += clk-rk808.o
+ obj-$(CONFIG_COMMON_CLK_RP1) += clk-rp1.o
++obj-$(CONFIG_COMMON_CLK_RP1_SDIO) += clk-rp1-sdio.o
+ obj-$(CONFIG_COMMON_CLK_HI655X) += clk-hi655x.o
+ obj-$(CONFIG_COMMON_CLK_S2MPS11) += clk-s2mps11.o
+ obj-$(CONFIG_COMMON_CLK_SCMI) += clk-scmi.o
+--- /dev/null
++++ b/drivers/clk/clk-rp1-sdio.c
+@@ -0,0 +1,600 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * SDIO clock driver for RP1
++ *
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ */
++
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <linux/clk-provider.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++// Register : MODE
++#define MODE 0x00000000
++#define MODE_BITS 0x70030000
++#define MODE_RESET 0x00000000
++// Field : MODE_STEPS_PER_CYCLE
++#define MODE_STEPS_PER_CYCLE_RESET 0x0
++#define MODE_STEPS_PER_CYCLE_BITS 0x70000000
++#define MODE_STEPS_PER_CYCLE_MSB 30
++#define MODE_STEPS_PER_CYCLE_LSB 28
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_20 0x0
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_10 0x1
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_16 0x2
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_8 0x3
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_12 0x4
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_6 0x5
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_5 0x6
++#define MODE_STEPS_PER_CYCLE_VALUE_STEPS_4 0x7
++// Field : MODE_SRC_SEL
++#define MODE_SRC_SEL_RESET 0x0
++#define MODE_SRC_SEL_BITS 0x00030000
++#define MODE_SRC_SEL_MSB 17
++#define MODE_SRC_SEL_LSB 16
++#define MODE_SRC_SEL_VALUE_STOP 0x0
++#define MODE_SRC_SEL_VALUE_CLK_ALT_SRC 0x1
++#define MODE_SRC_SEL_VALUE_PLL_SYS_VCO 0x2
++#define MODE_SRC_SEL_VALUE_PLL_SYS_VCO_AGAIN 0x3
++// Register : FROMIP
++#define FROMIP 0x00000004
++#define FROMIP_BITS 0x0f9713ff
++#define FROMIP_RESET 0x00000000
++// Field : FROMIP_TUNING_CCLK_SEL
++#define FROMIP_TUNING_CCLK_SEL_RESET 0x0
++#define FROMIP_TUNING_CCLK_SEL_BITS 0x0f000000
++#define FROMIP_TUNING_CCLK_SEL_MSB 27
++#define FROMIP_TUNING_CCLK_SEL_LSB 24
++// Field : FROMIP_TUNING_CCLK_UPDATE
++#define FROMIP_TUNING_CCLK_UPDATE_RESET 0x0
++#define FROMIP_TUNING_CCLK_UPDATE_BITS 0x00800000
++#define FROMIP_TUNING_CCLK_UPDATE_MSB 23
++#define FROMIP_TUNING_CCLK_UPDATE_LSB 23
++// Field : FROMIP_SAMPLE_CCLK_SEL
++#define FROMIP_SAMPLE_CCLK_SEL_RESET 0x0
++#define FROMIP_SAMPLE_CCLK_SEL_BITS 0x00100000
++#define FROMIP_SAMPLE_CCLK_SEL_MSB 20
++#define FROMIP_SAMPLE_CCLK_SEL_LSB 20
++// Field : FROMIP_CLK2CARD_ON
++#define FROMIP_CLK2CARD_ON_RESET 0x0
++#define FROMIP_CLK2CARD_ON_BITS 0x00040000
++#define FROMIP_CLK2CARD_ON_MSB 18
++#define FROMIP_CLK2CARD_ON_LSB 18
++// Field : FROMIP_CARD_CLK_STABLE
++#define FROMIP_CARD_CLK_STABLE_RESET 0x0
++#define FROMIP_CARD_CLK_STABLE_BITS 0x00020000
++#define FROMIP_CARD_CLK_STABLE_MSB 17
++#define FROMIP_CARD_CLK_STABLE_LSB 17
++// Field : FROMIP_CARD_CLK_EN
++#define FROMIP_CARD_CLK_EN_RESET 0x0
++#define FROMIP_CARD_CLK_EN_BITS 0x00010000
++#define FROMIP_CARD_CLK_EN_MSB 16
++#define FROMIP_CARD_CLK_EN_LSB 16
++// Field : FROMIP_CLK_GEN_SEL
++#define FROMIP_CLK_GEN_SEL_RESET 0x0
++#define FROMIP_CLK_GEN_SEL_BITS 0x00001000
++#define FROMIP_CLK_GEN_SEL_MSB 12
++#define FROMIP_CLK_GEN_SEL_LSB 12
++// Field : FROMIP_FREQ_SEL
++#define FROMIP_FREQ_SEL_RESET 0x000
++#define FROMIP_FREQ_SEL_BITS 0x000003ff
++#define FROMIP_FREQ_SEL_MSB 9
++#define FROMIP_FREQ_SEL_LSB 0
++// Register : LOCAL
++#define LOCAL 0x00000008
++#define LOCAL_BITS 0x1f9713ff
++#define LOCAL_RESET 0x00000000
++// Field : LOCAL_TUNING_CCLK_SEL
++#define LOCAL_TUNING_CCLK_SEL_RESET 0x00
++#define LOCAL_TUNING_CCLK_SEL_BITS 0x1f000000
++#define LOCAL_TUNING_CCLK_SEL_MSB 28
++#define LOCAL_TUNING_CCLK_SEL_LSB 24
++// Field : LOCAL_TUNING_CCLK_UPDATE
++#define LOCAL_TUNING_CCLK_UPDATE_RESET 0x0
++#define LOCAL_TUNING_CCLK_UPDATE_BITS 0x00800000
++#define LOCAL_TUNING_CCLK_UPDATE_MSB 23
++#define LOCAL_TUNING_CCLK_UPDATE_LSB 23
++// Field : LOCAL_SAMPLE_CCLK_SEL
++#define LOCAL_SAMPLE_CCLK_SEL_RESET 0x0
++#define LOCAL_SAMPLE_CCLK_SEL_BITS 0x00100000
++#define LOCAL_SAMPLE_CCLK_SEL_MSB 20
++#define LOCAL_SAMPLE_CCLK_SEL_LSB 20
++// Field : LOCAL_CLK2CARD_ON
++#define LOCAL_CLK2CARD_ON_RESET 0x0
++#define LOCAL_CLK2CARD_ON_BITS 0x00040000
++#define LOCAL_CLK2CARD_ON_MSB 18
++#define LOCAL_CLK2CARD_ON_LSB 18
++// Field : LOCAL_CARD_CLK_STABLE
++#define LOCAL_CARD_CLK_STABLE_RESET 0x0
++#define LOCAL_CARD_CLK_STABLE_BITS 0x00020000
++#define LOCAL_CARD_CLK_STABLE_MSB 17
++#define LOCAL_CARD_CLK_STABLE_LSB 17
++// Field : LOCAL_CARD_CLK_EN
++#define LOCAL_CARD_CLK_EN_RESET 0x0
++#define LOCAL_CARD_CLK_EN_BITS 0x00010000
++#define LOCAL_CARD_CLK_EN_MSB 16
++#define LOCAL_CARD_CLK_EN_LSB 16
++// Field : LOCAL_CLK_GEN_SEL
++#define LOCAL_CLK_GEN_SEL_RESET 0x0
++#define LOCAL_CLK_GEN_SEL_BITS 0x00001000
++#define LOCAL_CLK_GEN_SEL_MSB 12
++#define LOCAL_CLK_GEN_SEL_LSB 12
++#define LOCAL_CLK_GEN_SEL_VALUE_PROGCLOCKMODE 0x0
++#define LOCAL_CLK_GEN_SEL_VALUE_DIVCLOCKMODE 0x1
++// Field : LOCAL_FREQ_SEL
++#define LOCAL_FREQ_SEL_RESET 0x000
++#define LOCAL_FREQ_SEL_BITS 0x000003ff
++#define LOCAL_FREQ_SEL_MSB 9
++#define LOCAL_FREQ_SEL_LSB 0
++// Register : USE_LOCAL
++#define USE_LOCAL 0x0000000c
++#define USE_LOCAL_BITS 0x01951001
++#define USE_LOCAL_RESET 0x00000000
++// Field : USE_LOCAL_TUNING_CCLK_SEL
++#define USE_LOCAL_TUNING_CCLK_SEL_RESET 0x0
++#define USE_LOCAL_TUNING_CCLK_SEL_BITS 0x01000000
++#define USE_LOCAL_TUNING_CCLK_SEL_MSB 24
++#define USE_LOCAL_TUNING_CCLK_SEL_LSB 24
++// Field : USE_LOCAL_TUNING_CCLK_UPDATE
++#define USE_LOCAL_TUNING_CCLK_UPDATE_RESET 0x0
++#define USE_LOCAL_TUNING_CCLK_UPDATE_BITS 0x00800000
++#define USE_LOCAL_TUNING_CCLK_UPDATE_MSB 23
++#define USE_LOCAL_TUNING_CCLK_UPDATE_LSB 23
++// Field : USE_LOCAL_SAMPLE_CCLK_SEL
++#define USE_LOCAL_SAMPLE_CCLK_SEL_RESET 0x0
++#define USE_LOCAL_SAMPLE_CCLK_SEL_BITS 0x00100000
++#define USE_LOCAL_SAMPLE_CCLK_SEL_MSB 20
++#define USE_LOCAL_SAMPLE_CCLK_SEL_LSB 20
++// Field : USE_LOCAL_CLK2CARD_ON
++#define USE_LOCAL_CLK2CARD_ON_RESET 0x0
++#define USE_LOCAL_CLK2CARD_ON_BITS 0x00040000
++#define USE_LOCAL_CLK2CARD_ON_MSB 18
++#define USE_LOCAL_CLK2CARD_ON_LSB 18
++// Field : USE_LOCAL_CARD_CLK_EN
++#define USE_LOCAL_CARD_CLK_EN_RESET 0x0
++#define USE_LOCAL_CARD_CLK_EN_BITS 0x00010000
++#define USE_LOCAL_CARD_CLK_EN_MSB 16
++#define USE_LOCAL_CARD_CLK_EN_LSB 16
++// Field : USE_LOCAL_CLK_GEN_SEL
++#define USE_LOCAL_CLK_GEN_SEL_RESET 0x0
++#define USE_LOCAL_CLK_GEN_SEL_BITS 0x00001000
++#define USE_LOCAL_CLK_GEN_SEL_MSB 12
++#define USE_LOCAL_CLK_GEN_SEL_LSB 12
++// Field : USE_LOCAL_FREQ_SEL
++#define USE_LOCAL_FREQ_SEL_RESET 0x0
++#define USE_LOCAL_FREQ_SEL_BITS 0x00000001
++#define USE_LOCAL_FREQ_SEL_MSB 0
++#define USE_LOCAL_FREQ_SEL_LSB 0
++// Register : SD_DELAY
++#define SD_DELAY 0x00000010
++#define SD_DELAY_BITS 0x0000001f
++#define SD_DELAY_RESET 0x00000000
++// Field : SD_DELAY_STEPS
++#define SD_DELAY_STEPS_RESET 0x00
++#define SD_DELAY_STEPS_BITS 0x0000001f
++#define SD_DELAY_STEPS_MSB 4
++#define SD_DELAY_STEPS_LSB 0
++// Register : RX_DELAY
++#define RX_DELAY 0x00000014
++#define RX_DELAY_BITS 0x19f3331f
++#define RX_DELAY_RESET 0x00000000
++// Field : RX_DELAY_BYPASS
++#define RX_DELAY_BYPASS_RESET 0x0
++#define RX_DELAY_BYPASS_BITS 0x10000000
++#define RX_DELAY_BYPASS_MSB 28
++#define RX_DELAY_BYPASS_LSB 28
++// Field : RX_DELAY_FAIL_ACTUAL
++#define RX_DELAY_FAIL_ACTUAL_RESET 0x0
++#define RX_DELAY_FAIL_ACTUAL_BITS 0x08000000
++#define RX_DELAY_FAIL_ACTUAL_MSB 27
++#define RX_DELAY_FAIL_ACTUAL_LSB 27
++// Field : RX_DELAY_ACTUAL
++#define RX_DELAY_ACTUAL_RESET 0x00
++#define RX_DELAY_ACTUAL_BITS 0x01f00000
++#define RX_DELAY_ACTUAL_MSB 24
++#define RX_DELAY_ACTUAL_LSB 20
++// Field : RX_DELAY_OFFSET
++#define RX_DELAY_OFFSET_RESET 0x0
++#define RX_DELAY_OFFSET_BITS 0x00030000
++#define RX_DELAY_OFFSET_MSB 17
++#define RX_DELAY_OFFSET_LSB 16
++// Field : RX_DELAY_OVERFLOW
++#define RX_DELAY_OVERFLOW_RESET 0x0
++#define RX_DELAY_OVERFLOW_BITS 0x00003000
++#define RX_DELAY_OVERFLOW_MSB 13
++#define RX_DELAY_OVERFLOW_LSB 12
++#define RX_DELAY_OVERFLOW_VALUE_ALLOW 0x0
++#define RX_DELAY_OVERFLOW_VALUE_CLAMP 0x1
++#define RX_DELAY_OVERFLOW_VALUE_FAIL 0x2
++// Field : RX_DELAY_MAP
++#define RX_DELAY_MAP_RESET 0x0
++#define RX_DELAY_MAP_BITS 0x00000300
++#define RX_DELAY_MAP_MSB 9
++#define RX_DELAY_MAP_LSB 8
++#define RX_DELAY_MAP_VALUE_DIRECT 0x0
++#define RX_DELAY_MAP_VALUE 0x1
++#define RX_DELAY_MAP_VALUE_STRETCH 0x2
++// Field : RX_DELAY_FIXED
++#define RX_DELAY_FIXED_RESET 0x00
++#define RX_DELAY_FIXED_BITS 0x0000001f
++#define RX_DELAY_FIXED_MSB 4
++#define RX_DELAY_FIXED_LSB 0
++// Register : NDIV
++#define NDIV 0x00000018
++#define NDIV_BITS 0x1fff0000
++#define NDIV_RESET 0x00110000
++// Field : NDIV_DIVB
++#define NDIV_DIVB_RESET 0x001
++#define NDIV_DIVB_BITS 0x1ff00000
++#define NDIV_DIVB_MSB 28
++#define NDIV_DIVB_LSB 20
++// Field : NDIV_DIVA
++#define NDIV_DIVA_RESET 0x1
++#define NDIV_DIVA_BITS 0x000f0000
++#define NDIV_DIVA_MSB 19
++#define NDIV_DIVA_LSB 16
++// Register : CS
++#define CS 0x0000001c
++#define CS_BITS 0x00111101
++#define CS_RESET 0x00000001
++// Field : CS_RX_DEL_UPDATED
++#define CS_RX_DEL_UPDATED_RESET 0x0
++#define CS_RX_DEL_UPDATED_BITS 0x00100000
++#define CS_RX_DEL_UPDATED_MSB 20
++#define CS_RX_DEL_UPDATED_LSB 20
++// Field : CS_RX_CLK_RUNNING
++#define CS_RX_CLK_RUNNING_RESET 0x0
++#define CS_RX_CLK_RUNNING_BITS 0x00010000
++#define CS_RX_CLK_RUNNING_MSB 16
++#define CS_RX_CLK_RUNNING_LSB 16
++// Field : CS_SD_CLK_RUNNING
++#define CS_SD_CLK_RUNNING_RESET 0x0
++#define CS_SD_CLK_RUNNING_BITS 0x00001000
++#define CS_SD_CLK_RUNNING_MSB 12
++#define CS_SD_CLK_RUNNING_LSB 12
++// Field : CS_TX_CLK_RUNNING
++#define CS_TX_CLK_RUNNING_RESET 0x0
++#define CS_TX_CLK_RUNNING_BITS 0x00000100
++#define CS_TX_CLK_RUNNING_MSB 8
++#define CS_TX_CLK_RUNNING_LSB 8
++// Field : CS_RESET
++#define CS_RESET_RESET 0x1
++#define CS_RESET_BITS 0x00000001
++#define CS_RESET_MSB 0
++#define CS_RESET_LSB 0
++
++#define FPGA_SRC_RATE 400000000
++
++/* Base number of steps to delay in relation to tx clk.
++ * The relationship of the 3 clocks are as follows:
++ * tx_clk: This clock is provided to the controller. Data is sent out
++ * to the pads using this clock.
++ * sd_clk: This clock is sent out to the card.
++ * rx_clk: This clock is used to sample the data coming back from the card.
++ * This may need to be several steps ahead of the tx_clk. The default rx delay
++ * is used as a base delay, and can be further adjusted by the sd host
++ * controller during the tuning process if using a DDR50 or faster SD card
++ */
++/*
++ * PRJY-1813 - the default SD clock delay needs to be set to ~60% of the total
++ * number of steps to meet tISU (>6ns) and tIH (>2ns) in high-speed mode.
++ * On FPGA this means delay SDCLK by 5, and sample RX with a delay of 6.
++ */
++#define DEFAULT_RX_DELAY 6
++#define DEFAULT_SD_DELAY 5
++
++struct rp1_sdio_clkgen {
++ struct device *dev;
++
++ /* Source clock. Either PLL VCO or fixed freq on FPGA */
++ struct clk *src_clk;
++ /* Desired base frequency. Max freq card can go */
++ struct clk *base_clk;
++
++ struct clk_hw hw;
++ void __iomem *regs;
++
++ /* Starting value of local register before changing freq */
++ u32 local_base;
++};
++
++static inline void clkgen_write(struct rp1_sdio_clkgen *clkgen, u32 reg, u32 val)
++{
++ dev_dbg(clkgen->dev, "%s: write reg 0x%x: 0x%x\n", __func__, reg, val);
++ writel(val, clkgen->regs + reg);
++}
++
++static inline u32 clkgen_read(struct rp1_sdio_clkgen *clkgen, u32 reg)
++{
++ u32 val = readl(clkgen->regs + reg);
++
++ dev_dbg(clkgen->dev, "%s: read reg 0x%x: 0x%x\n", __func__, reg, val);
++ return val;
++}
++
++static int get_steps(unsigned int steps)
++{
++ int ret = -1;
++
++ if (steps == 4)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_4;
++ else if (steps == 5)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_5;
++ else if (steps == 6)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_6;
++ else if (steps == 8)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_8;
++ else if (steps == 10)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_10;
++ else if (steps == 12)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_12;
++ else if (steps == 16)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_16;
++ else if (steps == 20)
++ ret = MODE_STEPS_PER_CYCLE_VALUE_STEPS_20;
++ return ret;
++}
++
++static int rp1_sdio_clk_init(struct rp1_sdio_clkgen *clkgen)
++{
++ unsigned long src_rate = clk_get_rate(clkgen->src_clk);
++ unsigned long base_rate = clk_get_rate(clkgen->base_clk);
++ unsigned int steps = src_rate / base_rate;
++ u32 reg = 0;
++ int steps_value = 0;
++
++ dev_dbg(clkgen->dev, "init: src_rate %lu, base_rate %lu, steps %d\n",
++ src_rate, base_rate, steps);
++
++ /* Assert reset while we set up clkgen */
++ clkgen_write(clkgen, CS, CS_RESET_BITS);
++
++ /* Pick clock source */
++ if (src_rate == FPGA_SRC_RATE) {
++ /* Using ALT SRC */
++ reg |= MODE_SRC_SEL_VALUE_CLK_ALT_SRC << MODE_SRC_SEL_LSB;
++ } else {
++ /* Assume we are using PLL SYS VCO */
++ reg |= MODE_SRC_SEL_VALUE_PLL_SYS_VCO << MODE_SRC_SEL_LSB;
++ }
++
++ /* How many delay steps are available in one cycle for this source */
++ steps_value = get_steps(steps);
++ if (steps_value < 0) {
++ dev_err(clkgen->dev, "Invalid step value: %d\n", steps);
++ return -EINVAL;
++ }
++ reg |= steps_value << MODE_STEPS_PER_CYCLE_LSB;
++
++ /* Mode register is done now*/
++ clkgen_write(clkgen, MODE, reg);
++
++ /* Now set delay mode */
++ /* Clamp value if out of range rx delay is used */
++ reg = RX_DELAY_OVERFLOW_VALUE_CLAMP << RX_DELAY_OVERFLOW_LSB;
++ /* SD tuning bus goes from 0x0 to 0xf but we don't necessarily have that
++ * many steps available depending on the source so map 0x0 -> 0xf to one
++ * cycle of rx delay
++ */
++ reg |= RX_DELAY_MAP_VALUE_STRETCH << RX_DELAY_MAP_LSB;
++
++ /* Default RX delay */
++ dev_dbg(clkgen->dev, "default rx delay %d\n", DEFAULT_RX_DELAY);
++ reg |= (DEFAULT_RX_DELAY & RX_DELAY_FIXED_BITS) << RX_DELAY_FIXED_LSB;
++ clkgen_write(clkgen, RX_DELAY, reg);
++
++ /* Default SD delay */
++ dev_dbg(clkgen->dev, "default sd delay %d\n", DEFAULT_SD_DELAY);
++ reg = (DEFAULT_SD_DELAY & SD_DELAY_STEPS_BITS) << SD_DELAY_STEPS_LSB;
++ clkgen_write(clkgen, SD_DELAY, reg);
++
++ /* We select freq, we turn on tx clock, we turn on sd clk,
++ * we pick clock generator mode
++ */
++ reg = USE_LOCAL_FREQ_SEL_BITS | USE_LOCAL_CARD_CLK_EN_BITS |
++ USE_LOCAL_CLK2CARD_ON_BITS | USE_LOCAL_CLK_GEN_SEL_BITS;
++ clkgen_write(clkgen, USE_LOCAL, reg);
++
++ /* Deassert reset. Reset bit is only writable bit of CS
++ * reg so fine to write a 0.
++ */
++ clkgen_write(clkgen, CS, 0);
++
++ return 0;
++}
++
++#define RUNNING \
++ (CS_TX_CLK_RUNNING_BITS | CS_RX_CLK_RUNNING_BITS | \
++ CS_SD_CLK_RUNNING_BITS)
++static int rp1_sdio_clk_is_prepared(struct clk_hw *hw)
++{
++ struct rp1_sdio_clkgen *clkgen =
++ container_of(hw, struct rp1_sdio_clkgen, hw);
++ u32 status;
++
++ dev_dbg(clkgen->dev, "is_prepared\n");
++ status = clkgen_read(clkgen, CS);
++ return ((status & RUNNING) == RUNNING);
++}
++
++/* Can define an additional divider if an sd card isn't working at full speed */
++/* #define SLOWDOWN 3 */
++
++static unsigned long rp1_sdio_clk_get_rate(struct clk_hw *hw,
++ unsigned long parent_rate)
++{
++ /* Get the current rate */
++ struct rp1_sdio_clkgen *clkgen =
++ container_of(hw, struct rp1_sdio_clkgen, hw);
++ unsigned long actual_rate = 0;
++ u32 ndiv_diva;
++ u32 ndiv_divb;
++ u32 tmp;
++ u32 div;
++
++ tmp = clkgen_read(clkgen, LOCAL);
++ if ((tmp & LOCAL_CLK2CARD_ON_BITS) == 0) {
++ dev_dbg(clkgen->dev, "get_rate 0\n");
++ return 0;
++ }
++
++ tmp = clkgen_read(clkgen, NDIV);
++ ndiv_diva = (tmp & NDIV_DIVA_BITS) >> NDIV_DIVA_LSB;
++ ndiv_divb = (tmp & NDIV_DIVB_BITS) >> NDIV_DIVB_LSB;
++ div = ndiv_diva * ndiv_divb;
++ actual_rate = (clk_get_rate(clkgen->base_clk) / div);
++
++#ifdef SLOWDOWN
++ actual_rate *= SLOWDOWN;
++#endif
++
++ dev_dbg(clkgen->dev, "get_rate. ndiv_diva %d, ndiv_divb %d = %lu\n",
++ ndiv_diva, ndiv_divb, actual_rate);
++
++ return actual_rate;
++}
++
++static int rp1_sdio_clk_set_rate(struct clk_hw *hw, unsigned long rate,
++ unsigned long parent_rate)
++{
++ struct rp1_sdio_clkgen *clkgen =
++ container_of(hw, struct rp1_sdio_clkgen, hw);
++ u32 div;
++ u32 reg;
++
++ dev_dbg(clkgen->dev, "set_rate %lu\n", rate);
++
++ if (rate == 0) {
++ /* Keep tx clock running */
++ clkgen_write(clkgen, LOCAL, LOCAL_CARD_CLK_EN_BITS);
++ return 0;
++ }
++
++#ifdef SLOWDOWN
++ rate /= SLOWDOWN;
++#endif
++
++ div = (clk_get_rate(clkgen->base_clk) / rate) - 1;
++ reg = LOCAL_CLK_GEN_SEL_BITS | LOCAL_CARD_CLK_EN_BITS |
++ LOCAL_CLK2CARD_ON_BITS | (div << LOCAL_FREQ_SEL_LSB);
++ clkgen_write(clkgen, LOCAL, reg);
++
++ return 0;
++}
++
++#define MAX_NDIV (256 * 8)
++static int rp1_sdio_clk_determine_rate(struct clk_hw *hw,
++ struct clk_rate_request *req)
++{
++ unsigned long rate;
++ struct rp1_sdio_clkgen *clkgen =
++ container_of(hw, struct rp1_sdio_clkgen, hw);
++ unsigned long base_rate = clk_get_rate(clkgen->base_clk);
++ u32 div;
++
++ /* What is the actual rate I can get if I request xyz */
++ if (req->rate) {
++ div = min((u32)(base_rate / req->rate), (u32)MAX_NDIV);
++ rate = base_rate / div;
++ req->rate = rate;
++ dev_dbg(clkgen->dev, "determine_rate %lu: %lu / %d = %lu\n",
++ req->rate, base_rate, div, rate);
++ } else {
++ rate = 0;
++ dev_dbg(clkgen->dev, "determine_rate %lu: %lu\n", req->rate,
++ rate);
++ }
++
++ return 0;
++}
++
++static const struct clk_ops rp1_sdio_clk_ops = {
++ .is_prepared = rp1_sdio_clk_is_prepared,
++ .recalc_rate = rp1_sdio_clk_get_rate,
++ .set_rate = rp1_sdio_clk_set_rate,
++ .determine_rate = rp1_sdio_clk_determine_rate,
++};
++
++static int rp1_sdio_clk_probe(struct platform_device *pdev)
++{
++ struct device_node *node = pdev->dev.of_node;
++ struct rp1_sdio_clkgen *clkgen;
++ void __iomem *regs;
++ struct clk_init_data init = {};
++ int ret;
++
++ clkgen = devm_kzalloc(&pdev->dev, sizeof(*clkgen), GFP_KERNEL);
++ if (!clkgen)
++ return -ENOMEM;
++ platform_set_drvdata(pdev, clkgen);
++
++ clkgen->dev = &pdev->dev;
++
++ /* Source freq */
++ clkgen->src_clk = devm_clk_get(&pdev->dev, "src");
++ if (IS_ERR(clkgen->src_clk)) {
++ int err = PTR_ERR(clkgen->src_clk);
++
++ dev_err(&pdev->dev, "failed to get src clk: %d\n", err);
++ return err;
++ }
++
++ /* Desired maximum output freq (i.e. base freq) */
++ clkgen->base_clk = devm_clk_get(&pdev->dev, "base");
++ if (IS_ERR(clkgen->base_clk)) {
++ int err = PTR_ERR(clkgen->base_clk);
++
++ dev_err(&pdev->dev, "failed to get base clk: %d\n", err);
++ return err;
++ }
++
++ regs = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ init.name = node->name;
++ init.ops = &rp1_sdio_clk_ops;
++ init.flags = CLK_GET_RATE_NOCACHE;
++
++ clkgen->hw.init = &init;
++ clkgen->regs = regs;
++
++ dev_info(&pdev->dev, "loaded %s\n", init.name);
++
++ ret = devm_clk_hw_register(&pdev->dev, &clkgen->hw);
++ if (ret)
++ return ret;
++
++ ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, &clkgen->hw);
++ if (ret)
++ return ret;
++
++ ret = rp1_sdio_clk_init(clkgen);
++ return ret;
++}
++
++static int rp1_sdio_clk_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static const struct of_device_id rp1_sdio_clk_dt_ids[] = {
++ { .compatible = "raspberrypi,rp1-sdio-clk", },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, rp1_sdio_clk_dt_ids);
++
++static struct platform_driver rp1_sdio_clk_driver = {
++ .probe = rp1_sdio_clk_probe,
++ .remove = rp1_sdio_clk_remove,
++ .driver = {
++ .name = "rp1-sdio-clk",
++ .of_match_table = rp1_sdio_clk_dt_ids,
++ },
++};
++module_platform_driver(rp1_sdio_clk_driver);
++
++MODULE_AUTHOR("Liam Fraser <liam@raspberrypi.com>");
++MODULE_DESCRIPTION("RP1 SDIO clock driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 50adadfaf324ed5cbb59ce2b85eda59de4e3801a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 4 Dec 2020 15:20:36 +0000
+Subject: [PATCH] i2c: designware: Add SMBUS quick command support
+
+The SMBUS emulation code turns an SMBUS quick command into a zero-
+length read. This controller can't do zero length accesses, but it
+can do quick commands, so reverse the emulation. The alternative
+would be to properly implement the SMBUS support but that is a lot
+more work, and unnecessary just to get i2cdetect working.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/i2c/busses/i2c-designware-core.h | 2 ++
+ drivers/i2c/busses/i2c-designware-master.c | 17 +++++++++++++++--
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-designware-core.h
++++ b/drivers/i2c/busses/i2c-designware-core.h
+@@ -117,7 +117,9 @@
+
+ #define DW_IC_ERR_TX_ABRT 0x1
+
++#define DW_IC_TAR_SPECIAL BIT(11)
+ #define DW_IC_TAR_10BITADDR_MASTER BIT(12)
++#define DW_IC_TAR_SMBUS_QUICK_CMD BIT(16)
+
+ #define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3))
+ #define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2)
+--- a/drivers/i2c/busses/i2c-designware-master.c
++++ b/drivers/i2c/busses/i2c-designware-master.c
+@@ -228,6 +228,10 @@ static void i2c_dw_xfer_init(struct dw_i
+ ic_tar = DW_IC_TAR_10BITADDR_MASTER;
+ }
+
++ /* Convert a zero-length read into an SMBUS quick command */
++ if (!msgs[dev->msg_write_idx].len)
++ ic_tar = DW_IC_TAR_SPECIAL | DW_IC_TAR_SMBUS_QUICK_CMD;
++
+ regmap_update_bits(dev->map, DW_IC_CON, DW_IC_CON_10BITADDR_MASTER,
+ ic_con);
+
+@@ -409,6 +413,14 @@ i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
+ regmap_read(dev->map, DW_IC_RXFLR, &flr);
+ rx_limit = dev->rx_fifo_depth - flr;
+
++ /* Handle SMBUS quick commands */
++ if (!buf_len) {
++ if (msgs[dev->msg_write_idx].flags & I2C_M_RD)
++ regmap_write(dev->map, DW_IC_DATA_CMD, 0x300);
++ else
++ regmap_write(dev->map, DW_IC_DATA_CMD, 0x200);
++ }
++
+ while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
+ u32 cmd = 0;
+
+@@ -673,7 +685,7 @@ static const struct i2c_algorithm i2c_dw
+ };
+
+ static const struct i2c_adapter_quirks i2c_dw_quirks = {
+- .flags = I2C_AQ_NO_ZERO_LEN,
++ .flags = 0,
+ };
+
+ static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
+@@ -813,7 +825,8 @@ void i2c_dw_configure_master(struct dw_i
+ {
+ struct i2c_timings *t = &dev->timings;
+
+- dev->functionality = I2C_FUNC_10BIT_ADDR | DW_IC_DEFAULT_FUNCTIONALITY;
++ dev->functionality = I2C_FUNC_10BIT_ADDR | I2C_FUNC_SMBUS_QUICK |
++ DW_IC_DEFAULT_FUNCTIONALITY;
+
+ dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
+ DW_IC_CON_RESTART_EN;
--- /dev/null
+From 0a1cd70189daec3baf4b4a233dd8e25ffbb9d512 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 28 Apr 2021 17:46:01 +0100
+Subject: [PATCH] dmaengine: dw-axi-dmac: Fixes for RP1
+
+Don't assume that DMA addresses of devices are the same as their
+physical addresses - convert correctly.
+
+The CFG2 register layout is used when there are more than 8 channels,
+but also when configured for more than 16 target peripheral devices
+because the index of the handshake signal has to be made wider.
+
+Reset the DMAC on probe
+
+The driver goes to the trouble of tracking when transfers have been
+paused, but then doesn't report that state when queried.
+
+Not having APB registers is not an error - for most use cases it's
+not even of interest, it's expected. Demote the message to debug level,
+which is disabled by default.
+
+Each channel has a descriptor pool, which is shared between transfers.
+It is unsafe to treat the total number of descriptors allocated from a
+pool as the number allocated to a specific transfer; doing so leads
+to releasing buffers that shouldn't be released and walking off the
+ends of descriptor lists. Instead, give each transfer descriptor its
+own count.
+
+Support partial transfers:
+Some use cases involve streaming from a device where the transfer only
+proceeds when the device's FIFO occupancy exceeds a certain threshold.
+In such cases (e.g. when pulling data from a UART) it is important to
+know how much data has been transferred so far, in order that remaining
+bytes can be read from the FIFO directly by software.
+
+Add the necessary code to provide this "residue" value with a finer,
+sub-transfer granularity.
+
+In order to prevent the occasional byte getting stuck in the DMA
+controller's internal buffers, restrict the destination memory width
+to the source register width.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../dma/dw-axi-dmac/dw-axi-dmac-platform.c | 136 +++++++++++++++---
+ drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 3 +
+ 2 files changed, 118 insertions(+), 21 deletions(-)
+
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+@@ -12,6 +12,7 @@
+ #include <linux/device.h>
+ #include <linux/dmaengine.h>
+ #include <linux/dmapool.h>
++#include <linux/dma-direct.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/err.h>
+ #include <linux/interrupt.h>
+@@ -79,6 +80,17 @@ axi_chan_iowrite64(struct axi_dma_chan *
+ iowrite32(upper_32_bits(val), chan->chan_regs + reg + 4);
+ }
+
++static inline u64
++axi_chan_ioread64(struct axi_dma_chan *chan, u32 reg)
++{
++ /*
++ * We split one 64 bit read into two 32 bit reads as some HW doesn't
++ * support 64 bit access.
++ */
++ return ((u64)ioread32(chan->chan_regs + reg + 4) << 32) +
++ ioread32(chan->chan_regs + reg);
++}
++
+ static inline void axi_chan_config_write(struct axi_dma_chan *chan,
+ struct axi_dma_chan_config *config)
+ {
+@@ -86,7 +98,7 @@ static inline void axi_chan_config_write
+
+ cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS |
+ config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
+- if (chan->chip->dw->hdata->reg_map_8_channels) {
++ if (!chan->chip->dw->hdata->reg_map_cfg2) {
+ cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS |
+ config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS |
+ config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS |
+@@ -214,7 +226,18 @@ static void axi_dma_hw_init(struct axi_d
+ {
+ int ret;
+ u32 i;
++ int retries = 1000;
+
++ axi_dma_iowrite32(chip, DMAC_RESET, 1);
++ while (axi_dma_ioread32(chip, DMAC_RESET)) {
++ retries--;
++ if (!retries) {
++ dev_err(chip->dev, "%s: DMAC failed to reset\n",
++ __func__);
++ return;
++ }
++ cpu_relax();
++ }
+ for (i = 0; i < chip->dw->hdata->nr_channels; i++) {
+ axi_chan_irq_disable(&chip->dw->chan[i], DWAXIDMAC_IRQ_ALL);
+ axi_chan_disable(&chip->dw->chan[i]);
+@@ -276,7 +299,7 @@ static struct axi_dma_lli *axi_desc_get(
+ static void axi_desc_put(struct axi_dma_desc *desc)
+ {
+ struct axi_dma_chan *chan = desc->chan;
+- int count = atomic_read(&chan->descs_allocated);
++ u32 count = desc->hw_desc_count;
+ struct axi_dma_hw_desc *hw_desc;
+ int descs_put;
+
+@@ -298,6 +321,48 @@ static void vchan_desc_put(struct virt_d
+ axi_desc_put(vd_to_axi_desc(vdesc));
+ }
+
++static u32 axi_dma_desc_src_pos(struct axi_dma_desc *desc, dma_addr_t addr)
++{
++ unsigned int idx = 0;
++ u32 pos = 0;
++
++ while (pos < desc->length) {
++ struct axi_dma_hw_desc *hw_desc = &desc->hw_desc[idx++];
++ u32 len = hw_desc->len;
++ dma_addr_t start = le64_to_cpu(hw_desc->lli->sar);
++
++ if (addr >= start && addr <= (start + len)) {
++ pos += addr - start;
++ break;
++ }
++
++ pos += len;
++ }
++
++ return pos;
++}
++
++static u32 axi_dma_desc_dst_pos(struct axi_dma_desc *desc, dma_addr_t addr)
++{
++ unsigned int idx = 0;
++ u32 pos = 0;
++
++ while (pos < desc->length) {
++ struct axi_dma_hw_desc *hw_desc = &desc->hw_desc[idx++];
++ u32 len = hw_desc->len;
++ dma_addr_t start = le64_to_cpu(hw_desc->lli->dar);
++
++ if (addr >= start && addr <= (start + len)) {
++ pos += addr - start;
++ break;
++ }
++
++ pos += len;
++ }
++
++ return pos;
++}
++
+ static enum dma_status
+ dma_chan_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+@@ -307,10 +372,7 @@ dma_chan_tx_status(struct dma_chan *dcha
+ enum dma_status status;
+ u32 completed_length;
+ unsigned long flags;
+- u32 completed_blocks;
+ size_t bytes = 0;
+- u32 length;
+- u32 len;
+
+ status = dma_cookie_status(dchan, cookie, txstate);
+ if (status == DMA_COMPLETE || !txstate)
+@@ -319,16 +381,31 @@ dma_chan_tx_status(struct dma_chan *dcha
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
+ vdesc = vchan_find_desc(&chan->vc, cookie);
+- if (vdesc) {
+- length = vd_to_axi_desc(vdesc)->length;
+- completed_blocks = vd_to_axi_desc(vdesc)->completed_blocks;
+- len = vd_to_axi_desc(vdesc)->hw_desc[0].len;
+- completed_length = completed_blocks * len;
+- bytes = length - completed_length;
++ if (vdesc && vdesc == vchan_next_desc(&chan->vc)) {
++ /* This descriptor is in-progress */
++ struct axi_dma_desc *desc = vd_to_axi_desc(vdesc);
++ dma_addr_t addr;
++
++ if (chan->direction == DMA_MEM_TO_DEV) {
++ addr = axi_chan_ioread64(chan, CH_SAR);
++ completed_length = axi_dma_desc_src_pos(desc, addr);
++ } else if (chan->direction == DMA_DEV_TO_MEM) {
++ addr = axi_chan_ioread64(chan, CH_DAR);
++ completed_length = axi_dma_desc_dst_pos(desc, addr);
++ } else {
++ completed_length = 0;
++ }
++ bytes = desc->length - completed_length;
++ } else if (vdesc) {
++ /* Still in the queue so not started */
++ bytes = vd_to_axi_desc(vdesc)->length;
+ }
+
+- spin_unlock_irqrestore(&chan->vc.lock, flags);
++ if (chan->is_paused && status == DMA_IN_PROGRESS)
++ status = DMA_PAUSED;
++
+ dma_set_residue(txstate, bytes);
++ spin_unlock_irqrestore(&chan->vc.lock, flags);
+
+ return status;
+ }
+@@ -516,7 +593,7 @@ static void dw_axi_dma_set_hw_channel(st
+ unsigned long reg_value, val;
+
+ if (!chip->apb_regs) {
+- dev_err(chip->dev, "apb_regs not initialized\n");
++ dev_dbg(chip->dev, "apb_regs not initialized\n");
+ return;
+ }
+
+@@ -620,18 +697,25 @@ static int dw_axi_dma_set_hw_desc(struct
+ switch (chan->direction) {
+ case DMA_MEM_TO_DEV:
+ reg_width = __ffs(chan->config.dst_addr_width);
+- device_addr = chan->config.dst_addr;
++ device_addr = phys_to_dma(chan->chip->dev, chan->config.dst_addr);
+ ctllo = reg_width << CH_CTL_L_DST_WIDTH_POS |
+ mem_width << CH_CTL_L_SRC_WIDTH_POS |
++ DWAXIDMAC_BURST_TRANS_LEN_1 << CH_CTL_L_DST_MSIZE_POS |
++ DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS |
+ DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_DST_INC_POS |
+ DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_SRC_INC_POS;
+ block_ts = len >> mem_width;
+ break;
+ case DMA_DEV_TO_MEM:
+ reg_width = __ffs(chan->config.src_addr_width);
+- device_addr = chan->config.src_addr;
++ /* Prevent partial access units getting lost */
++ if (mem_width > reg_width)
++ mem_width = reg_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 |
++ DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
++ DWAXIDMAC_BURST_TRANS_LEN_1 << CH_CTL_L_SRC_MSIZE_POS |
+ DWAXIDMAC_CH_CTL_L_INC << CH_CTL_L_DST_INC_POS |
+ DWAXIDMAC_CH_CTL_L_NOINC << CH_CTL_L_SRC_INC_POS;
+ block_ts = len >> reg_width;
+@@ -667,9 +751,6 @@ static int dw_axi_dma_set_hw_desc(struct
+ }
+
+ hw_desc->lli->block_ts_lo = cpu_to_le32(block_ts - 1);
+-
+- ctllo |= DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_DST_MSIZE_POS |
+- DWAXIDMAC_BURST_TRANS_LEN_4 << CH_CTL_L_SRC_MSIZE_POS;
+ hw_desc->lli->ctl_lo = cpu_to_le32(ctllo);
+
+ set_desc_src_master(hw_desc);
+@@ -764,6 +845,8 @@ dw_axi_dma_chan_prep_cyclic(struct dma_c
+ src_addr += segment_len;
+ }
+
++ desc->hw_desc_count = total_segments;
++
+ llp = desc->hw_desc[0].llp;
+
+ /* Managed transfer list */
+@@ -843,6 +926,8 @@ dw_axi_dma_chan_prep_slave_sg(struct dma
+ } while (len >= segment_len);
+ }
+
++ desc->hw_desc_count = loop;
++
+ /* Set end-of-link to the last link descriptor of list */
+ set_desc_last(&desc->hw_desc[num_sgs - 1]);
+
+@@ -950,6 +1035,8 @@ dma_chan_prep_dma_memcpy(struct dma_chan
+ num++;
+ }
+
++ desc->hw_desc_count = num;
++
+ /* Set end-of-link to the last link descriptor of list */
+ set_desc_last(&desc->hw_desc[num - 1]);
+ /* Managed transfer list */
+@@ -998,7 +1085,7 @@ static void axi_chan_dump_lli(struct axi
+ static void axi_chan_list_dump_lli(struct axi_dma_chan *chan,
+ struct axi_dma_desc *desc_head)
+ {
+- int count = atomic_read(&chan->descs_allocated);
++ u32 count = desc_head->hw_desc_count;
+ int i;
+
+ for (i = 0; i < count; i++)
+@@ -1041,11 +1128,11 @@ out:
+
+ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
+ {
+- int count = atomic_read(&chan->descs_allocated);
+ struct axi_dma_hw_desc *hw_desc;
+ struct axi_dma_desc *desc;
+ struct virt_dma_desc *vd;
+ unsigned long flags;
++ u32 count;
+ u64 llp;
+ int i;
+
+@@ -1067,6 +1154,7 @@ static void axi_chan_block_xfer_complete
+ if (chan->cyclic) {
+ desc = vd_to_axi_desc(vd);
+ if (desc) {
++ count = desc->hw_desc_count;
+ llp = lo_hi_readq(chan->chan_regs + CH_LLP);
+ for (i = 0; i < count; i++) {
+ hw_desc = &desc->hw_desc[i];
+@@ -1310,6 +1398,8 @@ static int parse_device_properties(struc
+ chip->dw->hdata->nr_channels = tmp;
+ if (tmp <= DMA_REG_MAP_CH_REF)
+ chip->dw->hdata->reg_map_8_channels = true;
++ else
++ chip->dw->hdata->reg_map_cfg2 = true;
+
+ ret = device_property_read_u32(dev, "snps,dma-masters", &tmp);
+ if (ret)
+@@ -1319,6 +1409,10 @@ static int parse_device_properties(struc
+
+ chip->dw->hdata->nr_masters = tmp;
+
++ ret = device_property_read_u32(dev, "snps,dma-targets", &tmp);
++ if (!ret && tmp > 16)
++ chip->dw->hdata->reg_map_cfg2 = true;
++
+ ret = device_property_read_u32(dev, "snps,data-width", &tmp);
+ if (ret)
+ return ret;
+--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
++++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+@@ -32,6 +32,8 @@ struct dw_axi_dma_hcfg {
+ u32 axi_rw_burst_len;
+ /* Register map for DMAX_NUM_CHANNELS <= 8 */
+ bool reg_map_8_channels;
++ /* Register map for DMAX_NUM_CHANNELS > 8 || DMAX_NUM_HS_IF > 16*/
++ bool reg_map_cfg2;
+ bool restrict_axi_burst_len;
+ };
+
+@@ -100,6 +102,7 @@ struct axi_dma_desc {
+
+ struct virt_dma_desc vd;
+ struct axi_dma_chan *chan;
++ u32 hw_desc_count;
+ u32 completed_blocks;
+ u32 length;
+ u32 period_len;
--- /dev/null
+From 8a9c0607ce0daa91c48faefd70ea73bda54ed0ae Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 29 Nov 2022 10:09:54 +0000
+Subject: [PATCH] spi: dw: Handle combined tx and rx messages
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi-dw-core.c | 12 +++++++++---
+ drivers/spi/spi-dw-mmio.c | 8 ++++++--
+ 2 files changed, 15 insertions(+), 5 deletions(-)
+
+--- a/drivers/spi/spi-dw-core.c
++++ b/drivers/spi/spi-dw-core.c
+@@ -244,8 +244,11 @@ static irqreturn_t dw_spi_transfer_handl
+ */
+ if (irq_status & DW_SPI_INT_TXEI) {
+ dw_writer(dws);
+- if (!dws->tx_len)
++ if (!dws->tx_len) {
+ dw_spi_mask_intr(dws, DW_SPI_INT_TXEI);
++ if (!dws->rx_len)
++ spi_finalize_current_transfer(dws->master);
++ }
+ }
+
+ return IRQ_HANDLED;
+@@ -372,8 +375,11 @@ static void dw_spi_irq_setup(struct dw_s
+
+ dws->transfer_handler = dw_spi_transfer_handler;
+
+- imask = DW_SPI_INT_TXEI | DW_SPI_INT_TXOI |
+- DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI;
++ imask = 0;
++ if (dws->tx_len)
++ imask |= DW_SPI_INT_TXEI | DW_SPI_INT_TXOI;
++ if (dws->rx_len)
++ imask |= DW_SPI_INT_RXUI | DW_SPI_INT_RXOI | DW_SPI_INT_RXFI;
+ dw_spi_umask_intr(dws, imask);
+ }
+
+--- a/drivers/spi/spi-dw-mmio.c
++++ b/drivers/spi/spi-dw-mmio.c
+@@ -20,6 +20,7 @@
+ #include <linux/property.h>
+ #include <linux/regmap.h>
+ #include <linux/reset.h>
++#include <linux/interrupt.h>
+
+ #include "spi-dw.h"
+
+@@ -280,8 +281,11 @@ static int dw_spi_mmio_probe(struct plat
+ dws->paddr = mem->start;
+
+ dws->irq = platform_get_irq(pdev, 0);
+- if (dws->irq < 0)
+- return dws->irq; /* -ENXIO */
++ if (dws->irq < 0) {
++ if (dws->irq != -ENXIO)
++ return dws->irq; /* -ENXIO */
++ dws->irq = IRQ_NOTCONNECTED;
++ }
+
+ dwsmmio->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dwsmmio->clk))
--- /dev/null
+From 824f18efc8ad59e2783570ae2df83e2cd16b9f04 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 14 Feb 2023 14:03:54 +0000
+Subject: [PATCH] pwm: Add support for RP1 PWM
+
+Add a driver for the RP1 PWM block.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../devicetree/bindings/pwm/pwm-rp1.yaml | 38 ++++
+ drivers/pwm/Kconfig | 9 +
+ drivers/pwm/Makefile | 1 +
+ drivers/pwm/pwm-rp1.c | 203 ++++++++++++++++++
+ 4 files changed, 251 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/pwm/pwm-rp1.yaml
+ create mode 100644 drivers/pwm/pwm-rp1.c
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/pwm/pwm-rp1.yaml
+@@ -0,0 +1,38 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/pwm/pwm-rp1.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Raspberry Pi RP1 PWM controller
++
++maintainers:
++ - Naushir Patuck <naush@raspberrypi.com>
++
++properties:
++ compatible:
++ enum:
++ - raspberrypi,rp1-pwm
++
++ reg:
++ maxItems: 1
++
++ "#pwm-cells":
++ const: 3
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - "#pwm-cells"
++
++additionalProperties: false
++
++examples:
++ - |
++ pwm0: pwm@98000 {
++ compatible = "raspberrypi,rp1-pwm";
++ reg = <0x0 0x98000 0x0 0x100>;
++ clocks = <&rp1_sys>;
++ #pwm-cells = <3>;
++ };
+--- a/drivers/pwm/Kconfig
++++ b/drivers/pwm/Kconfig
+@@ -451,6 +451,15 @@ config PWM_RASPBERRYPI_POE
+ Enable Raspberry Pi firmware controller PWM bus used to control the
+ official RPI PoE hat
+
++config PWM_RP1
++ tristate "RP1 PWM support"
++ depends on ARCH_BCM2835 || COMPILE_TEST
++ help
++ PWM framework driver for Raspberry Pi RP1 controller
++
++ To compile this driver as a module, choose M here: the module
++ will be called pwm-rp1.
++
+ config PWM_RCAR
+ tristate "Renesas R-Car PWM support"
+ depends on ARCH_RENESAS || COMPILE_TEST
+--- a/drivers/pwm/Makefile
++++ b/drivers/pwm/Makefile
+@@ -41,6 +41,7 @@ obj-$(CONFIG_PWM_OMAP_DMTIMER) += pwm-om
+ obj-$(CONFIG_PWM_PCA9685) += pwm-pca9685.o
+ obj-$(CONFIG_PWM_PXA) += pwm-pxa.o
+ obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o
++obj-$(CONFIG_PWM_RP1) += pwm-rp1.o
+ obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
+ obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
+ obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
+--- /dev/null
++++ b/drivers/pwm/pwm-rp1.c
+@@ -0,0 +1,203 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * pwm-rp1.c
++ *
++ * Raspberry Pi RP1 PWM.
++ *
++ * Copyright © 2023 Raspberry Pi Ltd.
++ *
++ * Author: Naushir Patuck (naush@raspberrypi.com)
++ *
++ * Based on the pwm-bcm2835 driver by:
++ * Bart Tanghe <bart.tanghe@thomasmore.be>
++ */
++
++#include <linux/bitops.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/platform_device.h>
++#include <linux/pwm.h>
++
++#define PWM_GLOBAL_CTRL 0x000
++#define PWM_CHANNEL_CTRL(x) (0x014 + ((x) * 16))
++#define PWM_RANGE(x) (0x018 + ((x) * 16))
++#define PWM_DUTY(x) (0x020 + ((x) * 16))
++
++/* 8:FIFO_POP_MASK + 0:Trailing edge M/S modulation */
++#define PWM_CHANNEL_DEFAULT (BIT(8) + BIT(0))
++#define PWM_CHANNEL_ENABLE(x) BIT(x)
++#define PWM_POLARITY BIT(3)
++#define SET_UPDATE BIT(31)
++#define PWM_MODE_MASK GENMASK(1, 0)
++
++struct rp1_pwm {
++ struct pwm_chip chip;
++ struct device *dev;
++ void __iomem *base;
++ struct clk *clk;
++};
++
++static inline struct rp1_pwm *to_rp1_pwm(struct pwm_chip *chip)
++{
++ return container_of(chip, struct rp1_pwm, chip);
++}
++
++static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct rp1_pwm *pc = to_rp1_pwm(chip);
++ u32 value;
++
++ value = readl(pc->base + PWM_GLOBAL_CTRL);
++ value |= SET_UPDATE;
++ writel(value, pc->base + PWM_GLOBAL_CTRL);
++}
++
++static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct rp1_pwm *pc = to_rp1_pwm(chip);
++
++ writel(PWM_CHANNEL_DEFAULT, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++ return 0;
++}
++
++static void rp1_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
++{
++ struct rp1_pwm *pc = to_rp1_pwm(chip);
++ u32 value;
++
++ value = readl(pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++ value &= ~PWM_MODE_MASK;
++ writel(value, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++ rp1_pwm_apply_config(chip, pwm);
++}
++
++static int rp1_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
++ const struct pwm_state *state)
++{
++ struct rp1_pwm *pc = to_rp1_pwm(chip);
++ unsigned long clk_rate = clk_get_rate(pc->clk);
++ unsigned long clk_period;
++ u32 value;
++
++ if (!clk_rate) {
++ dev_err(pc->dev, "failed to get clock rate\n");
++ return -EINVAL;
++ }
++
++ /* set period */
++ clk_period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, clk_rate);
++
++ writel(DIV_ROUND_CLOSEST(state->duty_cycle, clk_period),
++ pc->base + PWM_DUTY(pwm->hwpwm));
++
++ /* set duty cycle */
++ writel(DIV_ROUND_CLOSEST(state->period, clk_period),
++ pc->base + PWM_RANGE(pwm->hwpwm));
++
++ /* set polarity */
++ value = readl(pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++ if (state->polarity == PWM_POLARITY_NORMAL)
++ value &= ~PWM_POLARITY;
++ else
++ value |= PWM_POLARITY;
++ writel(value, pc->base + PWM_CHANNEL_CTRL(pwm->hwpwm));
++
++ /* enable/disable */
++ value = readl(pc->base + PWM_GLOBAL_CTRL);
++ if (state->enabled)
++ value |= PWM_CHANNEL_ENABLE(pwm->hwpwm);
++ else
++ value &= ~PWM_CHANNEL_ENABLE(pwm->hwpwm);
++ writel(value, pc->base + PWM_GLOBAL_CTRL);
++
++ rp1_pwm_apply_config(chip, pwm);
++
++ return 0;
++}
++
++static const struct pwm_ops rp1_pwm_ops = {
++ .request = rp1_pwm_request,
++ .free = rp1_pwm_free,
++ .apply = rp1_pwm_apply,
++ .owner = THIS_MODULE,
++};
++
++static int rp1_pwm_probe(struct platform_device *pdev)
++{
++ struct rp1_pwm *pc;
++ struct resource *res;
++ int ret;
++
++ pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
++ if (!pc)
++ return -ENOMEM;
++
++ pc->dev = &pdev->dev;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ pc->base = devm_ioremap_resource(&pdev->dev, res);
++ if (IS_ERR(pc->base))
++ return PTR_ERR(pc->base);
++
++ pc->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(pc->clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk),
++ "clock not found\n");
++
++ ret = clk_prepare_enable(pc->clk);
++ if (ret)
++ return ret;
++
++ pc->chip.dev = &pdev->dev;
++ pc->chip.ops = &rp1_pwm_ops;
++ pc->chip.base = -1;
++ pc->chip.npwm = 4;
++ pc->chip.of_xlate = of_pwm_xlate_with_flags;
++ pc->chip.of_pwm_n_cells = 3;
++
++ platform_set_drvdata(pdev, pc);
++
++ ret = pwmchip_add(&pc->chip);
++ if (ret < 0)
++ goto add_fail;
++
++ return 0;
++
++add_fail:
++ clk_disable_unprepare(pc->clk);
++ return ret;
++}
++
++static int rp1_pwm_remove(struct platform_device *pdev)
++{
++ struct rp1_pwm *pc = platform_get_drvdata(pdev);
++
++ clk_disable_unprepare(pc->clk);
++
++ pwmchip_remove(&pc->chip);
++
++ return 0;
++}
++
++static const struct of_device_id rp1_pwm_of_match[] = {
++ { .compatible = "raspberrypi,rp1-pwm" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
++
++static struct platform_driver rp1_pwm_driver = {
++ .driver = {
++ .name = "rpi-pwm",
++ .of_match_table = rp1_pwm_of_match,
++ },
++ .probe = rp1_pwm_probe,
++ .remove = rp1_pwm_remove,
++};
++module_platform_driver(rp1_pwm_driver);
++
++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com");
++MODULE_DESCRIPTION("RP1 PWM driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From f93caa69a9af6476cd4d93944a83acd227e68fd4 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Tue, 14 Feb 2023 14:58:33 +0000
+Subject: [PATCH] drm: Add RP1 DSI driver
+
+Add support for the RP1 DSI hardware.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/Kconfig | 2 +
+ drivers/gpu/drm/Makefile | 1 +
+ drivers/gpu/drm/rp1/Kconfig | 5 +
+ drivers/gpu/drm/rp1/Makefile | 4 +
+ drivers/gpu/drm/rp1/rp1-dsi/Kconfig | 15 +
+ drivers/gpu/drm/rp1/rp1-dsi/Makefile | 5 +
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c | 537 ++++++++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h | 94 ++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c | 443 ++++++
+ drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c | 1504 +++++++++++++++++++++
+ 10 files changed, 2610 insertions(+)
+ create mode 100644 drivers/gpu/drm/rp1/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
+
+--- a/drivers/gpu/drm/Kconfig
++++ b/drivers/gpu/drm/Kconfig
+@@ -384,6 +384,8 @@ source "drivers/gpu/drm/v3d/Kconfig"
+
+ source "drivers/gpu/drm/vc4/Kconfig"
+
++source "drivers/gpu/drm/rp1/Kconfig"
++
+ source "drivers/gpu/drm/etnaviv/Kconfig"
+
+ source "drivers/gpu/drm/hisilicon/Kconfig"
+--- a/drivers/gpu/drm/Makefile
++++ b/drivers/gpu/drm/Makefile
+@@ -148,3 +148,4 @@ obj-y += gud/
+ obj-$(CONFIG_DRM_HYPERV) += hyperv/
+ obj-y += solomon/
+ obj-$(CONFIG_DRM_SPRD) += sprd/
++obj-y += rp1/
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/Kconfig
+@@ -0,0 +1,5 @@
++source "drivers/gpu/drm/rp1/rp1-dsi/Kconfig"
++
++source "drivers/gpu/drm/rp1/rp1-dpi/Kconfig"
++
++source "drivers/gpu/drm/rp1/rp1-vec/Kconfig"
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/Makefile
+@@ -0,0 +1,4 @@
++obj-$(CONFIG_DRM_RP1_DSI) += rp1-dsi/
++obj-$(CONFIG_DRM_RP1_DPI) += rp1-dpi/
++obj-$(CONFIG_DRM_RP1_VEC) += rp1-vec/
++
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/Kconfig
+@@ -0,0 +1,15 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config DRM_RP1_DSI
++ tristate "DRM Support for RP1 DSI"
++ depends on DRM
++ select MFD_RP1
++ select DRM_GEM_DMA_HELPER
++ select DRM_KMS_HELPER
++ select DRM_MIPI_DSI
++ select DRM_VRAM_HELPER
++ select DRM_TTM
++ select DRM_TTM_HELPER
++ select GENERIC_PHY
++ select GENERIC_PHY_MIPI_DPHY
++ help
++ Choose this option to enable DSI display on RP1
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++drm-rp1-dsi-y := rp1_dsi.o rp1_dsi_dma.o rp1_dsi_dsi.o
++
++obj-$(CONFIG_DRM_RP1_DSI) += drm-rp1-dsi.o
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.c
+@@ -0,0 +1,537 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/clk.h>
++#include <linux/component.h>
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/phy/phy-mipi-dphy.h>
++#include <linux/string.h>
++
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_crtc.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_encoder.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_gem.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_gem_dma_helper.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++#include <drm/drm_managed.h>
++#include <drm/drm_modeset_helper_vtables.h>
++#include <drm/drm_of.h>
++#include <drm/drm_print.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_dsi.h"
++
++static inline struct rp1_dsi *
++bridge_to_rp1_dsi(struct drm_bridge *bridge)
++{
++ return container_of(bridge, struct rp1_dsi, bridge);
++}
++
++static void rp1_dsi_bridge_pre_enable(struct drm_bridge *bridge,
++ struct drm_bridge_state *old_state)
++{
++ struct rp1_dsi *dsi = bridge_to_rp1_dsi(bridge);
++
++ rp1dsi_dsi_setup(dsi, &dsi->pipe.crtc.state->adjusted_mode);
++}
++
++static void rp1_dsi_bridge_enable(struct drm_bridge *bridge,
++ struct drm_bridge_state *old_state)
++{
++}
++
++static void rp1_dsi_bridge_disable(struct drm_bridge *bridge,
++ struct drm_bridge_state *state)
++{
++}
++
++static void rp1_dsi_bridge_post_disable(struct drm_bridge *bridge,
++ struct drm_bridge_state *state)
++{
++ struct rp1_dsi *dsi = bridge_to_rp1_dsi(bridge);
++
++ if (dsi->dsi_running) {
++ rp1dsi_dsi_stop(dsi);
++ dsi->dsi_running = false;
++ }
++}
++
++static int rp1_dsi_bridge_attach(struct drm_bridge *bridge,
++ enum drm_bridge_attach_flags flags)
++{
++ struct rp1_dsi *dsi = bridge_to_rp1_dsi(bridge);
++
++ /* Attach the panel or bridge to the dsi bridge */
++ return drm_bridge_attach(bridge->encoder, dsi->out_bridge,
++ &dsi->bridge, flags);
++ return 0;
++}
++
++static const struct drm_bridge_funcs rp1_dsi_bridge_funcs = {
++ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
++ .atomic_reset = drm_atomic_helper_bridge_reset,
++ .atomic_pre_enable = rp1_dsi_bridge_pre_enable,
++ .atomic_enable = rp1_dsi_bridge_enable,
++ .atomic_disable = rp1_dsi_bridge_disable,
++ .atomic_post_disable = rp1_dsi_bridge_post_disable,
++ .attach = rp1_dsi_bridge_attach,
++};
++
++static void rp1dsi_pipe_update(struct drm_simple_display_pipe *pipe,
++ struct drm_plane_state *old_state)
++{
++ struct drm_pending_vblank_event *event;
++ unsigned long flags;
++ struct drm_framebuffer *fb = pipe->plane.state->fb;
++ struct rp1_dsi *dsi = pipe->crtc.dev->dev_private;
++ struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
++ struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
++ bool can_update = fb && dma_obj && dsi && dsi->pipe_enabled;
++
++ /* (Re-)start DSI,DMA where required; and update FB address */
++ if (can_update) {
++ if (!dsi->dma_running || fb->format->format != dsi->cur_fmt) {
++ if (dsi->dma_running && fb->format->format != dsi->cur_fmt) {
++ rp1dsi_dma_stop(dsi);
++ dsi->dma_running = false;
++ }
++ if (!dsi->dma_running) {
++ rp1dsi_dma_setup(dsi,
++ fb->format->format, dsi->display_format,
++ &pipe->crtc.state->adjusted_mode);
++ dsi->dma_running = true;
++ }
++ dsi->cur_fmt = fb->format->format;
++ drm_crtc_vblank_on(&pipe->crtc);
++ }
++ rp1dsi_dma_update(dsi, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
++ }
++
++ /* Arm VBLANK event (or call it immediately in some error cases) */
++ spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
++ event = pipe->crtc.state->event;
++ if (event) {
++ pipe->crtc.state->event = NULL;
++ if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
++ drm_crtc_arm_vblank_event(&pipe->crtc, event);
++ else
++ drm_crtc_send_vblank_event(&pipe->crtc, event);
++ }
++ spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
++}
++
++static inline struct rp1_dsi *
++encoder_to_rp1_dsi(struct drm_encoder *encoder)
++{
++ struct drm_simple_display_pipe *pipe =
++ container_of(encoder, struct drm_simple_display_pipe, encoder);
++ return container_of(pipe, struct rp1_dsi, pipe);
++}
++
++static void rp1dsi_encoder_enable(struct drm_encoder *encoder)
++{
++ struct rp1_dsi *dsi = encoder_to_rp1_dsi(encoder);
++
++ /* Put DSI into video mode before starting video */
++ rp1dsi_dsi_set_cmdmode(dsi, 0);
++
++ /* Start DMA -> DPI */
++ dsi->pipe_enabled = true;
++ dsi->cur_fmt = 0xdeadbeef;
++ rp1dsi_pipe_update(&dsi->pipe, 0);
++}
++
++static void rp1dsi_encoder_disable(struct drm_encoder *encoder)
++{
++ struct rp1_dsi *dsi = encoder_to_rp1_dsi(encoder);
++
++ drm_crtc_vblank_off(&dsi->pipe.crtc);
++ if (dsi->dma_running) {
++ rp1dsi_dma_stop(dsi);
++ dsi->dma_running = false;
++ }
++ dsi->pipe_enabled = false;
++
++ /* Return to command mode after stopping video */
++ rp1dsi_dsi_set_cmdmode(dsi, 1);
++}
++
++static const struct drm_encoder_helper_funcs rp1_dsi_encoder_funcs = {
++ .enable = rp1dsi_encoder_enable,
++ .disable = rp1dsi_encoder_disable,
++};
++
++static void rp1dsi_pipe_enable(struct drm_simple_display_pipe *pipe,
++ struct drm_crtc_state *crtc_state,
++ struct drm_plane_state *plane_state)
++{
++}
++
++static void rp1dsi_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++}
++
++static int rp1dsi_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
++{
++ struct rp1_dsi *dsi = pipe->crtc.dev->dev_private;
++
++ if (dsi)
++ rp1dsi_dma_vblank_ctrl(dsi, 1);
++
++ return 0;
++}
++
++static void rp1dsi_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
++{
++ struct rp1_dsi *dsi = pipe->crtc.dev->dev_private;
++
++ if (dsi)
++ rp1dsi_dma_vblank_ctrl(dsi, 0);
++}
++
++static const struct drm_simple_display_pipe_funcs rp1dsi_pipe_funcs = {
++ .enable = rp1dsi_pipe_enable,
++ .update = rp1dsi_pipe_update,
++ .disable = rp1dsi_pipe_disable,
++ .prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
++ .enable_vblank = rp1dsi_pipe_enable_vblank,
++ .disable_vblank = rp1dsi_pipe_disable_vblank,
++};
++
++static const struct drm_mode_config_funcs rp1dsi_mode_funcs = {
++ .fb_create = drm_gem_fb_create,
++ .atomic_check = drm_atomic_helper_check,
++ .atomic_commit = drm_atomic_helper_commit,
++};
++
++static const u32 rp1dsi_formats[] = {
++ DRM_FORMAT_XRGB8888,
++ DRM_FORMAT_XBGR8888,
++ DRM_FORMAT_RGB888,
++ DRM_FORMAT_BGR888,
++ DRM_FORMAT_RGB565
++};
++
++static void rp1dsi_stopall(struct drm_device *drm)
++{
++ if (drm->dev_private) {
++ struct rp1_dsi *dsi = drm->dev_private;
++
++ if (dsi->dma_running || rp1dsi_dma_busy(dsi)) {
++ rp1dsi_dma_stop(dsi);
++ dsi->dma_running = false;
++ }
++ if (dsi->dsi_running) {
++ rp1dsi_dsi_stop(dsi);
++ dsi->dsi_running = false;
++ }
++ if (dsi->clocks[RP1DSI_CLOCK_CFG])
++ clk_disable_unprepare(dsi->clocks[RP1DSI_CLOCK_CFG]);
++ }
++}
++
++DEFINE_DRM_GEM_DMA_FOPS(rp1dsi_fops);
++
++static struct drm_driver rp1dsi_driver = {
++ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
++ .fops = &rp1dsi_fops,
++ .name = "drm-rp1-dsi",
++ .desc = "drm-rp1-dsi",
++ .date = "0",
++ .major = 1,
++ .minor = 0,
++ DRM_GEM_DMA_DRIVER_OPS,
++ .release = rp1dsi_stopall,
++};
++
++static int rp1dsi_bind(struct rp1_dsi *dsi)
++{
++ struct platform_device *pdev = dsi->pdev;
++ struct drm_device *drm = dsi->drm;
++ int ret;
++
++ dsi->out_bridge = drmm_of_get_bridge(drm, pdev->dev.of_node, 0, 0);
++ if (IS_ERR(dsi->out_bridge))
++ return PTR_ERR(dsi->out_bridge);
++
++ ret = drmm_mode_config_init(drm);
++ if (ret)
++ goto rtn;
++
++ drm->mode_config.max_width = 4096;
++ drm->mode_config.max_height = 4096;
++ drm->mode_config.fb_base = 0;
++ drm->mode_config.preferred_depth = 32;
++ drm->mode_config.prefer_shadow = 0;
++ drm->mode_config.prefer_shadow_fbdev = 1;
++ drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
++ drm->mode_config.funcs = &rp1dsi_mode_funcs;
++ drm_vblank_init(drm, 1);
++
++ ret = drm_simple_display_pipe_init(drm,
++ &dsi->pipe,
++ &rp1dsi_pipe_funcs,
++ rp1dsi_formats,
++ ARRAY_SIZE(rp1dsi_formats),
++ NULL, NULL);
++ if (ret)
++ goto rtn;
++
++ /* We need slightly more complex encoder handling (enabling/disabling
++ * video mode), so add encoder helper functions.
++ */
++ drm_encoder_helper_add(&dsi->pipe.encoder, &rp1_dsi_encoder_funcs);
++
++ ret = drm_simple_display_pipe_attach_bridge(&dsi->pipe, &dsi->bridge);
++ if (ret)
++ goto rtn;
++
++ drm_bridge_add(&dsi->bridge);
++
++ drm_mode_config_reset(drm);
++
++ if (dsi->clocks[RP1DSI_CLOCK_CFG])
++ clk_prepare_enable(dsi->clocks[RP1DSI_CLOCK_CFG]);
++
++ ret = drm_dev_register(drm, 0);
++
++ if (ret == 0)
++ drm_fbdev_generic_setup(drm, 32);
++
++rtn:
++ if (ret)
++ dev_err(&pdev->dev, "%s returned %d\n", __func__, ret);
++ else
++ dev_info(&pdev->dev, "%s succeeded", __func__);
++
++ return ret;
++}
++
++static void rp1dsi_unbind(struct rp1_dsi *dsi)
++{
++ struct drm_device *drm = dsi->drm;
++
++ rp1dsi_stopall(drm);
++ drm_dev_unregister(drm);
++ drm_atomic_helper_shutdown(drm);
++}
++
++int rp1dsi_host_attach(struct mipi_dsi_host *host, struct mipi_dsi_device *dsi_dev)
++{
++ struct rp1_dsi *dsi = container_of(host, struct rp1_dsi, dsi_host);
++
++ dev_info(&dsi->pdev->dev, "%s: Attach DSI device name=%s channel=%d lanes=%d format=%d flags=0x%lx hs_rate=%lu lp_rate=%lu",
++ __func__, dsi_dev->name, dsi_dev->channel, dsi_dev->lanes,
++ dsi_dev->format, dsi_dev->mode_flags, dsi_dev->hs_rate,
++ dsi_dev->lp_rate);
++ dsi->vc = dsi_dev->channel & 3;
++ dsi->lanes = dsi_dev->lanes;
++
++ switch (dsi_dev->format) {
++ case MIPI_DSI_FMT_RGB666:
++ case MIPI_DSI_FMT_RGB666_PACKED:
++ case MIPI_DSI_FMT_RGB565:
++ case MIPI_DSI_FMT_RGB888:
++ break;
++ default:
++ return -EINVAL;
++ }
++ dsi->display_format = dsi_dev->format;
++ dsi->display_flags = dsi_dev->mode_flags;
++ dsi->display_hs_rate = dsi_dev->hs_rate;
++ dsi->display_lp_rate = dsi_dev->lp_rate;
++
++ /*
++ * Previously, we added a separate component to handle panel/bridge
++ * discovery and DRM registration, but now it's just a function call.
++ * The downstream/attaching device should deal with -EPROBE_DEFER
++ */
++ return rp1dsi_bind(dsi);
++}
++
++int rp1dsi_host_detach(struct mipi_dsi_host *host, struct mipi_dsi_device *dsi_dev)
++{
++ struct rp1_dsi *dsi = container_of(host, struct rp1_dsi, dsi_host);
++
++ /*
++ * Unregister the DRM driver.
++ * TODO: Check we are cleaning up correctly and not doing things multiple times!
++ */
++ rp1dsi_unbind(dsi);
++ return 0;
++}
++
++ssize_t rp1dsi_host_transfer(struct mipi_dsi_host *host, const struct mipi_dsi_msg *msg)
++{
++ struct rp1_dsi *dsi = container_of(host, struct rp1_dsi, dsi_host);
++ struct mipi_dsi_packet packet;
++ int ret = 0;
++
++ /* Write */
++ ret = mipi_dsi_create_packet(&packet, msg);
++ if (ret) {
++ dev_err(dsi->drm->dev, "RP1DSI: failed to create packet: %d\n", ret);
++ return ret;
++ }
++
++ rp1dsi_dsi_send(dsi, *(u32 *)(&packet.header), packet.payload_length, packet.payload);
++
++ /* Optional read back */
++ if (msg->rx_len && msg->rx_buf)
++ ret = rp1dsi_dsi_recv(dsi, msg->rx_len, msg->rx_buf);
++
++ return (ssize_t)ret;
++}
++
++static const struct mipi_dsi_host_ops rp1dsi_mipi_dsi_host_ops = {
++ .attach = rp1dsi_host_attach,
++ .detach = rp1dsi_host_detach,
++ .transfer = rp1dsi_host_transfer
++};
++
++static int rp1dsi_platform_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct drm_device *drm;
++ struct rp1_dsi *dsi;
++ int i, ret;
++
++ drm = drm_dev_alloc(&rp1dsi_driver, dev);
++ if (IS_ERR(drm)) {
++ ret = PTR_ERR(drm);
++ return ret;
++ }
++ dsi = drmm_kzalloc(drm, sizeof(*dsi), GFP_KERNEL);
++ if (!dsi) {
++ ret = -ENOMEM;
++ goto err_free_drm;
++ }
++ init_completion(&dsi->finished);
++ dsi->drm = drm;
++ dsi->pdev = pdev;
++ drm->dev_private = dsi;
++ platform_set_drvdata(pdev, drm);
++
++ dsi->bridge.funcs = &rp1_dsi_bridge_funcs;
++ dsi->bridge.of_node = dev->of_node;
++ dsi->bridge.type = DRM_MODE_CONNECTOR_DSI;
++
++ /* Safe default values for DSI mode */
++ dsi->lanes = 1;
++ dsi->display_format = MIPI_DSI_FMT_RGB888;
++ dsi->display_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_LPM;
++
++ /* Hardware resources */
++ for (i = 0; i < RP1DSI_NUM_CLOCKS; i++) {
++ static const char * const myclocknames[RP1DSI_NUM_CLOCKS] = {
++ "cfgclk", "dpiclk", "byteclk", "refclk"
++ };
++ dsi->clocks[i] = devm_clk_get(dev, myclocknames[i]);
++ if (IS_ERR(dsi->clocks[i])) {
++ ret = PTR_ERR(dsi->clocks[i]);
++ dev_err(dev, "Error getting clocks[%d]\n", i);
++ goto err_free_drm;
++ }
++ }
++
++ for (i = 0; i < RP1DSI_NUM_HW_BLOCKS; i++) {
++ dsi->hw_base[i] =
++ devm_ioremap_resource(dev,
++ platform_get_resource(dsi->pdev,
++ IORESOURCE_MEM,
++ i));
++ if (IS_ERR(dsi->hw_base[i])) {
++ ret = PTR_ERR(dsi->hw_base[i]);
++ dev_err(dev, "Error memory mapping regs[%d]\n", i);
++ goto err_free_drm;
++ }
++ }
++ ret = platform_get_irq(dsi->pdev, 0);
++ if (ret > 0)
++ ret = devm_request_irq(dev, ret, rp1dsi_dma_isr,
++ IRQF_SHARED, "rp1-dsi", dsi);
++ if (ret) {
++ dev_err(dev, "Unable to request interrupt\n");
++ ret = -EINVAL;
++ goto err_free_drm;
++ }
++ rp1dsi_mipicfg_setup(dsi);
++ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
++
++ /* Create the MIPI DSI Host and wait for the panel/bridge to attach to it */
++ dsi->dsi_host.ops = &rp1dsi_mipi_dsi_host_ops;
++ dsi->dsi_host.dev = dev;
++ ret = mipi_dsi_host_register(&dsi->dsi_host);
++ if (ret)
++ goto err_free_drm;
++
++ return ret;
++
++err_free_drm:
++ dev_err(dev, "%s fail %d\n", __func__, ret);
++ drm_dev_put(drm);
++ return ret;
++}
++
++static int rp1dsi_platform_remove(struct platform_device *pdev)
++{
++ struct drm_device *drm = platform_get_drvdata(pdev);
++ struct rp1_dsi *dsi = drm->dev_private;
++
++ mipi_dsi_host_unregister(&dsi->dsi_host);
++ return 0;
++}
++
++static void rp1dsi_platform_shutdown(struct platform_device *pdev)
++{
++ struct drm_device *drm = platform_get_drvdata(pdev);
++
++ rp1dsi_stopall(drm);
++}
++
++static const struct of_device_id rp1dsi_of_match[] = {
++ {
++ .compatible = "raspberrypi,rp1dsi",
++ },
++ { /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rp1dsi_of_match);
++
++static struct platform_driver rp1dsi_platform_driver = {
++ .probe = rp1dsi_platform_probe,
++ .remove = rp1dsi_platform_remove,
++ .shutdown = rp1dsi_platform_shutdown,
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .of_match_table = rp1dsi_of_match,
++ },
++};
++
++module_platform_driver(rp1dsi_platform_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("MIPI DSI driver for Raspberry Pi RP1");
++MODULE_AUTHOR("Nick Hollinghurst");
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi.h
+@@ -0,0 +1,94 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++#ifndef _RP1_DSI_H_
++#define _RP1_DSI_H_
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/types.h>
++
++#include <drm/drm_bridge.h>
++#include <drm/drm_device.h>
++#include <drm/drm_mipi_dsi.h>
++#include <drm/drm_simple_kms_helper.h>
++
++#define MODULE_NAME "drm-rp1-dsi"
++#define DRIVER_NAME "drm-rp1-dsi"
++
++/* ---------------------------------------------------------------------- */
++
++#define RP1DSI_HW_BLOCK_DMA 0
++#define RP1DSI_HW_BLOCK_DSI 1
++#define RP1DSI_HW_BLOCK_CFG 2
++#define RP1DSI_NUM_HW_BLOCKS 3
++
++#define RP1DSI_CLOCK_CFG 0
++#define RP1DSI_CLOCK_DPI 1
++#define RP1DSI_CLOCK_BYTE 2
++#define RP1DSI_CLOCK_REF 3
++#define RP1DSI_NUM_CLOCKS 4
++
++/* ---------------------------------------------------------------------- */
++
++struct rp1_dsi {
++ /* DRM and platform device pointers */
++ struct drm_device *drm;
++ struct platform_device *pdev;
++
++ /* Framework and helper objects */
++ struct drm_simple_display_pipe pipe;
++ struct drm_bridge bridge;
++ struct drm_bridge *out_bridge;
++ struct mipi_dsi_host dsi_host;
++
++ /* Clocks. We need DPI clock; the others are frequency references */
++ struct clk *clocks[RP1DSI_NUM_CLOCKS];
++
++ /* Block (DSI DMA, DSI Host) base addresses, and current state */
++ void __iomem *hw_base[RP1DSI_NUM_HW_BLOCKS];
++ u32 cur_fmt;
++ bool dsi_running, dma_running, pipe_enabled;
++ struct completion finished;
++
++ /* Attached display parameters (from mipi_dsi_device) */
++ unsigned long display_flags, display_hs_rate, display_lp_rate;
++ enum mipi_dsi_pixel_format display_format;
++ u8 vc;
++ u8 lanes;
++
++ /* DPHY */
++ u8 hsfreq_index;
++};
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the DSI/DPI/DMA block */
++
++void rp1dsi_dma_setup(struct rp1_dsi *dsi,
++ u32 in_format, enum mipi_dsi_pixel_format out_format,
++ struct drm_display_mode const *mode);
++void rp1dsi_dma_update(struct rp1_dsi *dsi, dma_addr_t addr, u32 offset, u32 stride);
++void rp1dsi_dma_stop(struct rp1_dsi *dsi);
++int rp1dsi_dma_busy(struct rp1_dsi *dsi);
++irqreturn_t rp1dsi_dma_isr(int irq, void *dev);
++void rp1dsi_dma_vblank_ctrl(struct rp1_dsi *dsi, int enable);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the MIPICFG block and check RP1 platform */
++
++void rp1dsi_mipicfg_setup(struct rp1_dsi *dsi);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the SNPS D-PHY and DSI block setup */
++
++void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode);
++void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 header, int len, const u8 *buf);
++int rp1dsi_dsi_recv(struct rp1_dsi *dsi, int len, u8 *buf);
++void rp1dsi_dsi_set_cmdmode(struct rp1_dsi *dsi, int cmd_mode);
++void rp1dsi_dsi_stop(struct rp1_dsi *dsi);
++
++#endif
++
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dma.c
+@@ -0,0 +1,443 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_dsi.h"
++
++// --- DPI DMA REGISTERS (derived from Argon firmware, via RP1 drivers/mipi, with corrections) ---
++
++// Control
++#define DPI_DMA_CONTROL 0x0
++#define DPI_DMA_CONTROL_ARM_SHIFT 0
++#define DPI_DMA_CONTROL_ARM_MASK BIT(DPI_DMA_CONTROL_ARM_SHIFT)
++#define DPI_DMA_CONTROL_ALIGN16_SHIFT 2
++#define DPI_DMA_CONTROL_ALIGN16_MASK BIT(DPI_DMA_CONTROL_ALIGN16_SHIFT)
++#define DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT 1
++#define DPI_DMA_CONTROL_AUTO_REPEAT_MASK BIT(DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT)
++#define DPI_DMA_CONTROL_HIGH_WATER_SHIFT 3
++#define DPI_DMA_CONTROL_HIGH_WATER_MASK (0x1FF << DPI_DMA_CONTROL_HIGH_WATER_SHIFT)
++#define DPI_DMA_CONTROL_DEN_POL_SHIFT 12
++#define DPI_DMA_CONTROL_DEN_POL_MASK BIT(DPI_DMA_CONTROL_DEN_POL_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_POL_SHIFT 13
++#define DPI_DMA_CONTROL_HSYNC_POL_MASK BIT(DPI_DMA_CONTROL_HSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_POL_SHIFT 14
++#define DPI_DMA_CONTROL_VSYNC_POL_MASK BIT(DPI_DMA_CONTROL_VSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_COLORM_SHIFT 15
++#define DPI_DMA_CONTROL_COLORM_MASK BIT(DPI_DMA_CONTROL_COLORM_SHIFT)
++#define DPI_DMA_CONTROL_SHUTDN_SHIFT 16
++#define DPI_DMA_CONTROL_SHUTDN_MASK BIT(DPI_DMA_CONTROL_SHUTDN_SHIFT)
++#define DPI_DMA_CONTROL_HBP_EN_SHIFT 17
++#define DPI_DMA_CONTROL_HBP_EN_MASK BIT(DPI_DMA_CONTROL_HBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HFP_EN_SHIFT 18
++#define DPI_DMA_CONTROL_HFP_EN_MASK BIT(DPI_DMA_CONTROL_HFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VBP_EN_SHIFT 19
++#define DPI_DMA_CONTROL_VBP_EN_MASK BIT(DPI_DMA_CONTROL_VBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VFP_EN_SHIFT 20
++#define DPI_DMA_CONTROL_VFP_EN_MASK BIT(DPI_DMA_CONTROL_VFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_EN_SHIFT 21
++#define DPI_DMA_CONTROL_HSYNC_EN_MASK BIT(DPI_DMA_CONTROL_HSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_EN_SHIFT 22
++#define DPI_DMA_CONTROL_VSYNC_EN_MASK BIT(DPI_DMA_CONTROL_VSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_IMMED_SHIFT 23
++#define DPI_DMA_CONTROL_FORCE_IMMED_MASK BIT(DPI_DMA_CONTROL_FORCE_IMMED_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT 24
++#define DPI_DMA_CONTROL_FORCE_DRAIN_MASK BIT(DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT 25
++#define DPI_DMA_CONTROL_FORCE_EMPTY_MASK BIT(DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT)
++
++// IRQ_ENABLES
++#define DPI_DMA_IRQ_EN 0x04
++#define DPI_DMA_IRQ_EN_DMA_READY_SHIFT 0
++#define DPI_DMA_IRQ_EN_DMA_READY_MASK BIT(DPI_DMA_IRQ_EN_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT 1
++#define DPI_DMA_IRQ_EN_UNDERFLOW_MASK BIT(DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_EN_FRAME_START_SHIFT 2
++#define DPI_DMA_IRQ_EN_FRAME_START_MASK BIT(DPI_DMA_IRQ_EN_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT 3
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_MASK BIT(DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_EN_TE_SHIFT 4
++#define DPI_DMA_IRQ_EN_TE_MASK BIT(DPI_DMA_IRQ_EN_TE_SHIFT)
++#define DPI_DMA_IRQ_EN_ERROR_SHIFT 5
++#define DPI_DMA_IRQ_EN_ERROR_MASK BIT(DPI_DMA_IRQ_EN_ERROR_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_SHIFT 6
++#define DPI_DMA_IRQ_EN_MATCH_MASK BIT(DPI_DMA_IRQ_EN_MATCH_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT 16
++#define DPI_DMA_IRQ_EN_MATCH_LINE_MASK (0xFFF << DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT)
++
++// IRQ_FLAGS
++#define DPI_DMA_IRQ_FLAGS 0x08
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT 0
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_MASK BIT(DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT 1
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK BIT(DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT 2
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_MASK BIT(DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT 3
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK BIT(DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_TE_SHIFT 4
++#define DPI_DMA_IRQ_FLAGS_TE_MASK BIT(DPI_DMA_IRQ_FLAGS_TE_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_ERROR_SHIFT 5
++#define DPI_DMA_IRQ_FLAGS_ERROR_MASK BIT(DPI_DMA_IRQ_FLAGS_ERROR_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_MATCH_SHIFT 6
++#define DPI_DMA_IRQ_FLAGS_MATCH_MASK BIT(DPI_DMA_IRQ_FLAGS_MATCH_SHIFT)
++
++// QOS
++#define DPI_DMA_QOS 0xC
++#define DPI_DMA_QOS_DQOS_SHIFT 0
++#define DPI_DMA_QOS_DQOS_MASK (0xF << DPI_DMA_QOS_DQOS_SHIFT)
++#define DPI_DMA_QOS_ULEV_SHIFT 4
++#define DPI_DMA_QOS_ULEV_MASK (0xF << DPI_DMA_QOS_ULEV_SHIFT)
++#define DPI_DMA_QOS_UQOS_SHIFT 8
++#define DPI_DMA_QOS_UQOS_MASK (0xF << DPI_DMA_QOS_UQOS_SHIFT)
++#define DPI_DMA_QOS_LLEV_SHIFT 12
++#define DPI_DMA_QOS_LLEV_MASK (0xF << DPI_DMA_QOS_LLEV_SHIFT)
++#define DPI_DMA_QOS_LQOS_SHIFT 16
++#define DPI_DMA_QOS_LQOS_MASK (0xF << DPI_DMA_QOS_LQOS_SHIFT)
++
++// Panics
++#define DPI_DMA_PANICS 0x38
++#define DPI_DMA_PANICS_UPPER_COUNT_SHIFT 0
++#define DPI_DMA_PANICS_UPPER_COUNT_MASK \
++ (0x0000FFFF << DPI_DMA_PANICS_UPPER_COUNT_SHIFT)
++#define DPI_DMA_PANICS_LOWER_COUNT_SHIFT 16
++#define DPI_DMA_PANICS_LOWER_COUNT_MASK \
++ (0x0000FFFF << DPI_DMA_PANICS_LOWER_COUNT_SHIFT)
++
++// DMA Address Lower:
++#define DPI_DMA_DMA_ADDR_L 0x10
++
++// DMA Address Upper:
++#define DPI_DMA_DMA_ADDR_H 0x40
++
++// DMA stride
++#define DPI_DMA_DMA_STRIDE 0x14
++
++// Visible Area
++#define DPI_DMA_VISIBLE_AREA 0x18
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT 0
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_MASK (0x0FFF << DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT)
++#define DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT 16
++#define DPI_DMA_VISIBLE_AREA_COLSM1_MASK (0x0FFF << DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT)
++
++// Sync width
++#define DPI_DMA_SYNC_WIDTH 0x1C
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT 0
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_MASK (0x0FFF << DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT)
++#define DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT 16
++#define DPI_DMA_SYNC_WIDTH_COLSM1_MASK (0x0FFF << DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT)
++
++// Back porch
++#define DPI_DMA_BACK_PORCH 0x20
++#define DPI_DMA_BACK_PORCH_ROWSM1_SHIFT 0
++#define DPI_DMA_BACK_PORCH_ROWSM1_MASK (0x0FFF << DPI_DMA_BACK_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_BACK_PORCH_COLSM1_SHIFT 16
++#define DPI_DMA_BACK_PORCH_COLSM1_MASK (0x0FFF << DPI_DMA_BACK_PORCH_COLSM1_SHIFT)
++
++// Front porch
++#define DPI_DMA_FRONT_PORCH 0x24
++#define DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT 0
++#define DPI_DMA_FRONT_PORCH_ROWSM1_MASK (0x0FFF << DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_FRONT_PORCH_COLSM1_SHIFT 16
++#define DPI_DMA_FRONT_PORCH_COLSM1_MASK (0x0FFF << DPI_DMA_FRONT_PORCH_COLSM1_SHIFT)
++
++// Input masks
++#define DPI_DMA_IMASK 0x2C
++#define DPI_DMA_IMASK_R_SHIFT 0
++#define DPI_DMA_IMASK_R_MASK (0x3FF << DPI_DMA_IMASK_R_SHIFT)
++#define DPI_DMA_IMASK_G_SHIFT 10
++#define DPI_DMA_IMASK_G_MASK (0x3FF << DPI_DMA_IMASK_G_SHIFT)
++#define DPI_DMA_IMASK_B_SHIFT 20
++#define DPI_DMA_IMASK_B_MASK (0x3FF << DPI_DMA_IMASK_B_SHIFT)
++
++// Output Masks
++#define DPI_DMA_OMASK 0x30
++#define DPI_DMA_OMASK_R_SHIFT 0
++#define DPI_DMA_OMASK_R_MASK (0x3FF << DPI_DMA_OMASK_R_SHIFT)
++#define DPI_DMA_OMASK_G_SHIFT 10
++#define DPI_DMA_OMASK_G_MASK (0x3FF << DPI_DMA_OMASK_G_SHIFT)
++#define DPI_DMA_OMASK_B_SHIFT 20
++#define DPI_DMA_OMASK_B_MASK (0x3FF << DPI_DMA_OMASK_B_SHIFT)
++
++// Shifts
++#define DPI_DMA_SHIFT 0x28
++#define DPI_DMA_SHIFT_IR_SHIFT 0
++#define DPI_DMA_SHIFT_IR_MASK (0x1F << DPI_DMA_SHIFT_IR_SHIFT)
++#define DPI_DMA_SHIFT_IG_SHIFT 5
++#define DPI_DMA_SHIFT_IG_MASK (0x1F << DPI_DMA_SHIFT_IG_SHIFT)
++#define DPI_DMA_SHIFT_IB_SHIFT 10
++#define DPI_DMA_SHIFT_IB_MASK (0x1F << DPI_DMA_SHIFT_IB_SHIFT)
++#define DPI_DMA_SHIFT_OR_SHIFT 15
++#define DPI_DMA_SHIFT_OR_MASK (0x1F << DPI_DMA_SHIFT_OR_SHIFT)
++#define DPI_DMA_SHIFT_OG_SHIFT 20
++#define DPI_DMA_SHIFT_OG_MASK (0x1F << DPI_DMA_SHIFT_OG_SHIFT)
++#define DPI_DMA_SHIFT_OB_SHIFT 25
++#define DPI_DMA_SHIFT_OB_MASK (0x1F << DPI_DMA_SHIFT_OB_SHIFT)
++
++// Scaling
++#define DPI_DMA_RGBSZ 0x34
++#define DPI_DMA_RGBSZ_BPP_SHIFT 16
++#define DPI_DMA_RGBSZ_BPP_MASK (0x3 << DPI_DMA_RGBSZ_BPP_SHIFT)
++#define DPI_DMA_RGBSZ_R_SHIFT 0
++#define DPI_DMA_RGBSZ_R_MASK (0xF << DPI_DMA_RGBSZ_R_SHIFT)
++#define DPI_DMA_RGBSZ_G_SHIFT 4
++#define DPI_DMA_RGBSZ_G_MASK (0xF << DPI_DMA_RGBSZ_G_SHIFT)
++#define DPI_DMA_RGBSZ_B_SHIFT 8
++#define DPI_DMA_RGBSZ_B_MASK (0xF << DPI_DMA_RGBSZ_B_SHIFT)
++
++// Status
++#define DPI_DMA_STATUS 0x3c
++
++#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
++
++static unsigned int rp1dsi_dma_read(struct rp1_dsi *dsi, unsigned int reg)
++{
++ void __iomem *addr = dsi->hw_base[RP1DSI_HW_BLOCK_DMA] + reg;
++
++ return readl(addr);
++}
++
++static void rp1dsi_dma_write(struct rp1_dsi *dsi, unsigned int reg, unsigned int val)
++{
++ void __iomem *addr = dsi->hw_base[RP1DSI_HW_BLOCK_DMA] + reg;
++
++ writel(val, addr);
++}
++
++int rp1dsi_dma_busy(struct rp1_dsi *dsi)
++{
++ return (rp1dsi_dma_read(dsi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0;
++}
++
++/* Table of supported input (in-memory/DMA) pixel formats. */
++struct rp1dsi_ipixfmt {
++ u32 format; /* DRM format code */
++ u32 mask; /* RGB masks (10 bits each, left justified) */
++ u32 shift; /* RGB MSB positions in the memory word */
++ u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */
++};
++
++#define IMASK_RGB(r, g, b) (BITS(DPI_DMA_IMASK_R, r) | \
++ BITS(DPI_DMA_IMASK_G, g) | \
++ BITS(DPI_DMA_IMASK_B, b))
++#define ISHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_IR, r) | \
++ BITS(DPI_DMA_SHIFT_IG, g) | \
++ BITS(DPI_DMA_SHIFT_IB, b))
++
++static const struct rp1dsi_ipixfmt my_formats[] = {
++ {
++ .format = DRM_FORMAT_XRGB8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
++ .format = DRM_FORMAT_XBGR8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
++ .format = DRM_FORMAT_RGB888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
++ },
++ {
++ .format = DRM_FORMAT_BGR888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
++ },
++ {
++ .format = DRM_FORMAT_RGB565,
++ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
++ .shift = ISHIFT_RGB(15, 10, 4),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
++ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
++ }
++};
++
++/* Choose the internal on-the-bus DPI format as expected by DSI Host. */
++static u32 get_omask_oshift(enum mipi_dsi_pixel_format fmt, u32 *oshift)
++{
++ switch (fmt) {
++ case MIPI_DSI_FMT_RGB565:
++ *oshift = BITS(DPI_DMA_SHIFT_OR, 15) |
++ BITS(DPI_DMA_SHIFT_OG, 10) |
++ BITS(DPI_DMA_SHIFT_OB, 4);
++ return BITS(DPI_DMA_OMASK_R, 0x3e0) |
++ BITS(DPI_DMA_OMASK_G, 0x3f0) |
++ BITS(DPI_DMA_OMASK_B, 0x3e0);
++ case MIPI_DSI_FMT_RGB666_PACKED:
++ *oshift = BITS(DPI_DMA_SHIFT_OR, 17) |
++ BITS(DPI_DMA_SHIFT_OG, 11) |
++ BITS(DPI_DMA_SHIFT_OB, 5);
++ return BITS(DPI_DMA_OMASK_R, 0x3f0) |
++ BITS(DPI_DMA_OMASK_G, 0x3f0) |
++ BITS(DPI_DMA_OMASK_B, 0x3f0);
++ case MIPI_DSI_FMT_RGB666:
++ *oshift = BITS(DPI_DMA_SHIFT_OR, 21) |
++ BITS(DPI_DMA_SHIFT_OG, 13) |
++ BITS(DPI_DMA_SHIFT_OB, 5);
++ return BITS(DPI_DMA_OMASK_R, 0x3f0) |
++ BITS(DPI_DMA_OMASK_G, 0x3f0) |
++ BITS(DPI_DMA_OMASK_B, 0x3f0);
++ default:
++ *oshift = BITS(DPI_DMA_SHIFT_OR, 23) |
++ BITS(DPI_DMA_SHIFT_OG, 15) |
++ BITS(DPI_DMA_SHIFT_OB, 7);
++ return BITS(DPI_DMA_OMASK_R, 0x3fc) |
++ BITS(DPI_DMA_OMASK_G, 0x3fc) |
++ BITS(DPI_DMA_OMASK_B, 0x3fc);
++ }
++}
++
++void rp1dsi_dma_setup(struct rp1_dsi *dsi,
++ u32 in_format, enum mipi_dsi_pixel_format out_format,
++ struct drm_display_mode const *mode)
++{
++ u32 oshift;
++ int i;
++
++ /*
++ * Configure all DSI/DPI/DMA block registers, except base address.
++ * DMA will not actually start until a FB base address is specified
++ * using rp1dsi_dma_update().
++ */
++
++ rp1dsi_dma_write(dsi, DPI_DMA_VISIBLE_AREA,
++ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
++ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
++
++ rp1dsi_dma_write(dsi, DPI_DMA_SYNC_WIDTH,
++ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
++ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
++
++ /* In the DPIDMA registers, "back porch" time includes sync width */
++ rp1dsi_dma_write(dsi, DPI_DMA_BACK_PORCH,
++ BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
++ BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
++
++ rp1dsi_dma_write(dsi, DPI_DMA_FRONT_PORCH,
++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
++ BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
++
++ /* Input to output pixel format conversion */
++ for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
++ if (my_formats[i].format == in_format)
++ break;
++ }
++ if (i >= ARRAY_SIZE(my_formats)) {
++ drm_err(dsi->drm, "%s: bad input format\n", __func__);
++ i = 0;
++ }
++ rp1dsi_dma_write(dsi, DPI_DMA_IMASK, my_formats[i].mask);
++ rp1dsi_dma_write(dsi, DPI_DMA_OMASK, get_omask_oshift(out_format, &oshift));
++ rp1dsi_dma_write(dsi, DPI_DMA_SHIFT, my_formats[i].shift | oshift);
++ if (out_format == MIPI_DSI_FMT_RGB888)
++ rp1dsi_dma_write(dsi, DPI_DMA_RGBSZ, my_formats[i].rgbsz);
++ else
++ rp1dsi_dma_write(dsi, DPI_DMA_RGBSZ, my_formats[i].rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
++
++ rp1dsi_dma_write(dsi, DPI_DMA_QOS,
++ BITS(DPI_DMA_QOS_DQOS, 0x0) |
++ BITS(DPI_DMA_QOS_ULEV, 0xb) |
++ BITS(DPI_DMA_QOS_UQOS, 0x2) |
++ BITS(DPI_DMA_QOS_LLEV, 0x8) |
++ BITS(DPI_DMA_QOS_LQOS, 0x7));
++
++ rp1dsi_dma_write(dsi, DPI_DMA_IRQ_FLAGS, -1);
++ rp1dsi_dma_vblank_ctrl(dsi, 1);
++
++ i = rp1dsi_dma_busy(dsi);
++ if (i)
++ drm_err(dsi->drm, "RP1DSI: Unexpectedly busy at start!");
++
++ rp1dsi_dma_write(dsi, DPI_DMA_CONTROL,
++ BITS(DPI_DMA_CONTROL_ARM, (i == 0)) |
++ BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) |
++ BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) |
++ BITS(DPI_DMA_CONTROL_DEN_POL, 0) |
++ BITS(DPI_DMA_CONTROL_HSYNC_POL, 0) |
++ BITS(DPI_DMA_CONTROL_VSYNC_POL, 0) |
++ BITS(DPI_DMA_CONTROL_COLORM, 0) |
++ BITS(DPI_DMA_CONTROL_SHUTDN, 0) |
++ BITS(DPI_DMA_CONTROL_HBP_EN, 1) |
++ BITS(DPI_DMA_CONTROL_HFP_EN, 1) |
++ BITS(DPI_DMA_CONTROL_VBP_EN, 1) |
++ BITS(DPI_DMA_CONTROL_VFP_EN, 1) |
++ BITS(DPI_DMA_CONTROL_HSYNC_EN, 1) |
++ BITS(DPI_DMA_CONTROL_VSYNC_EN, 1));
++}
++
++void rp1dsi_dma_update(struct rp1_dsi *dsi, dma_addr_t addr, u32 offset, u32 stride)
++{
++ /*
++ * Update STRIDE, DMAH and DMAL only. When called after rp1dsi_dma_setup(),
++ * DMA starts immediately; if already running, the buffer will flip at
++ * the next vertical sync event.
++ */
++ u64 a = addr + offset;
++
++ rp1dsi_dma_write(dsi, DPI_DMA_DMA_STRIDE, stride);
++ rp1dsi_dma_write(dsi, DPI_DMA_DMA_ADDR_H, a >> 32);
++ rp1dsi_dma_write(dsi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
++}
++
++void rp1dsi_dma_stop(struct rp1_dsi *dsi)
++{
++ /*
++ * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
++ * the current and any queued frame to end. "Force drain" flags are not used,
++ * as they seem to prevent DMA from re-starting properly; it's safer to wait.
++ */
++ u32 ctrl;
++
++ reinit_completion(&dsi->finished);
++ ctrl = rp1dsi_dma_read(dsi, DPI_DMA_CONTROL);
++ ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
++ rp1dsi_dma_write(dsi, DPI_DMA_CONTROL, ctrl);
++ if (!wait_for_completion_timeout(&dsi->finished, HZ / 10))
++ drm_err(dsi->drm, "%s: timed out waiting for idle\n", __func__);
++ rp1dsi_dma_write(dsi, DPI_DMA_IRQ_EN, 0);
++}
++
++void rp1dsi_dma_vblank_ctrl(struct rp1_dsi *dsi, int enable)
++{
++ rp1dsi_dma_write(dsi, DPI_DMA_IRQ_EN,
++ BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) |
++ BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) |
++ BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
++ BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
++}
++
++irqreturn_t rp1dsi_dma_isr(int irq, void *dev)
++{
++ struct rp1_dsi *dsi = dev;
++ u32 u = rp1dsi_dma_read(dsi, DPI_DMA_IRQ_FLAGS);
++
++ if (u) {
++ rp1dsi_dma_write(dsi, DPI_DMA_IRQ_FLAGS, u);
++ if (dsi) {
++ if (u & DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK)
++ drm_err_ratelimited(dsi->drm,
++ "Underflow! (panics=0x%08x)\n",
++ rp1dsi_dma_read(dsi, DPI_DMA_PANICS));
++ if (u & DPI_DMA_IRQ_FLAGS_DMA_READY_MASK)
++ drm_crtc_handle_vblank(&dsi->pipe.crtc);
++ if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
++ complete(&dsi->finished);
++ }
++ }
++ return u ? IRQ_HANDLED : IRQ_NONE;
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dsi/rp1_dsi_dsi.c
+@@ -0,0 +1,1504 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/platform_device.h>
++#include <linux/rp1_platform.h>
++#include "drm/drm_print.h"
++
++#include "rp1_dsi.h"
++
++/* ------------------------------- Synopsis DSI ------------------------ */
++#define DSI_VERSION_CFG 0x000
++#define DSI_PWR_UP 0x004
++#define DSI_CLKMGR_CFG 0x008
++#define DSI_DPI_VCID 0x00C
++#define DSI_DPI_COLOR_CODING 0x010
++#define DSI_DPI_CFG_POL 0x014
++#define DSI_DPI_LP_CMD_TIM 0x018
++#define DSI_DBI_VCID 0x01C
++#define DSI_DBI_CFG 0x020
++#define DSI_DBI_PARTITIONING_EN 0x024
++#define DSI_DBI_CMDSIZE 0x028
++#define DSI_PCKHDL_CFG 0x02C
++#define DSI_GEN_VCID 0x030
++#define DSI_MODE_CFG 0x034
++#define DSI_VID_MODE_CFG 0x038
++#define DSI_VID_PKT_SIZE 0x03C
++#define DSI_VID_NUM_CHUNKS 0x040
++#define DSI_VID_NULL_SIZE 0x044
++#define DSI_VID_HSA_TIME 0x048
++#define DSI_VID_HBP_TIME 0x04C
++#define DSI_VID_HLINE_TIME 0x050
++#define DSI_VID_VSA_LINES 0x054
++#define DSI_VID_VBP_LINES 0x058
++#define DSI_VID_VFP_LINES 0x05C
++#define DSI_VID_VACTIVE_LINES 0x060
++#define DSI_EDPI_CMD_SIZE 0x064
++#define DSI_CMD_MODE_CFG 0x068
++#define DSI_GEN_HDR 0x06C
++#define DSI_GEN_PLD_DATA 0x070
++#define DSI_CMD_PKT_STATUS 0x074
++#define DSI_TO_CNT_CFG 0x078
++#define DSI_HS_RD_TO_CNT 0x07C
++#define DSI_LP_RD_TO_CNT 0x080
++#define DSI_HS_WR_TO_CNT 0x084
++#define DSI_LP_WR_TO_CNT 0x088
++#define DSI_BTA_TO_CNT 0x08C
++#define DSI_SDF_3D 0x090
++#define DSI_LPCLK_CTRL 0x094
++#define DSI_PHY_TMR_LPCLK_CFG 0x098
++#define DSI_PHY_TMR_HS2LP_LSB 16
++#define DSI_PHY_TMR_LP2HS_LSB 0
++#define DSI_PHY_TMR_CFG 0x09C
++#define DSI_PHY_TMR_RD_CFG 0x0F4
++#define DSI_PHYRSTZ 0x0A0
++#define DSI_PHY_IF_CFG 0x0A4
++#define DSI_PHY_ULPS_CTRL 0x0A8
++#define DSI_PHY_TX_TRIGGERS 0x0AC
++#define DSI_PHY_STATUS 0x0B0
++
++#define DSI_PHY_TST_CTRL0 0x0B4
++#define DSI_PHY_TST_CTRL1 0x0B8
++#define DSI_INT_ST0 0x0BC
++#define DSI_INT_ST1 0x0C0
++#define DSI_INT_MASK0_CFG 0x0C4
++#define DSI_INT_MASK1_CFG 0x0C8
++#define DSI_PHY_CAL 0x0CC
++#define DSI_HEXP_NPKT_CLR 0x104
++#define DSI_HEXP_NPKT_SIZE 0x108
++#define DSI_VID_SHADOW_CTRL 0x100
++
++#define DSI_DPI_VCID_ACT 0x10C
++#define DSI_DPI_COLOR_CODING_ACT 0x110
++#define DSI_DPI_LP_CMD_TIM_ACT 0x118
++#define DSI_VID_MODE_CFG_ACT 0x138
++#define DSI_VID_PKT_SIZE_ACT 0x13C
++#define DSI_VID_NUM_CHUNKS_ACT 0x140
++#define DSI_VID_NULL_SIZE_ACT 0x144
++#define DSI_VID_HSA_TIME_ACT 0x148
++#define DSI_VID_HBP_TIME_ACT 0x14C
++#define DSI_VID_HLINE_TIME_ACT 0x150
++#define DSI_VID_VSA_LINES_ACT 0x154
++#define DSI_VID_VBP_LINES_ACT 0x158
++#define DSI_VID_VFP_LINES_ACT 0x15C
++#define DSI_VID_VACTIVE_LINES_ACT 0x160
++#define DSI_SDF_3D_CFG_ACT 0x190
++
++#define DSI_INT_FORCE0 0x0D8
++#define DSI_INT_FORCE1 0x0DC
++
++#define DSI_AUTO_ULPS_MODE 0x0E0
++#define DSI_AUTO_ULPS_ENTRY_DELAY 0x0E4
++#define DSI_AUTO_ULPS_WAKEUP_TIME 0x0E8
++#define DSI_EDPI_ADV_FEATURES 0x0EC
++
++#define DSI_DSC_PARAMETER 0x0F0
++
++/* And some bitfield definitions */
++
++#define DPHY_PWR_UP_SHUTDOWNZ_LSB 0
++#define DPHY_PWR_UP_SHUTDOWNZ_BITS BIT(DPHY_PWR_UP_SHUTDOWNZ_LSB)
++
++#define DPHY_CTRL0_PHY_TESTCLK_LSB 1
++#define DPHY_CTRL0_PHY_TESTCLK_BITS BIT(DPHY_CTRL0_PHY_TESTCLK_LSB)
++#define DPHY_CTRL0_PHY_TESTCLR_LSB 0
++#define DPHY_CTRL0_PHY_TESTCLR_BITS BIT(DPHY_CTRL0_PHY_TESTCLR_LSB)
++
++#define DPHY_CTRL1_PHY_TESTDIN_LSB 0
++#define DPHY_CTRL1_PHY_TESTDIN_BITS (0xff << DPHY_CTRL1_PHY_TESTDIN_LSB)
++#define DPHY_CTRL1_PHY_TESTDOUT_LSB 8
++#define DPHY_CTRL1_PHY_TESTDOUT_BITS (0xff << DPHY_CTRL1_PHY_TESTDOUT_LSB)
++#define DPHY_CTRL1_PHY_TESTEN_LSB 16
++#define DPHY_CTRL1_PHY_TESTEN_BITS BIT(DPHY_CTRL1_PHY_TESTEN_LSB)
++
++#define DSI_PHYRSTZ_SHUTDOWNZ_LSB 0
++#define DSI_PHYRSTZ_SHUTDOWNZ_BITS BIT(DSI_PHYRSTZ_SHUTDOWNZ_LSB)
++#define DSI_PHYRSTZ_RSTZ_LSB 1
++#define DSI_PHYRSTZ_RSTZ_BITS BIT(DSI_PHYRSTZ_RSTZ_LSB)
++#define DSI_PHYRSTZ_ENABLECLK_LSB 2
++#define DSI_PHYRSTZ_ENABLECLK_BITS BIT(DSI_PHYRSTZ_ENABLECLK_LSB)
++#define DSI_PHYRSTZ_FORCEPLL_LSB 3
++#define DSI_PHYRSTZ_FORCEPLL_BITS BIT(DSI_PHYRSTZ_FORCEPLL_LSB)
++
++#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44
++#define DPHY_PLL_INPUT_DIV_OFFSET 0x17
++#define DPHY_PLL_LOOP_DIV_OFFSET 0x18
++#define DPHY_PLL_DIV_CTRL_OFFSET 0x19
++
++#define DPHY_PLL_BIAS_OFFSET 0x10
++#define DPHY_PLL_BIAS_VCO_RANGE_LSB 3
++#define DPHY_PLL_BIAS_USE_PROGRAMMED_VCO_RANGE BIT(7)
++
++#define DPHY_PLL_CHARGE_PUMP_OFFSET 0x11
++#define DPHY_PLL_LPF_OFFSET 0x12
++
++#define DSI_WRITE(reg, val) writel((val), dsi->hw_base[RP1DSI_HW_BLOCK_DSI] + (reg))
++#define DSI_READ(reg) readl(dsi->hw_base[RP1DSI_HW_BLOCK_DSI] + (reg))
++
++// ================================================================================
++// Register block : RPI_MIPICFG
++// Version : 1
++// Bus type : apb
++// Description : Register block to control mipi DPHY
++// ================================================================================
++#define RPI_MIPICFG_REGS_RWTYPE_MSB 13
++#define RPI_MIPICFG_REGS_RWTYPE_LSB 12
++// ================================================================================
++// Register : RPI_MIPICFG_CLK2FC
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_CLK2FC_OFFSET 0x00000000
++#define RPI_MIPICFG_CLK2FC_BITS 0x00000007
++#define RPI_MIPICFG_CLK2FC_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_CLK2FC_SEL
++// Description : select a clock to be sent to the frequency counter
++// 7 = none
++// 6 = none
++// 5 = none
++// 4 = rxbyteclkhs (187.5MHz)
++// 3 = rxclkesc0 (20MHz)
++// 2 = txbyteclkhs (187.5MHz)
++// 1 = txclkesc (125MHz)
++// 0 = none
++#define RPI_MIPICFG_CLK2FC_SEL_RESET 0x0
++#define RPI_MIPICFG_CLK2FC_SEL_BITS 0x00000007
++#define RPI_MIPICFG_CLK2FC_SEL_MSB 2
++#define RPI_MIPICFG_CLK2FC_SEL_LSB 0
++#define RPI_MIPICFG_CLK2FC_SEL_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_CFG
++// JTAG access : asynchronous
++// Description : Top level configuration
++#define RPI_MIPICFG_CFG_OFFSET 0x00000004
++#define RPI_MIPICFG_CFG_BITS 0x00000111
++#define RPI_MIPICFG_CFG_RESET 0x00000001
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_CFG_DPIUPDATE
++// Description : Indicate the DSI block that the next frame will have a new video configuration
++#define RPI_MIPICFG_CFG_DPIUPDATE_RESET 0x0
++#define RPI_MIPICFG_CFG_DPIUPDATE_BITS 0x00000100
++#define RPI_MIPICFG_CFG_DPIUPDATE_MSB 8
++#define RPI_MIPICFG_CFG_DPIUPDATE_LSB 8
++#define RPI_MIPICFG_CFG_DPIUPDATE_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_CFG_SEL_TE_EXT
++// Description : Select the TE source: 1 - ext, 0 - int
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_RESET 0x0
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_BITS 0x00000010
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_MSB 4
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_LSB 4
++#define RPI_MIPICFG_CFG_SEL_TE_EXT_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_CFG_SEL_CSI_DSI_N
++// Description : Select PHY direction: input to CSI, output from DSI. CSI 1 DSI 0
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_RESET 0x1
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_BITS 0x00000001
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_MSB 0
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_LSB 0
++#define RPI_MIPICFG_CFG_SEL_CSI_DSI_N_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_TE
++// JTAG access : synchronous
++// Description : Tearing effect processing
++#define RPI_MIPICFG_TE_OFFSET 0x00000008
++#define RPI_MIPICFG_TE_BITS 0x10ffffff
++#define RPI_MIPICFG_TE_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_TE_ARM
++// Description : Tearing effect arm
++#define RPI_MIPICFG_TE_ARM_RESET 0x0
++#define RPI_MIPICFG_TE_ARM_BITS 0x10000000
++#define RPI_MIPICFG_TE_ARM_MSB 28
++#define RPI_MIPICFG_TE_ARM_LSB 28
++#define RPI_MIPICFG_TE_ARM_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_TE_HALT_CYC
++// Description : When arm pulse has been seen, wait for te; then halt the dpi block
++// for this many clk_dpi cycles
++#define RPI_MIPICFG_TE_HALT_CYC_RESET 0x000000
++#define RPI_MIPICFG_TE_HALT_CYC_BITS 0x00ffffff
++#define RPI_MIPICFG_TE_HALT_CYC_MSB 23
++#define RPI_MIPICFG_TE_HALT_CYC_LSB 0
++#define RPI_MIPICFG_TE_HALT_CYC_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_DPHY_MONITOR
++// JTAG access : asynchronous
++// Description : DPHY status monitors for analog DFT
++#define RPI_MIPICFG_DPHY_MONITOR_OFFSET 0x00000010
++#define RPI_MIPICFG_DPHY_MONITOR_BITS 0x00111fff
++#define RPI_MIPICFG_DPHY_MONITOR_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_MONITOR_LOCK
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_RESET 0x0
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_BITS 0x00100000
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_MSB 20
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_LSB 20
++#define RPI_MIPICFG_DPHY_MONITOR_LOCK_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_MONITOR_BISTOK
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_RESET 0x0
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_BITS 0x00010000
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_MSB 16
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_LSB 16
++#define RPI_MIPICFG_DPHY_MONITOR_BISTOK_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_RESET 0x0
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_BITS 0x00001000
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_MSB 12
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_LSB 12
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATECLK_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_RESET 0x0
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_BITS 0x00000f00
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_MSB 11
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_LSB 8
++#define RPI_MIPICFG_DPHY_MONITOR_STOPSTATEDATA_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_MONITOR_TESTDOUT
++// Description : None
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_RESET 0x00
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_BITS 0x000000ff
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_MSB 7
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_LSB 0
++#define RPI_MIPICFG_DPHY_MONITOR_TESTDOUT_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_DPHY_CTRL_0
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_0_OFFSET 0x00000014
++#define RPI_MIPICFG_DPHY_CTRL_0_BITS 0x0000003f
++#define RPI_MIPICFG_DPHY_CTRL_0_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE
++// Description : When set in lpmode, TXCLKESC is driven from clk_vec(driven from clocks block)
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_BITS 0x00000020
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_MSB 5
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_LSB 5
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_LPMODE_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA
++// Description : When set, drive the DPHY from the test registers
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_BITS 0x00000010
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_MSB 4
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_LSB 4
++#define RPI_MIPICFG_DPHY_CTRL_0_TEST_ENA_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS
++// Description : When test_ena is set, disable cfg_clk
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_BITS 0x00000008
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_MSB 3
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_LSB 3
++#define RPI_MIPICFG_DPHY_CTRL_0_CFG_CLK_DIS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS
++// Description : When test_ena is set, disable refclk
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_BITS 0x00000004
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_MSB 2
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_LSB 2
++#define RPI_MIPICFG_DPHY_CTRL_0_REFCLK_DIS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS
++// Description : When test_ena is set, disable txclkesc
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_BITS 0x00000002
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_MSB 1
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_LSB 1
++#define RPI_MIPICFG_DPHY_CTRL_0_TXCLKESC_DIS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS
++// Description : When test_ena is set, disable txbyteclkhs
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_BITS 0x00000001
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_MSB 0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_LSB 0
++#define RPI_MIPICFG_DPHY_CTRL_0_TXBYTECLKHS_DIS_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_DPHY_CTRL_1
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_1_OFFSET 0x00000018
++#define RPI_MIPICFG_DPHY_CTRL_1_BITS 0x7fffffff
++#define RPI_MIPICFG_DPHY_CTRL_1_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_BITS 0x40000000
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_MSB 30
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_LSB 30
++#define RPI_MIPICFG_DPHY_CTRL_1_FORCEPLL_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_BITS 0x20000000
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_MSB 29
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_LSB 29
++#define RPI_MIPICFG_DPHY_CTRL_1_SHUTDOWNZ_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_RSTZ
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_BITS 0x10000000
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_MSB 28
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_LSB 28
++#define RPI_MIPICFG_DPHY_CTRL_1_RSTZ_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_BITS 0x08000000
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_MSB 27
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_LSB 27
++#define RPI_MIPICFG_DPHY_CTRL_1_MASTERSLAVEZ_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_BISTON
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_BITS 0x04000000
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_MSB 26
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_LSB 26
++#define RPI_MIPICFG_DPHY_CTRL_1_BISTON_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_BITS 0x02000000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_MSB 25
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_LSB 25
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTHSCLK_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_BITS 0x01000000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_MSB 24
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_LSB 24
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLECLK_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_BITS 0x00800000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_MSB 23
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_LSB 23
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_BITS 0x00400000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_MSB 22
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_LSB 22
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_BITS 0x00200000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_MSB 21
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_LSB 21
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_BITS 0x00100000
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_MSB 20
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_LSB 20
++#define RPI_MIPICFG_DPHY_CTRL_1_ENABLE_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_BITS 0x00080000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_MSB 19
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_LSB 19
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_BITS 0x00040000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_MSB 18
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_LSB 18
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_BITS 0x00020000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_MSB 17
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_LSB 17
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_BITS 0x00010000
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_MSB 16
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_LSB 16
++#define RPI_MIPICFG_DPHY_CTRL_1_BASEDIR_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_BITS 0x00008000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_MSB 15
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_LSB 15
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_BITS 0x00004000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_MSB 14
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_LSB 14
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_BITS 0x00002000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_MSB 13
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_LSB 13
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_BITS 0x00001000
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_MSB 12
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_LSB 12
++#define RPI_MIPICFG_DPHY_CTRL_1_TXLPDTESC_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_BITS 0x00000800
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_MSB 11
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_LSB 11
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_BITS 0x00000400
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_MSB 10
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_LSB 10
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_BITS 0x00000200
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_MSB 9
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_LSB 9
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_BITS 0x00000100
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_MSB 8
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_LSB 8
++#define RPI_MIPICFG_DPHY_CTRL_1_TXVALIDESC_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_BITS 0x00000080
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_MSB 7
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_LSB 7
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_BITS 0x00000040
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_MSB 6
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_LSB 6
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_BITS 0x00000020
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_MSB 5
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_LSB 5
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_BITS 0x00000010
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_MSB 4
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_LSB 4
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTESC_0_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_BITS 0x00000008
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_MSB 3
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_LSB 3
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_BITS 0x00000004
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_MSB 2
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_LSB 2
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_BITS 0x00000002
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_MSB 1
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_LSB 1
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_BITS 0x00000001
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_MSB 0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_LSB 0
++#define RPI_MIPICFG_DPHY_CTRL_1_TXREQUESTDATAHS_0_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_DPHY_CTRL_2
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_2_OFFSET 0x0000001c
++#define RPI_MIPICFG_DPHY_CTRL_2_BITS 0x000007ff
++#define RPI_MIPICFG_DPHY_CTRL_2_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_2_TESTCLK
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_BITS 0x00000400
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_MSB 10
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_LSB 10
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLK_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_2_TESTEN
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_BITS 0x00000200
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_MSB 9
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_LSB 9
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTEN_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_2_TESTCLR
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_RESET 0x0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_BITS 0x00000100
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_MSB 8
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_LSB 8
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTCLR_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_2_TESTDIN
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_BITS 0x000000ff
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_MSB 7
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_LSB 0
++#define RPI_MIPICFG_DPHY_CTRL_2_TESTDIN_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_DPHY_CTRL_3
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_3_OFFSET 0x00000020
++#define RPI_MIPICFG_DPHY_CTRL_3_BITS 0xffffffff
++#define RPI_MIPICFG_DPHY_CTRL_3_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_BITS 0xff000000
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_MSB 31
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_LSB 24
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_BITS 0x00ff0000
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_MSB 23
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_LSB 16
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_BITS 0x0000ff00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_MSB 15
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_LSB 8
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_BITS 0x000000ff
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_MSB 7
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_LSB 0
++#define RPI_MIPICFG_DPHY_CTRL_3_TXDATAESC_0_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_DPHY_CTRL_4
++// JTAG access : asynchronous
++// Description : DPHY control for analog DFT
++#define RPI_MIPICFG_DPHY_CTRL_4_OFFSET 0x00000024
++#define RPI_MIPICFG_DPHY_CTRL_4_BITS 0xffffffff
++#define RPI_MIPICFG_DPHY_CTRL_4_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_BITS 0xff000000
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_MSB 31
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_LSB 24
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_3_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_BITS 0x00ff0000
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_MSB 23
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_LSB 16
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_2_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_BITS 0x0000ff00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_MSB 15
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_LSB 8
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_1_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0
++// Description : None
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_RESET 0x00
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_BITS 0x000000ff
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_MSB 7
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_LSB 0
++#define RPI_MIPICFG_DPHY_CTRL_4_TXDATAHS_0_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_INTR
++// JTAG access : synchronous
++// Description : Raw Interrupts
++#define RPI_MIPICFG_INTR_OFFSET 0x00000028
++#define RPI_MIPICFG_INTR_BITS 0x0000000f
++#define RPI_MIPICFG_INTR_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTR_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTR_DSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTR_DSI_HOST_BITS 0x00000008
++#define RPI_MIPICFG_INTR_DSI_HOST_MSB 3
++#define RPI_MIPICFG_INTR_DSI_HOST_LSB 3
++#define RPI_MIPICFG_INTR_DSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTR_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTR_CSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTR_CSI_HOST_BITS 0x00000004
++#define RPI_MIPICFG_INTR_CSI_HOST_MSB 2
++#define RPI_MIPICFG_INTR_CSI_HOST_LSB 2
++#define RPI_MIPICFG_INTR_CSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTR_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTR_DSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTR_DSI_DMA_BITS 0x00000002
++#define RPI_MIPICFG_INTR_DSI_DMA_MSB 1
++#define RPI_MIPICFG_INTR_DSI_DMA_LSB 1
++#define RPI_MIPICFG_INTR_DSI_DMA_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTR_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTR_CSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTR_CSI_DMA_BITS 0x00000001
++#define RPI_MIPICFG_INTR_CSI_DMA_MSB 0
++#define RPI_MIPICFG_INTR_CSI_DMA_LSB 0
++#define RPI_MIPICFG_INTR_CSI_DMA_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_INTE
++// JTAG access : synchronous
++// Description : Interrupt Enable
++#define RPI_MIPICFG_INTE_OFFSET 0x0000002c
++#define RPI_MIPICFG_INTE_BITS 0x0000000f
++#define RPI_MIPICFG_INTE_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTE_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTE_DSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTE_DSI_HOST_BITS 0x00000008
++#define RPI_MIPICFG_INTE_DSI_HOST_MSB 3
++#define RPI_MIPICFG_INTE_DSI_HOST_LSB 3
++#define RPI_MIPICFG_INTE_DSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTE_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTE_CSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTE_CSI_HOST_BITS 0x00000004
++#define RPI_MIPICFG_INTE_CSI_HOST_MSB 2
++#define RPI_MIPICFG_INTE_CSI_HOST_LSB 2
++#define RPI_MIPICFG_INTE_CSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTE_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTE_DSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTE_DSI_DMA_BITS 0x00000002
++#define RPI_MIPICFG_INTE_DSI_DMA_MSB 1
++#define RPI_MIPICFG_INTE_DSI_DMA_LSB 1
++#define RPI_MIPICFG_INTE_DSI_DMA_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTE_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTE_CSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTE_CSI_DMA_BITS 0x00000001
++#define RPI_MIPICFG_INTE_CSI_DMA_MSB 0
++#define RPI_MIPICFG_INTE_CSI_DMA_LSB 0
++#define RPI_MIPICFG_INTE_CSI_DMA_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_INTF
++// JTAG access : synchronous
++// Description : Interrupt Force
++#define RPI_MIPICFG_INTF_OFFSET 0x00000030
++#define RPI_MIPICFG_INTF_BITS 0x0000000f
++#define RPI_MIPICFG_INTF_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTF_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTF_DSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTF_DSI_HOST_BITS 0x00000008
++#define RPI_MIPICFG_INTF_DSI_HOST_MSB 3
++#define RPI_MIPICFG_INTF_DSI_HOST_LSB 3
++#define RPI_MIPICFG_INTF_DSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTF_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTF_CSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTF_CSI_HOST_BITS 0x00000004
++#define RPI_MIPICFG_INTF_CSI_HOST_MSB 2
++#define RPI_MIPICFG_INTF_CSI_HOST_LSB 2
++#define RPI_MIPICFG_INTF_CSI_HOST_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTF_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTF_DSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTF_DSI_DMA_BITS 0x00000002
++#define RPI_MIPICFG_INTF_DSI_DMA_MSB 1
++#define RPI_MIPICFG_INTF_DSI_DMA_LSB 1
++#define RPI_MIPICFG_INTF_DSI_DMA_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTF_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTF_CSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTF_CSI_DMA_BITS 0x00000001
++#define RPI_MIPICFG_INTF_CSI_DMA_MSB 0
++#define RPI_MIPICFG_INTF_CSI_DMA_LSB 0
++#define RPI_MIPICFG_INTF_CSI_DMA_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_INTS
++// JTAG access : synchronous
++// Description : Interrupt status after masking & forcing
++#define RPI_MIPICFG_INTS_OFFSET 0x00000034
++#define RPI_MIPICFG_INTS_BITS 0x0000000f
++#define RPI_MIPICFG_INTS_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTS_DSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTS_DSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTS_DSI_HOST_BITS 0x00000008
++#define RPI_MIPICFG_INTS_DSI_HOST_MSB 3
++#define RPI_MIPICFG_INTS_DSI_HOST_LSB 3
++#define RPI_MIPICFG_INTS_DSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTS_CSI_HOST
++// Description : None
++#define RPI_MIPICFG_INTS_CSI_HOST_RESET 0x0
++#define RPI_MIPICFG_INTS_CSI_HOST_BITS 0x00000004
++#define RPI_MIPICFG_INTS_CSI_HOST_MSB 2
++#define RPI_MIPICFG_INTS_CSI_HOST_LSB 2
++#define RPI_MIPICFG_INTS_CSI_HOST_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTS_DSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTS_DSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTS_DSI_DMA_BITS 0x00000002
++#define RPI_MIPICFG_INTS_DSI_DMA_MSB 1
++#define RPI_MIPICFG_INTS_DSI_DMA_LSB 1
++#define RPI_MIPICFG_INTS_DSI_DMA_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_INTS_CSI_DMA
++// Description : None
++#define RPI_MIPICFG_INTS_CSI_DMA_RESET 0x0
++#define RPI_MIPICFG_INTS_CSI_DMA_BITS 0x00000001
++#define RPI_MIPICFG_INTS_CSI_DMA_MSB 0
++#define RPI_MIPICFG_INTS_CSI_DMA_LSB 0
++#define RPI_MIPICFG_INTS_CSI_DMA_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_BLOCK_ID
++// JTAG access : asynchronous
++// Description : Block Identifier
++#define RPI_MIPICFG_BLOCK_ID_OFFSET 0x00000038
++#define RPI_MIPICFG_BLOCK_ID_BITS 0xffffffff
++#define RPI_MIPICFG_BLOCK_ID_RESET 0x4d495049
++#define RPI_MIPICFG_BLOCK_ID_MSB 31
++#define RPI_MIPICFG_BLOCK_ID_LSB 0
++#define RPI_MIPICFG_BLOCK_ID_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_INSTANCE_ID
++// JTAG access : asynchronous
++// Description : Block Instance Identifier
++#define RPI_MIPICFG_INSTANCE_ID_OFFSET 0x0000003c
++#define RPI_MIPICFG_INSTANCE_ID_BITS 0x0000000f
++#define RPI_MIPICFG_INSTANCE_ID_RESET 0x00000000
++#define RPI_MIPICFG_INSTANCE_ID_MSB 3
++#define RPI_MIPICFG_INSTANCE_ID_LSB 0
++#define RPI_MIPICFG_INSTANCE_ID_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_RSTSEQ_AUTO
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_AUTO_OFFSET 0x00000040
++#define RPI_MIPICFG_RSTSEQ_AUTO_BITS 0x00000007
++#define RPI_MIPICFG_RSTSEQ_AUTO_RESET 0x00000007
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_AUTO_CSI
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_RESET 0x1
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_BITS 0x00000004
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_MSB 2
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_LSB 2
++#define RPI_MIPICFG_RSTSEQ_AUTO_CSI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_AUTO_DPI
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_RESET 0x1
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_BITS 0x00000002
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_MSB 1
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_LSB 1
++#define RPI_MIPICFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_RESET 0x1
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_BITS 0x00000001
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_MSB 0
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_LSB 0
++#define RPI_MIPICFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_RSTSEQ_PARALLEL
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_OFFSET 0x00000044
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BITS 0x00000007
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_RESET 0x00000006
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_PARALLEL_CSI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_RESET 0x1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_BITS 0x00000004
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_MSB 2
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_LSB 2
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_CSI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_PARALLEL_DPI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_RESET 0x1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_BITS 0x00000002
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_MSB 1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_LSB 1
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS 0x00000001
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB 0
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB 0
++#define RPI_MIPICFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_RSTSEQ_CTRL
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_CTRL_OFFSET 0x00000048
++#define RPI_MIPICFG_RSTSEQ_CTRL_BITS 0x00000007
++#define RPI_MIPICFG_RSTSEQ_CTRL_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_CTRL_CSI
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_BITS 0x00000004
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_MSB 2
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_LSB 2
++#define RPI_MIPICFG_RSTSEQ_CTRL_CSI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_CTRL_DPI
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_BITS 0x00000002
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_MSB 1
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_LSB 1
++#define RPI_MIPICFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_BITS 0x00000001
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_MSB 0
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_LSB 0
++#define RPI_MIPICFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
++// ================================================================================
++// Register : RPI_MIPICFG_RSTSEQ_TRIG
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_TRIG_OFFSET 0x0000004c
++#define RPI_MIPICFG_RSTSEQ_TRIG_BITS 0x00000007
++#define RPI_MIPICFG_RSTSEQ_TRIG_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_TRIG_CSI
++// Description : Pulses the reset output
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_BITS 0x00000004
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_MSB 2
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_LSB 2
++#define RPI_MIPICFG_RSTSEQ_TRIG_CSI_ACCESS "SC"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_TRIG_DPI
++// Description : Pulses the reset output
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_BITS 0x00000002
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_MSB 1
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_LSB 1
++#define RPI_MIPICFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER
++// Description : Pulses the reset output
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_BITS 0x00000001
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_MSB 0
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_LSB 0
++#define RPI_MIPICFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
++// ================================================================================
++// Register : RPI_MIPICFG_RSTSEQ_DONE
++// JTAG access : synchronous
++// Description : None
++#define RPI_MIPICFG_RSTSEQ_DONE_OFFSET 0x00000050
++#define RPI_MIPICFG_RSTSEQ_DONE_BITS 0x00000007
++#define RPI_MIPICFG_RSTSEQ_DONE_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_DONE_CSI
++// Description : Indicates the current state of the reset
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_BITS 0x00000004
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_MSB 2
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_LSB 2
++#define RPI_MIPICFG_RSTSEQ_DONE_CSI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_DONE_DPI
++// Description : Indicates the current state of the reset
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_BITS 0x00000002
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_MSB 1
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_LSB 1
++#define RPI_MIPICFG_RSTSEQ_DONE_DPI_ACCESS "RO"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER
++// Description : Indicates the current state of the reset
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_RESET 0x0
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_BITS 0x00000001
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_MSB 0
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_LSB 0
++#define RPI_MIPICFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
++// ================================================================================
++// Register : RPI_MIPICFG_DFTSS
++// JTAG access : asynchronous
++// Description : None
++#define RPI_MIPICFG_DFTSS_OFFSET 0x00000054
++#define RPI_MIPICFG_DFTSS_BITS 0x0000001f
++#define RPI_MIPICFG_DFTSS_RESET 0x00000000
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DFTSS_JTAG_COPY
++// Description : None
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_RESET 0x0
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_BITS 0x00000010
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_MSB 4
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_LSB 4
++#define RPI_MIPICFG_DFTSS_JTAG_COPY_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY
++// Description : None
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_RESET 0x0
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_BITS 0x00000008
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_MSB 3
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_LSB 3
++#define RPI_MIPICFG_DFTSS_JTAG_ACCESS_ONLY_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS
++// Description : None
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_RESET 0x0
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_BITS 0x00000004
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_MSB 2
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_LSB 2
++#define RPI_MIPICFG_DFTSS_BYPASS_OUTSYNCS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DFTSS_BYPASS_INSYNCS
++// Description : None
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_RESET 0x0
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_BITS 0x00000002
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_MSB 1
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_LSB 1
++#define RPI_MIPICFG_DFTSS_BYPASS_INSYNCS_ACCESS "RW"
++// --------------------------------------------------------------------------------
++// Field : RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS
++// Description : None
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_RESET 0x0
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_BITS 0x00000001
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_MSB 0
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_LSB 0
++#define RPI_MIPICFG_DFTSS_BYPASS_RESETSYNCS_ACCESS "RW"
++
++#define CFG_WRITE(reg, val) writel((val), dsi->hw_base[RP1DSI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++#define CFG_READ(reg) readl(dsi->hw_base[RP1DSI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++
++/* ------------------------------- DPHY setup stuff ------------------------ */
++
++static void dphy_transaction(struct rp1_dsi *dsi, uint8_t test_code, uint8_t test_data)
++{
++ /*
++ * See pg 101 of mipi dphy bidir databook
++ * Assume we start with testclk high.
++ * Each APB write takes at least 10ns and we ignore TESTDOUT
++ * so there is no need for extra delays between the transitions.
++ */
++ u32 tmp;
++
++ DSI_WRITE(DSI_PHY_TST_CTRL1, test_code | DPHY_CTRL1_PHY_TESTEN_BITS);
++ DSI_WRITE(DSI_PHY_TST_CTRL0, 0);
++ tmp = (DSI_READ(DSI_PHY_TST_CTRL1) >> DPHY_CTRL1_PHY_TESTDOUT_LSB) & 0xFF;
++ DSI_WRITE(DSI_PHY_TST_CTRL1, test_data);
++ DSI_WRITE(DSI_PHY_TST_CTRL0, DPHY_CTRL0_PHY_TESTCLK_BITS);
++}
++
++static uint8_t dphy_get_div(u32 refclk_khz, u32 vco_freq_khz, u32 *ptr_m, u32 *ptr_n)
++{
++ /*
++ * See pg 77-78 of dphy databook
++ * fvco = m/n * refclk
++ * with the limit
++ * 40MHz >= fREFCLK / N >= 5MHz
++ * M (multiplier) must be an even number between 2 and 300
++ * N (input divider) must be an integer between 1 and 100
++ *
++ * In practice, given a 50MHz reference clock, it can produce any
++ * multiple of 10MHz, 11.1111MHz, 12.5MHz, 14.286MHz or 16.667MHz
++ * with < 1% error for all frequencies above 495MHz.
++ */
++
++ static const u32 REF_DIVN_MAX = 40000u;
++ static const u32 REF_DIVN_MIN = 5000u;
++ u32 best_n, best_m, best_err = 0x7fffffff;
++ unsigned int n;
++
++ for (n = 1 + refclk_khz / REF_DIVN_MAX; n * REF_DIVN_MIN <= refclk_khz && n < 100; ++n) {
++ u32 half_m = (n * vco_freq_khz + refclk_khz) / (2 * refclk_khz);
++
++ if (half_m < 150) {
++ u32 f = (2 * half_m * refclk_khz) / n;
++ u32 err = (f > vco_freq_khz) ? f - vco_freq_khz : vco_freq_khz - f;
++
++ if (err < best_err) {
++ best_n = n;
++ best_m = 2 * half_m;
++ best_err = err;
++ if (err == 0)
++ break;
++ }
++ }
++ }
++
++ if (64 * best_err < vco_freq_khz) { /* tolerate small error */
++ *ptr_n = best_n;
++ *ptr_m = best_m;
++ return 1;
++ }
++ return 0;
++}
++
++struct hsfreq_range {
++ u16 mhz_max;
++ u8 hsfreqrange;
++ u8 clk_lp2hs;
++ u8 clk_hs2lp;
++ u8 data_lp2hs; /* excluding clk lane entry */
++ u8 data_hs2lp;
++};
++
++/* See Table A-3 on page 258 of dphy databook */
++static const struct hsfreq_range hsfreq_table[] = {
++ { 89, 0b000000, 32, 20, 26, 13 },
++ { 99, 0b010000, 35, 23, 28, 14 },
++ { 109, 0b100000, 32, 22, 26, 13 },
++ { 129, 0b000001, 31, 20, 27, 13 },
++ { 139, 0b010001, 33, 22, 26, 14 },
++ { 149, 0b100001, 33, 21, 26, 14 },
++ { 169, 0b000010, 32, 20, 27, 13 },
++ { 179, 0b010010, 36, 23, 30, 15 },
++ { 199, 0b100010, 40, 22, 33, 15 },
++ { 219, 0b000011, 40, 22, 33, 15 },
++ { 239, 0b010011, 44, 24, 36, 16 },
++ { 249, 0b100011, 48, 24, 38, 17 },
++ { 269, 0b000100, 48, 24, 38, 17 },
++ { 299, 0b010100, 50, 27, 41, 18 },
++ { 329, 0b000101, 56, 28, 45, 18 },
++ { 359, 0b010101, 59, 28, 48, 19 },
++ { 399, 0b100101, 61, 30, 50, 20 },
++ { 449, 0b000110, 67, 31, 55, 21 },
++ { 499, 0b010110, 73, 31, 59, 22 },
++ { 549, 0b000111, 79, 36, 63, 24 },
++ { 599, 0b010111, 83, 37, 68, 25 },
++ { 649, 0b001000, 90, 38, 73, 27 },
++ { 699, 0b011000, 95, 40, 77, 28 },
++ { 749, 0b001001, 102, 40, 84, 28 },
++ { 799, 0b011001, 106, 42, 87, 30 },
++ { 849, 0b101001, 113, 44, 93, 31 },
++ { 899, 0b111001, 118, 47, 98, 32 },
++ { 949, 0b001010, 124, 47, 102, 34 },
++ { 999, 0b011010, 130, 49, 107, 35 },
++ { 1049, 0b101010, 135, 51, 111, 37 },
++ { 1099, 0b111010, 139, 51, 114, 38 },
++ { 1149, 0b001011, 146, 54, 120, 40 },
++ { 1199, 0b011011, 153, 57, 125, 41 },
++ { 1249, 0b101011, 158, 58, 130, 42 },
++ { 1299, 0b111011, 163, 58, 135, 44 },
++ { 1349, 0b001100, 168, 60, 140, 45 },
++ { 1399, 0b011100, 172, 64, 144, 47 },
++ { 1449, 0b101100, 176, 65, 148, 48 },
++ { 1500, 0b111100, 181, 66, 153, 50 },
++};
++
++static void dphy_set_hsfreqrange(struct rp1_dsi *dsi, u32 freq_mhz)
++{
++ unsigned int i;
++
++ if (freq_mhz < 80 || freq_mhz > 1500)
++ drm_err(dsi->drm, "DPHY: Frequency %u MHz out of range\n",
++ freq_mhz);
++
++ for (i = 0; i < ARRAY_SIZE(hsfreq_table) - 1; i++) {
++ if (freq_mhz <= hsfreq_table[i].mhz_max)
++ break;
++ }
++
++ dsi->hsfreq_index = i;
++ dphy_transaction(dsi, DPHY_HS_RX_CTRL_LANE0_OFFSET,
++ hsfreq_table[i].hsfreqrange << 1);
++}
++
++static void dphy_configure_pll(struct rp1_dsi *dsi, u32 refclk_khz, u32 vco_freq_khz)
++{
++ u32 m = 0;
++ u32 n = 0;
++
++ if (dphy_get_div(refclk_khz, vco_freq_khz, &m, &n)) {
++ dphy_set_hsfreqrange(dsi, vco_freq_khz / 1000);
++ /* Program m,n from registers */
++ dphy_transaction(dsi, DPHY_PLL_DIV_CTRL_OFFSET, 0x30);
++ /* N (program N-1) */
++ dphy_transaction(dsi, DPHY_PLL_INPUT_DIV_OFFSET, n - 1);
++ /* M[8:5] ?? */
++ dphy_transaction(dsi, DPHY_PLL_LOOP_DIV_OFFSET, 0x80 | ((m - 1) >> 5));
++ /* M[4:0] (program M-1) */
++ dphy_transaction(dsi, DPHY_PLL_LOOP_DIV_OFFSET, ((m - 1) & 0x1F));
++ drm_dbg_driver(dsi->drm,
++ "DPHY: vco freq want %dkHz got %dkHz = %d * (%dkHz / %d), hsfreqrange = 0x%02x\r\n",
++ vco_freq_khz, refclk_khz * m / n, m, refclk_khz,
++ n, hsfreq_table[dsi->hsfreq_index].hsfreqrange);
++ } else {
++ drm_info(dsi->drm,
++ "rp1dsi: Error configuring DPHY PLL! %dkHz = %d * (%dkHz / %d)\r\n",
++ vco_freq_khz, m, refclk_khz, n);
++ }
++}
++
++static void dphy_init_khz(struct rp1_dsi *dsi, u32 ref_freq, u32 vco_freq)
++{
++ /* Reset the PHY */
++ DSI_WRITE(DSI_PHYRSTZ, 0);
++ DSI_WRITE(DSI_PHY_TST_CTRL0, DPHY_CTRL0_PHY_TESTCLK_BITS);
++ DSI_WRITE(DSI_PHY_TST_CTRL1, 0);
++ DSI_WRITE(DSI_PHY_TST_CTRL0, (DPHY_CTRL0_PHY_TESTCLK_BITS | DPHY_CTRL0_PHY_TESTCLR_BITS));
++ udelay(1);
++ DSI_WRITE(DSI_PHY_TST_CTRL0, DPHY_CTRL0_PHY_TESTCLK_BITS);
++ udelay(1);
++ /* Since we are in DSI (not CSI2) mode here, start the PLL */
++ dphy_configure_pll(dsi, ref_freq, vco_freq);
++ udelay(1);
++ /* Unreset */
++ DSI_WRITE(DSI_PHYRSTZ, DSI_PHYRSTZ_SHUTDOWNZ_BITS);
++ udelay(1);
++ DSI_WRITE(DSI_PHYRSTZ, (DSI_PHYRSTZ_SHUTDOWNZ_BITS | DSI_PHYRSTZ_RSTZ_BITS));
++ udelay(1); /* so we can see PLL coming up? */
++}
++
++void rp1dsi_mipicfg_setup(struct rp1_dsi *dsi)
++{
++ /* Select DSI rather than CSI-2 */
++ CFG_WRITE(RPI_MIPICFG_CFG, 0);
++ /* Enable DSIDMA interrupt only */
++ CFG_WRITE(RPI_MIPICFG_INTE, RPI_MIPICFG_INTE_DSI_DMA_BITS);
++}
++
++static unsigned long rp1dsi_refclk_freq(struct rp1_dsi *dsi)
++{
++ unsigned long u;
++
++ u = (dsi->clocks[RP1DSI_CLOCK_REF]) ? clk_get_rate(dsi->clocks[RP1DSI_CLOCK_REF]) : 0;
++ if (u < 1 || u >= (1ul << 30))
++ u = 50000000ul; /* default XOSC frequency */
++ return u;
++}
++
++static void rp1dsi_dpiclk_start(struct rp1_dsi *dsi, unsigned int bpp, unsigned int lanes)
++{
++ unsigned long u;
++
++ if (dsi->clocks[RP1DSI_CLOCK_DPI]) {
++ u = (dsi->clocks[RP1DSI_CLOCK_BYTE]) ?
++ clk_get_rate(dsi->clocks[RP1DSI_CLOCK_BYTE]) : 0;
++ drm_info(dsi->drm,
++ "rp1dsi: Nominal byte clock %lu; scale by %u/%u",
++ u, 4 * lanes, (bpp >> 1));
++ if (u < 1 || u >= (1ul << 28))
++ u = 72000000ul; /* default DUMMY frequency for byteclock */
++
++ clk_set_parent(dsi->clocks[RP1DSI_CLOCK_DPI], dsi->clocks[RP1DSI_CLOCK_BYTE]);
++ clk_set_rate(dsi->clocks[RP1DSI_CLOCK_DPI], (4 * lanes * u) / (bpp >> 1));
++ clk_prepare_enable(dsi->clocks[RP1DSI_CLOCK_DPI]);
++ }
++}
++
++static void rp1dsi_dpiclk_stop(struct rp1_dsi *dsi)
++{
++ if (dsi->clocks[RP1DSI_CLOCK_DPI])
++ clk_disable_unprepare(dsi->clocks[RP1DSI_CLOCK_DPI]);
++}
++
++/* Choose the internal on-the-bus DPI format, and DSI packing flag. */
++static u32 get_colorcode(enum mipi_dsi_pixel_format fmt)
++{
++ switch (fmt) {
++ case MIPI_DSI_FMT_RGB666:
++ return 0x104;
++ case MIPI_DSI_FMT_RGB666_PACKED:
++ return 0x003;
++ case MIPI_DSI_FMT_RGB565:
++ return 0x000;
++ case MIPI_DSI_FMT_RGB888:
++ return 0x005;
++ }
++
++ /* This should be impossible as the format is validated in
++ * rp1dsi_host_attach
++ */
++ WARN_ONCE(1, "Invalid colour format configured for DSI");
++ return 0x005;
++}
++
++void rp1dsi_dsi_setup(struct rp1_dsi *dsi, struct drm_display_mode const *mode)
++{
++ u32 timeout, mask, vid_mode_cfg;
++ u32 freq_khz;
++ unsigned int bpp = mipi_dsi_pixel_format_to_bpp(dsi->display_format);
++
++ DSI_WRITE(DSI_PHY_IF_CFG, dsi->lanes - 1);
++ DSI_WRITE(DSI_DPI_CFG_POL, 0);
++ DSI_WRITE(DSI_GEN_VCID, dsi->vc);
++ DSI_WRITE(DSI_DPI_COLOR_CODING, get_colorcode(dsi->display_format));
++ /* a conservative guess (LP escape is slow!) */
++ DSI_WRITE(DSI_DPI_LP_CMD_TIM, 0x00100000);
++
++ /* Drop to LP where possible */
++ vid_mode_cfg = 0xbf00;
++ if (!(dsi->display_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE))
++ vid_mode_cfg |= 0x01;
++ if (dsi->display_flags & MIPI_DSI_MODE_VIDEO_BURST)
++ vid_mode_cfg |= 0x02;
++ DSI_WRITE(DSI_VID_MODE_CFG, vid_mode_cfg);
++
++ /* Use LP Escape Data signalling for all commands */
++ DSI_WRITE(DSI_CMD_MODE_CFG, 0x10F7F00);
++ /* Select Command Mode */
++ DSI_WRITE(DSI_MODE_CFG, 1);
++ /* XXX magic number */
++ DSI_WRITE(DSI_TO_CNT_CFG, 0x02000200);
++ /* XXX magic number */
++ DSI_WRITE(DSI_BTA_TO_CNT, 0x800);
++
++ DSI_WRITE(DSI_VID_PKT_SIZE, mode->hdisplay);
++ DSI_WRITE(DSI_VID_NUM_CHUNKS, 0);
++ DSI_WRITE(DSI_VID_NULL_SIZE, 0);
++
++ /* Note, unlike Argon firmware, here we DON'T consider sync to be concurrent with porch */
++ DSI_WRITE(DSI_VID_HSA_TIME,
++ (bpp * (mode->hsync_end - mode->hsync_start)) / (8 * dsi->lanes));
++ DSI_WRITE(DSI_VID_HBP_TIME,
++ (bpp * (mode->htotal - mode->hsync_end)) / (8 * dsi->lanes));
++ DSI_WRITE(DSI_VID_HLINE_TIME, (bpp * mode->htotal) / (8 * dsi->lanes));
++ DSI_WRITE(DSI_VID_VSA_LINES, (mode->vsync_end - mode->vsync_start));
++ DSI_WRITE(DSI_VID_VBP_LINES, (mode->vtotal - mode->vsync_end));
++ DSI_WRITE(DSI_VID_VFP_LINES, (mode->vsync_start - mode->vdisplay));
++ DSI_WRITE(DSI_VID_VACTIVE_LINES, mode->vdisplay);
++
++ freq_khz = (bpp * mode->clock) / dsi->lanes;
++
++ dphy_init_khz(dsi, rp1dsi_refclk_freq(dsi) / 1000, freq_khz);
++
++ DSI_WRITE(DSI_PHY_TMR_LPCLK_CFG,
++ (hsfreq_table[dsi->hsfreq_index].clk_lp2hs << DSI_PHY_TMR_LP2HS_LSB) |
++ (hsfreq_table[dsi->hsfreq_index].clk_hs2lp << DSI_PHY_TMR_HS2LP_LSB));
++ DSI_WRITE(DSI_PHY_TMR_CFG,
++ (hsfreq_table[dsi->hsfreq_index].data_lp2hs << DSI_PHY_TMR_LP2HS_LSB) |
++ (hsfreq_table[dsi->hsfreq_index].data_hs2lp << DSI_PHY_TMR_HS2LP_LSB));
++
++ DSI_WRITE(DSI_CLKMGR_CFG, 0x00000505);
++
++ /* Wait for PLL lock */
++ for (timeout = (1 << 14); timeout != 0; --timeout) {
++ usleep_range(10, 50);
++ if (DSI_READ(DSI_PHY_STATUS) & (1 << 0))
++ break;
++ }
++ if (timeout == 0)
++ drm_err(dsi->drm, "RP1DSI: Time out waiting for PLL\n");
++
++ DSI_WRITE(DSI_LPCLK_CTRL, 0x1); /* configure the requesthsclk */
++ DSI_WRITE(DSI_PHY_TST_CTRL0, 0x2);
++ DSI_WRITE(DSI_PCKHDL_CFG, 1 << 2); /* allow bus turnaround */
++ DSI_WRITE(DSI_PWR_UP, 0x1); /* power up */
++
++ /* Now it should be safe to start the external DPI clock divider */
++ rp1dsi_dpiclk_start(dsi, bpp, dsi->lanes);
++
++ /* Wait for all lane(s) to be in Stopstate */
++ mask = (1 << 4);
++ if (dsi->lanes >= 2)
++ mask |= (1 << 7);
++ if (dsi->lanes >= 3)
++ mask |= (1 << 9);
++ if (dsi->lanes >= 4)
++ mask |= (1 << 11);
++ for (timeout = (1 << 10); timeout != 0; --timeout) {
++ usleep_range(10, 50);
++ if ((DSI_READ(DSI_PHY_STATUS) & mask) == mask)
++ break;
++ }
++ if (timeout == 0)
++ drm_err(dsi->drm, "RP1DSI: Time out waiting for lanes (%x %x)\n",
++ mask, DSI_READ(DSI_PHY_STATUS));
++}
++
++void rp1dsi_dsi_send(struct rp1_dsi *dsi, u32 hdr, int len, const u8 *buf)
++{
++ u32 val;
++
++ /* Wait for both FIFOs empty */
++ for (val = 256; val > 0; --val) {
++ if ((DSI_READ(DSI_CMD_PKT_STATUS) & 0xF) == 0x5)
++ break;
++ usleep_range(100, 150);
++ }
++
++ /* Write payload (in 32-bit words) and header */
++ for (; len > 0; len -= 4) {
++ val = *buf++;
++ if (len > 1)
++ val |= (*buf++) << 8;
++ if (len > 2)
++ val |= (*buf++) << 16;
++ if (len > 3)
++ val |= (*buf++) << 24;
++ DSI_WRITE(DSI_GEN_PLD_DATA, val);
++ }
++ DSI_WRITE(DSI_GEN_HDR, hdr);
++
++ /* Wait for both FIFOs empty */
++ for (val = 256; val > 0; --val) {
++ if ((DSI_READ(DSI_CMD_PKT_STATUS) & 0xF) == 0x5)
++ break;
++ usleep_range(100, 150);
++ }
++}
++
++int rp1dsi_dsi_recv(struct rp1_dsi *dsi, int len, u8 *buf)
++{
++ int i, j;
++ u32 val;
++
++ /* Wait until not busy and FIFO not empty */
++ for (i = 1024; i > 0; --i) {
++ val = DSI_READ(DSI_CMD_PKT_STATUS);
++ if ((val & ((1 << 6) | (1 << 4))) == 0)
++ break;
++ usleep_range(100, 150);
++ }
++ if (i == 0)
++ return -EIO;
++
++ for (i = 0; i < len; i += 4) {
++ /* Read fifo must not be empty before all bytes are read */
++ if (DSI_READ(DSI_CMD_PKT_STATUS) & (1 << 4))
++ break;
++
++ val = DSI_READ(DSI_GEN_PLD_DATA);
++ for (j = 0; j < 4 && j + i < len; j++)
++ *buf++ = val >> (8 * j);
++ }
++
++ return (i >= len) ? len : (i > 0) ? i : -EIO;
++}
++
++void rp1dsi_dsi_stop(struct rp1_dsi *dsi)
++{
++ DSI_WRITE(DSI_MODE_CFG, 1); /* Return to Command Mode */
++ DSI_WRITE(DSI_LPCLK_CTRL, 2); /* Stop the HS clock */
++ DSI_WRITE(DSI_PWR_UP, 0x0); /* Power down host controller */
++ DSI_WRITE(DSI_PHYRSTZ, 0); /* PHY into reset. */
++ rp1dsi_dpiclk_stop(dsi);
++}
++
++void rp1dsi_dsi_set_cmdmode(struct rp1_dsi *dsi, int mode)
++{
++ DSI_WRITE(DSI_MODE_CFG, mode);
++}
--- /dev/null
+From 61c3065f89d4447c7e4cf61a466ebc3c4a834ad2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Tue, 19 Sep 2023 17:51:49 +0100
+Subject: [PATCH] drm: Add RP1 DPI driver
+
+Add support for the RP1 DPI hardware.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/rp1/rp1-dpi/Kconfig | 12 +
+ drivers/gpu/drm/rp1/rp1-dpi/Makefile | 5 +
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c | 429 ++++++++++++++++++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h | 69 +++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c | 510 ++++++++++++++++++++++
+ drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c | 486 +++++++++++++++++++++
+ 6 files changed, 1511 insertions(+)
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
+
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/Kconfig
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config DRM_RP1_DPI
++ tristate "DRM Support for RP1 DPI"
++ depends on DRM
++ select MFD_RP1
++ select DRM_GEM_DMA_HELPER
++ select DRM_KMS_HELPER
++ select DRM_VRAM_HELPER
++ select DRM_TTM
++ select DRM_TTM_HELPER
++ help
++ Choose this option to enable Video Out on RP1
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++drm-rp1-dpi-y := rp1_dpi.o rp1_dpi_hw.o rp1_dpi_cfg.o
++
++obj-$(CONFIG_DRM_RP1_DPI) += drm-rp1-dpi.o
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.c
+@@ -0,0 +1,429 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DPI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/mm.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/list.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/printk.h>
++#include <linux/console.h>
++#include <linux/debugfs.h>
++#include <linux/uaccess.h>
++#include <linux/io.h>
++#include <linux/dma-mapping.h>
++#include <linux/cred.h>
++#include <linux/media-bus-format.h>
++#include <linux/pinctrl/consumer.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_mm.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_gem_dma_helper.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_managed.h>
++#include <drm/drm_crtc.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_encoder.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_gem.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_modeset_helper_vtables.h>
++#include <drm/drm_vblank.h>
++#include <drm/drm_of.h>
++
++#include "rp1_dpi.h"
++
++/*
++ * Default bus format, where not specified by a connector/bridge
++ * and not overridden by the OF property "default_bus_fmt".
++ * This value is for compatibility with vc4 and VGA666-style boards,
++ * even though RP1 hardware cannot achieve the full 18-bit depth
++ * with that pinout (MEDIA_BUS_FMT_RGB666_1X24_CPADHI is preferred).
++ */
++static unsigned int default_bus_fmt = MEDIA_BUS_FMT_RGB666_1X18;
++module_param(default_bus_fmt, uint, 0644);
++
++/* -------------------------------------------------------------- */
++
++static void rp1dpi_pipe_update(struct drm_simple_display_pipe *pipe,
++ struct drm_plane_state *old_state)
++{
++ struct drm_pending_vblank_event *event;
++ unsigned long flags;
++ struct drm_framebuffer *fb = pipe->plane.state->fb;
++ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++ struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
++ struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
++ bool can_update = fb && dma_obj && dpi && dpi->pipe_enabled;
++
++ /* (Re-)start DPI-DMA where required; and update FB address */
++ if (can_update) {
++ if (!dpi->dpi_running || fb->format->format != dpi->cur_fmt) {
++ if (dpi->dpi_running &&
++ fb->format->format != dpi->cur_fmt) {
++ rp1dpi_hw_stop(dpi);
++ dpi->dpi_running = false;
++ }
++ if (!dpi->dpi_running) {
++ rp1dpi_hw_setup(dpi,
++ fb->format->format,
++ dpi->bus_fmt,
++ dpi->de_inv,
++ &pipe->crtc.state->mode);
++ dpi->dpi_running = true;
++ }
++ dpi->cur_fmt = fb->format->format;
++ drm_crtc_vblank_on(&pipe->crtc);
++ }
++ rp1dpi_hw_update(dpi, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
++ }
++
++ /* Arm VBLANK event (or call it immediately in some error cases) */
++ spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
++ event = pipe->crtc.state->event;
++ if (event) {
++ pipe->crtc.state->event = NULL;
++ if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
++ drm_crtc_arm_vblank_event(&pipe->crtc, event);
++ else
++ drm_crtc_send_vblank_event(&pipe->crtc, event);
++ }
++ spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
++}
++
++static void rp1dpi_pipe_enable(struct drm_simple_display_pipe *pipe,
++ struct drm_crtc_state *crtc_state,
++ struct drm_plane_state *plane_state)
++{
++ static const unsigned int M = 1000000;
++ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++ struct drm_connector *conn;
++ struct drm_connector_list_iter conn_iter;
++ unsigned int fpix, fdiv, fvco;
++ int ret;
++
++ /* Look up the connector attached to DPI so we can get the
++ * bus_format. Ideally the bridge would tell us the
++ * bus_format we want, but it doesn't yet, so assume that it's
++ * uniform throughout the bridge chain.
++ */
++ dev_info(&dpi->pdev->dev, __func__);
++ drm_connector_list_iter_begin(pipe->encoder.dev, &conn_iter);
++ drm_for_each_connector_iter(conn, &conn_iter) {
++ if (conn->encoder == &pipe->encoder) {
++ dpi->de_inv = !!(conn->display_info.bus_flags &
++ DRM_BUS_FLAG_DE_LOW);
++ dpi->clk_inv = !!(conn->display_info.bus_flags &
++ DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE);
++ if (conn->display_info.num_bus_formats)
++ dpi->bus_fmt = conn->display_info.bus_formats[0];
++ break;
++ }
++ }
++ drm_connector_list_iter_end(&conn_iter);
++
++ /* Set DPI clock to desired frequency. Currently (experimentally)
++ * we take control of the VideoPLL, to ensure we can generate it
++ * accurately. NB: this prevents concurrent use of DPI and VEC!
++ * Magic numbers ensure the parent clock is within [100MHz, 200MHz]
++ * with VCO in [1GHz, 1.33GHz]. The initial divide is by 6, 8 or 10.
++ */
++ fpix = 1000 * pipe->crtc.state->mode.clock;
++ fpix = clamp(fpix, 1 * M, 200 * M);
++ fdiv = fpix;
++ while (fdiv < 100 * M)
++ fdiv *= 2;
++ fvco = fdiv * 2 * DIV_ROUND_UP(500 * M, fdiv);
++ ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLCORE], fvco);
++ if (ret)
++ dev_err(&dpi->pdev->dev, "Failed to set PLL VCO to %u (%d)", fvco, ret);
++ ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_PLLDIV], fdiv);
++ if (ret)
++ dev_err(&dpi->pdev->dev, "Failed to set PLL output to %u (%d)", fdiv, ret);
++ ret = clk_set_rate(dpi->clocks[RP1DPI_CLK_DPI], fpix);
++ if (ret)
++ dev_err(&dpi->pdev->dev, "Failed to set DPI clock to %u (%d)", fpix, ret);
++
++ rp1dpi_vidout_setup(dpi, dpi->clk_inv);
++ clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLCORE]);
++ clk_prepare_enable(dpi->clocks[RP1DPI_CLK_PLLDIV]);
++ pinctrl_pm_select_default_state(&dpi->pdev->dev);
++ clk_prepare_enable(dpi->clocks[RP1DPI_CLK_DPI]);
++ dev_info(&dpi->pdev->dev, "Want %u /%u %u /%u %u; got VCO=%lu DIV=%lu DPI=%lu",
++ fvco, fvco / fdiv, fdiv, fdiv / fpix, fpix,
++ clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLCORE]),
++ clk_get_rate(dpi->clocks[RP1DPI_CLK_PLLDIV]),
++ clk_get_rate(dpi->clocks[RP1DPI_CLK_DPI]));
++
++ /* Start DPI-DMA. pipe already has the new crtc and plane state. */
++ dpi->pipe_enabled = true;
++ dpi->cur_fmt = 0xdeadbeef;
++ rp1dpi_pipe_update(pipe, 0);
++}
++
++static void rp1dpi_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++
++ dev_info(&dpi->pdev->dev, __func__);
++ drm_crtc_vblank_off(&pipe->crtc);
++ if (dpi->dpi_running) {
++ rp1dpi_hw_stop(dpi);
++ dpi->dpi_running = false;
++ }
++ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
++ pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
++ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLDIV]);
++ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_PLLCORE]);
++ dpi->pipe_enabled = false;
++}
++
++static int rp1dpi_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
++{
++ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++
++ if (dpi)
++ rp1dpi_hw_vblank_ctrl(dpi, 1);
++
++ return 0;
++}
++
++static void rp1dpi_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
++{
++ struct rp1_dpi *dpi = pipe->crtc.dev->dev_private;
++
++ if (dpi)
++ rp1dpi_hw_vblank_ctrl(dpi, 0);
++}
++
++static const struct drm_simple_display_pipe_funcs rp1dpi_pipe_funcs = {
++ .enable = rp1dpi_pipe_enable,
++ .update = rp1dpi_pipe_update,
++ .disable = rp1dpi_pipe_disable,
++ .prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
++ .enable_vblank = rp1dpi_pipe_enable_vblank,
++ .disable_vblank = rp1dpi_pipe_disable_vblank,
++};
++
++static const struct drm_mode_config_funcs rp1dpi_mode_funcs = {
++ .fb_create = drm_gem_fb_create,
++ .atomic_check = drm_atomic_helper_check,
++ .atomic_commit = drm_atomic_helper_commit,
++};
++
++static void rp1dpi_stopall(struct drm_device *drm)
++{
++ if (drm->dev_private) {
++ struct rp1_dpi *dpi = drm->dev_private;
++
++ if (dpi->dpi_running || rp1dpi_hw_busy(dpi)) {
++ rp1dpi_hw_stop(dpi);
++ clk_disable_unprepare(dpi->clocks[RP1DPI_CLK_DPI]);
++ dpi->dpi_running = false;
++ }
++ rp1dpi_vidout_poweroff(dpi);
++ pinctrl_pm_select_sleep_state(&dpi->pdev->dev);
++ }
++}
++
++DEFINE_DRM_GEM_DMA_FOPS(rp1dpi_fops);
++
++static struct drm_driver rp1dpi_driver = {
++ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
++ .fops = &rp1dpi_fops,
++ .name = "drm-rp1-dpi",
++ .desc = "drm-rp1-dpi",
++ .date = "0",
++ .major = 1,
++ .minor = 0,
++ DRM_GEM_DMA_DRIVER_OPS,
++ .release = rp1dpi_stopall,
++};
++
++static const u32 rp1dpi_formats[] = {
++ DRM_FORMAT_XRGB8888,
++ DRM_FORMAT_XBGR8888,
++ DRM_FORMAT_RGB888,
++ DRM_FORMAT_BGR888,
++ DRM_FORMAT_RGB565
++};
++
++static int rp1dpi_platform_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct drm_device *drm;
++ struct rp1_dpi *dpi;
++ struct drm_bridge *bridge = NULL;
++ struct drm_panel *panel;
++ int i, ret;
++
++ dev_info(dev, __func__);
++ ret = drm_of_find_panel_or_bridge(pdev->dev.of_node, 0, 0,
++ &panel, &bridge);
++ if (ret) {
++ dev_info(dev, "%s: bridge not found\n", __func__);
++ return -EPROBE_DEFER;
++ }
++ if (panel) {
++ bridge = devm_drm_panel_bridge_add(dev, panel);
++ if (IS_ERR(bridge))
++ return PTR_ERR(bridge);
++ }
++
++ drm = drm_dev_alloc(&rp1dpi_driver, dev);
++ if (IS_ERR(drm)) {
++ dev_info(dev, "%s %d", __func__, (int)__LINE__);
++ ret = PTR_ERR(drm);
++ return ret;
++ }
++ dpi = drmm_kzalloc(drm, sizeof(*dpi), GFP_KERNEL);
++ if (!dpi) {
++ dev_info(dev, "%s %d", __func__, (int)__LINE__);
++ drm_dev_put(drm);
++ return -ENOMEM;
++ }
++
++ init_completion(&dpi->finished);
++ dpi->drm = drm;
++ dpi->pdev = pdev;
++ drm->dev_private = dpi;
++ platform_set_drvdata(pdev, drm);
++
++ dpi->bus_fmt = default_bus_fmt;
++ ret = of_property_read_u32(dev->of_node, "default_bus_fmt", &dpi->bus_fmt);
++
++ for (i = 0; i < RP1DPI_NUM_HW_BLOCKS; i++) {
++ dpi->hw_base[i] =
++ devm_ioremap_resource(dev,
++ platform_get_resource(dpi->pdev, IORESOURCE_MEM, i));
++ if (IS_ERR(dpi->hw_base[i])) {
++ ret = PTR_ERR(dpi->hw_base[i]);
++ dev_err(dev, "Error memory mapping regs[%d]\n", i);
++ goto err_free_drm;
++ }
++ }
++ ret = platform_get_irq(dpi->pdev, 0);
++ if (ret > 0)
++ ret = devm_request_irq(dev, ret, rp1dpi_hw_isr,
++ IRQF_SHARED, "rp1-dpi", dpi);
++ if (ret) {
++ dev_err(dev, "Unable to request interrupt\n");
++ ret = -EINVAL;
++ goto err_free_drm;
++ }
++ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
++
++ for (i = 0; i < RP1DPI_NUM_CLOCKS; i++) {
++ static const char * const myclocknames[RP1DPI_NUM_CLOCKS] = {
++ "dpiclk", "plldiv", "pllcore"
++ };
++ dpi->clocks[i] = devm_clk_get(dev, myclocknames[i]);
++ if (IS_ERR(dpi->clocks[i])) {
++ ret = PTR_ERR(dpi->clocks[i]);
++ goto err_free_drm;
++ }
++ }
++
++ ret = drmm_mode_config_init(drm);
++ if (ret)
++ goto err_free_drm;
++
++ drm->mode_config.max_width = 4096;
++ drm->mode_config.max_height = 4096;
++ drm->mode_config.fb_base = 0;
++ drm->mode_config.preferred_depth = 32;
++ drm->mode_config.prefer_shadow = 0;
++ drm->mode_config.prefer_shadow_fbdev = 1;
++ drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
++ drm->mode_config.funcs = &rp1dpi_mode_funcs;
++ drm_vblank_init(drm, 1);
++
++ ret = drm_simple_display_pipe_init(drm,
++ &dpi->pipe,
++ &rp1dpi_pipe_funcs,
++ rp1dpi_formats,
++ ARRAY_SIZE(rp1dpi_formats),
++ NULL, NULL);
++ if (!ret)
++ ret = drm_simple_display_pipe_attach_bridge(&dpi->pipe, bridge);
++ if (ret)
++ goto err_free_drm;
++
++ drm_mode_config_reset(drm);
++
++ ret = drm_dev_register(drm, 0);
++ if (ret)
++ goto err_free_drm;
++
++ drm_fbdev_generic_setup(drm, 32);
++
++ dev_info(dev, "%s success\n", __func__);
++ return ret;
++
++err_free_drm:
++ dev_err(dev, "%s fail %d\n", __func__, ret);
++ drm_dev_put(drm);
++ return ret;
++}
++
++static int rp1dpi_platform_remove(struct platform_device *pdev)
++{
++ struct drm_device *drm = platform_get_drvdata(pdev);
++
++ rp1dpi_stopall(drm);
++ drm_dev_unregister(drm);
++ drm_atomic_helper_shutdown(drm);
++ drm_dev_put(drm);
++
++ return 0;
++}
++
++static void rp1dpi_platform_shutdown(struct platform_device *pdev)
++{
++ struct drm_device *drm = platform_get_drvdata(pdev);
++
++ rp1dpi_stopall(drm);
++}
++
++static const struct of_device_id rp1dpi_of_match[] = {
++ {
++ .compatible = "raspberrypi,rp1dpi",
++ },
++ { /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rp1dpi_of_match);
++
++static struct platform_driver rp1dpi_platform_driver = {
++ .probe = rp1dpi_platform_probe,
++ .remove = rp1dpi_platform_remove,
++ .shutdown = rp1dpi_platform_shutdown,
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .of_match_table = rp1dpi_of_match,
++ },
++};
++
++module_platform_driver(rp1dpi_platform_driver);
++
++MODULE_AUTHOR("Nick Hollinghurst");
++MODULE_DESCRIPTION("DRM driver for DPI output on Raspberry Pi RP1");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi.h
+@@ -0,0 +1,69 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/types.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <drm/drm_device.h>
++#include <drm/drm_simple_kms_helper.h>
++
++#define MODULE_NAME "drm-rp1-dpi"
++#define DRIVER_NAME "drm-rp1-dpi"
++
++/* ---------------------------------------------------------------------- */
++
++#define RP1DPI_HW_BLOCK_DPI 0
++#define RP1DPI_HW_BLOCK_CFG 1
++#define RP1DPI_NUM_HW_BLOCKS 2
++
++#define RP1DPI_CLK_DPI 0
++#define RP1DPI_CLK_PLLDIV 1
++#define RP1DPI_CLK_PLLCORE 2
++#define RP1DPI_NUM_CLOCKS 3
++
++/* ---------------------------------------------------------------------- */
++
++struct rp1_dpi {
++ /* DRM and platform device pointers */
++ struct drm_device *drm;
++ struct platform_device *pdev;
++
++ /* Framework and helper objects */
++ struct drm_simple_display_pipe pipe;
++ struct drm_connector connector;
++
++ /* Clocks: Video PLL, its primary divider, and DPI clock. */
++ struct clk *clocks[RP1DPI_NUM_CLOCKS];
++
++ /* Block (DPI, VOCFG) base addresses, and current state */
++ void __iomem *hw_base[RP1DPI_NUM_HW_BLOCKS];
++ u32 cur_fmt;
++ u32 bus_fmt;
++ bool de_inv, clk_inv;
++ bool dpi_running, pipe_enabled;
++ struct completion finished;
++};
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the DPI/DMA block */
++
++void rp1dpi_hw_setup(struct rp1_dpi *dpi,
++ u32 in_format,
++ u32 bus_format,
++ bool de_inv,
++ struct drm_display_mode const *mode);
++void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride);
++void rp1dpi_hw_stop(struct rp1_dpi *dpi);
++int rp1dpi_hw_busy(struct rp1_dpi *dpi);
++irqreturn_t rp1dpi_hw_isr(int irq, void *dev);
++void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the VIDEO OUT CFG block and check RP1 platform */
++
++void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge);
++void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi);
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_cfg.c
+@@ -0,0 +1,510 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DPI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/rp1_platform.h>
++
++#include "rp1_dpi.h"
++
++// =============================================================================
++// Register : VIDEO_OUT_CFG_SEL
++// JTAG access : synchronous
++// Description : Selects source: VEC or DPI
++#define VIDEO_OUT_CFG_SEL_OFFSET 0x00000000
++#define VIDEO_OUT_CFG_SEL_BITS 0x00000013
++#define VIDEO_OUT_CFG_SEL_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_SEL_PCLK_INV
++// Description : Select dpi_pclk output port polarity inversion.
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_RESET 0x0
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_BITS 0x00000010
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_MSB 4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_LSB 4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_SEL_PAD_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_RESET 0x0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_BITS 0x00000002
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_MSB 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_LSB 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_SEL_VDAC_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_RESET 0x0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS 0x00000001
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_MSB 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_LSB 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_VDAC_CFG
++// JTAG access : synchronous
++// Description : Configure SNPS VDAC
++#define VIDEO_OUT_CFG_VDAC_CFG_OFFSET 0x00000004
++#define VIDEO_OUT_CFG_VDAC_CFG_BITS 0x1fffffff
++#define VIDEO_OUT_CFG_VDAC_CFG_RESET 0x0003ffff
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENCTR
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_BITS 0x1c000000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_MSB 28
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_LSB 26
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENSC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_BITS 0x03800000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_MSB 25
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_LSB 23
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENDAC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_BITS 0x00700000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_MSB 22
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_LSB 20
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENVBG
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_BITS 0x00080000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_MSB 19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_LSB 19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_BITS 0x00040000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_MSB 18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_LSB 18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC2GC
++// Description : dac2 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_RESET 0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_BITS 0x0003f000
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_MSB 17
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_LSB 12
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC1GC
++// Description : dac1 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_RESET 0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_BITS 0x00000fc0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_MSB 11
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_LSB 6
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC0GC
++// Description : dac0 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_RESET 0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_BITS 0x0000003f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_MSB 5
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_LSB 0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_VDAC_STATUS
++// JTAG access : synchronous
++// Description : Read VDAC status
++#define VIDEO_OUT_CFG_VDAC_STATUS_OFFSET 0x00000008
++#define VIDEO_OUT_CFG_VDAC_STATUS_BITS 0x00000017
++#define VIDEO_OUT_CFG_VDAC_STATUS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_BITS 0x00000010
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_MSB 4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_LSB 4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_RESET "-"
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_BITS 0x00000007
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_MSB 2
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_LSB 0
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_MEM_PD
++// JTAG access : synchronous
++// Description : Control memory power down
++#define VIDEO_OUT_CFG_MEM_PD_OFFSET 0x0000000c
++#define VIDEO_OUT_CFG_MEM_PD_BITS 0x00000003
++#define VIDEO_OUT_CFG_MEM_PD_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_MEM_PD_VEC
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_MEM_PD_VEC_BITS 0x00000002
++#define VIDEO_OUT_CFG_MEM_PD_VEC_MSB 1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_LSB 1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_MEM_PD_DPI
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_BITS 0x00000001
++#define VIDEO_OUT_CFG_MEM_PD_DPI_MSB 0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_LSB 0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_TEST_OVERRIDE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_OFFSET 0x00000010
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_BITS 0xffffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_PAD
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_RESET 0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_BITS 0x80000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_MSB 31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_LSB 31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_RESET 0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS 0x40000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_MSB 30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_LSB 30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_RESET 0x00000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_BITS 0x3fffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_MSB 29
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_LSB 0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTR
++// JTAG access : synchronous
++// Description : Raw Interrupts
++#define VIDEO_OUT_CFG_INTR_OFFSET 0x00000014
++#define VIDEO_OUT_CFG_INTR_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTR_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTR_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTR_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTR_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTR_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTR_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTR_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTR_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTR_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTR_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTR_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTR_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTR_VEC_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTE
++// JTAG access : synchronous
++// Description : Interrupt Enable
++#define VIDEO_OUT_CFG_INTE_OFFSET 0x00000018
++#define VIDEO_OUT_CFG_INTE_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTE_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTE_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTE_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTE_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTE_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTE_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTE_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTE_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTE_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTE_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTE_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTE_VEC_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTF
++// JTAG access : synchronous
++// Description : Interrupt Force
++#define VIDEO_OUT_CFG_INTF_OFFSET 0x0000001c
++#define VIDEO_OUT_CFG_INTF_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTF_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTF_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTF_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTF_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTF_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTF_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTF_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTF_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTF_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTF_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTF_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTF_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTF_VEC_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTS
++// JTAG access : synchronous
++// Description : Interrupt status after masking & forcing
++#define VIDEO_OUT_CFG_INTS_OFFSET 0x00000020
++#define VIDEO_OUT_CFG_INTS_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTS_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTS_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTS_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTS_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTS_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTS_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTS_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTS_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTS_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTS_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTS_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTS_VEC_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_BLOCK_ID
++// JTAG access : synchronous
++// Description : Block Identifier
++// Hexadecimal representation of "VOCF"
++#define VIDEO_OUT_CFG_BLOCK_ID_OFFSET 0x00000024
++#define VIDEO_OUT_CFG_BLOCK_ID_BITS 0xffffffff
++#define VIDEO_OUT_CFG_BLOCK_ID_RESET 0x564f4346
++#define VIDEO_OUT_CFG_BLOCK_ID_MSB 31
++#define VIDEO_OUT_CFG_BLOCK_ID_LSB 0
++#define VIDEO_OUT_CFG_BLOCK_ID_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INSTANCE_ID
++// JTAG access : synchronous
++// Description : Block Instance Identifier
++#define VIDEO_OUT_CFG_INSTANCE_ID_OFFSET 0x00000028
++#define VIDEO_OUT_CFG_INSTANCE_ID_BITS 0x0000000f
++#define VIDEO_OUT_CFG_INSTANCE_ID_RESET 0x00000000
++#define VIDEO_OUT_CFG_INSTANCE_ID_MSB 3
++#define VIDEO_OUT_CFG_INSTANCE_ID_LSB 0
++#define VIDEO_OUT_CFG_INSTANCE_ID_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_AUTO
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_OFFSET 0x0000002c
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_RESET 0x00000007
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_PARALLEL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_OFFSET 0x00000030
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_RESET 0x00000006
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_CTRL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_OFFSET 0x00000034
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_TRIG
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_OFFSET 0x00000038
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_DONE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_OFFSET 0x0000003c
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_VEC
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_DPI
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++
++#define CFG_WRITE(reg, val) writel((val), dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++#define CFG_READ(reg) readl(dpi->hw_base[RP1DPI_HW_BLOCK_CFG] + (reg ## _OFFSET))
++
++void rp1dpi_vidout_setup(struct rp1_dpi *dpi, bool drive_negedge)
++{
++ /*
++ * We assume DPI and VEC can't be used at the same time (due to
++ * clashing requirements for PLL_VIDEO, and potentially for VDAC).
++ * We therefore leave VEC memories powered down.
++ */
++ CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_VEC_BITS);
++ CFG_WRITE(VIDEO_OUT_CFG_TEST_OVERRIDE,
++ VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS);
++
++ /* DPI->Pads; DPI->VDAC; optionally flip PCLK polarity */
++ CFG_WRITE(VIDEO_OUT_CFG_SEL,
++ drive_negedge ? VIDEO_OUT_CFG_SEL_PCLK_INV_BITS : 0);
++
++ /* configure VDAC for 3 channels, bandgap on, 710mV swing */
++ CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
++
++ /* enable DPI interrupt */
++ CFG_WRITE(VIDEO_OUT_CFG_INTE, VIDEO_OUT_CFG_INTE_DPI_BITS);
++}
++
++void rp1dpi_vidout_poweroff(struct rp1_dpi *dpi)
++{
++ /* disable DPI interrupt */
++ CFG_WRITE(VIDEO_OUT_CFG_INTE, 0);
++
++ /* Ensure VDAC is turned off; power down DPI,VEC memories */
++ CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
++ CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_BITS);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-dpi/rp1_dpi_hw.c
+@@ -0,0 +1,486 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DPI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/media-bus-format.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_dpi.h"
++
++// --- DPI DMA REGISTERS ---
++
++// Control
++#define DPI_DMA_CONTROL 0x0
++#define DPI_DMA_CONTROL_ARM_SHIFT 0
++#define DPI_DMA_CONTROL_ARM_MASK BIT(DPI_DMA_CONTROL_ARM_SHIFT)
++#define DPI_DMA_CONTROL_ALIGN16_SHIFT 2
++#define DPI_DMA_CONTROL_ALIGN16_MASK BIT(DPI_DMA_CONTROL_ALIGN16_SHIFT)
++#define DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT 1
++#define DPI_DMA_CONTROL_AUTO_REPEAT_MASK BIT(DPI_DMA_CONTROL_AUTO_REPEAT_SHIFT)
++#define DPI_DMA_CONTROL_HIGH_WATER_SHIFT 3
++#define DPI_DMA_CONTROL_HIGH_WATER_MASK (0x1FF << DPI_DMA_CONTROL_HIGH_WATER_SHIFT)
++#define DPI_DMA_CONTROL_DEN_POL_SHIFT 12
++#define DPI_DMA_CONTROL_DEN_POL_MASK BIT(DPI_DMA_CONTROL_DEN_POL_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_POL_SHIFT 13
++#define DPI_DMA_CONTROL_HSYNC_POL_MASK BIT(DPI_DMA_CONTROL_HSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_POL_SHIFT 14
++#define DPI_DMA_CONTROL_VSYNC_POL_MASK BIT(DPI_DMA_CONTROL_VSYNC_POL_SHIFT)
++#define DPI_DMA_CONTROL_COLORM_SHIFT 15
++#define DPI_DMA_CONTROL_COLORM_MASK BIT(DPI_DMA_CONTROL_COLORM_SHIFT)
++#define DPI_DMA_CONTROL_SHUTDN_SHIFT 16
++#define DPI_DMA_CONTROL_SHUTDN_MASK BIT(DPI_DMA_CONTROL_SHUTDN_SHIFT)
++#define DPI_DMA_CONTROL_HBP_EN_SHIFT 17
++#define DPI_DMA_CONTROL_HBP_EN_MASK BIT(DPI_DMA_CONTROL_HBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HFP_EN_SHIFT 18
++#define DPI_DMA_CONTROL_HFP_EN_MASK BIT(DPI_DMA_CONTROL_HFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VBP_EN_SHIFT 19
++#define DPI_DMA_CONTROL_VBP_EN_MASK BIT(DPI_DMA_CONTROL_VBP_EN_SHIFT)
++#define DPI_DMA_CONTROL_VFP_EN_SHIFT 20
++#define DPI_DMA_CONTROL_VFP_EN_MASK BIT(DPI_DMA_CONTROL_VFP_EN_SHIFT)
++#define DPI_DMA_CONTROL_HSYNC_EN_SHIFT 21
++#define DPI_DMA_CONTROL_HSYNC_EN_MASK BIT(DPI_DMA_CONTROL_HSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_VSYNC_EN_SHIFT 22
++#define DPI_DMA_CONTROL_VSYNC_EN_MASK BIT(DPI_DMA_CONTROL_VSYNC_EN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_IMMED_SHIFT 23
++#define DPI_DMA_CONTROL_FORCE_IMMED_MASK BIT(DPI_DMA_CONTROL_FORCE_IMMED_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT 24
++#define DPI_DMA_CONTROL_FORCE_DRAIN_MASK BIT(DPI_DMA_CONTROL_FORCE_DRAIN_SHIFT)
++#define DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT 25
++#define DPI_DMA_CONTROL_FORCE_EMPTY_MASK BIT(DPI_DMA_CONTROL_FORCE_EMPTY_SHIFT)
++
++// IRQ_ENABLES
++#define DPI_DMA_IRQ_EN 0x04
++#define DPI_DMA_IRQ_EN_DMA_READY_SHIFT 0
++#define DPI_DMA_IRQ_EN_DMA_READY_MASK BIT(DPI_DMA_IRQ_EN_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT 1
++#define DPI_DMA_IRQ_EN_UNDERFLOW_MASK BIT(DPI_DMA_IRQ_EN_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_EN_FRAME_START_SHIFT 2
++#define DPI_DMA_IRQ_EN_FRAME_START_MASK BIT(DPI_DMA_IRQ_EN_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT 3
++#define DPI_DMA_IRQ_EN_AFIFO_EMPTY_MASK BIT(DPI_DMA_IRQ_EN_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_EN_TE_SHIFT 4
++#define DPI_DMA_IRQ_EN_TE_MASK BIT(DPI_DMA_IRQ_EN_TE_SHIFT)
++#define DPI_DMA_IRQ_EN_ERROR_SHIFT 5
++#define DPI_DMA_IRQ_EN_ERROR_MASK BIT(DPI_DMA_IRQ_EN_ERROR_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_SHIFT 6
++#define DPI_DMA_IRQ_EN_MATCH_MASK BIT(DPI_DMA_IRQ_EN_MATCH_SHIFT)
++#define DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT 16
++#define DPI_DMA_IRQ_EN_MATCH_LINE_MASK (0xFFF << DPI_DMA_IRQ_EN_MATCH_LINE_SHIFT)
++
++// IRQ_FLAGS
++#define DPI_DMA_IRQ_FLAGS 0x08
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT 0
++#define DPI_DMA_IRQ_FLAGS_DMA_READY_MASK BIT(DPI_DMA_IRQ_FLAGS_DMA_READY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT 1
++#define DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK BIT(DPI_DMA_IRQ_FLAGS_UNDERFLOW_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT 2
++#define DPI_DMA_IRQ_FLAGS_FRAME_START_MASK BIT(DPI_DMA_IRQ_FLAGS_FRAME_START_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT 3
++#define DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK BIT(DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_TE_SHIFT 4
++#define DPI_DMA_IRQ_FLAGS_TE_MASK BIT(DPI_DMA_IRQ_FLAGS_TE_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_ERROR_SHIFT 5
++#define DPI_DMA_IRQ_FLAGS_ERROR_MASK BIT(DPI_DMA_IRQ_FLAGS_ERROR_SHIFT)
++#define DPI_DMA_IRQ_FLAGS_MATCH_SHIFT 6
++#define DPI_DMA_IRQ_FLAGS_MATCH_MASK BIT(DPI_DMA_IRQ_FLAGS_MATCH_SHIFT)
++
++// QOS
++#define DPI_DMA_QOS 0xC
++#define DPI_DMA_QOS_DQOS_SHIFT 0
++#define DPI_DMA_QOS_DQOS_MASK (0xF << DPI_DMA_QOS_DQOS_SHIFT)
++#define DPI_DMA_QOS_ULEV_SHIFT 4
++#define DPI_DMA_QOS_ULEV_MASK (0xF << DPI_DMA_QOS_ULEV_SHIFT)
++#define DPI_DMA_QOS_UQOS_SHIFT 8
++#define DPI_DMA_QOS_UQOS_MASK (0xF << DPI_DMA_QOS_UQOS_SHIFT)
++#define DPI_DMA_QOS_LLEV_SHIFT 12
++#define DPI_DMA_QOS_LLEV_MASK (0xF << DPI_DMA_QOS_LLEV_SHIFT)
++#define DPI_DMA_QOS_LQOS_SHIFT 16
++#define DPI_DMA_QOS_LQOS_MASK (0xF << DPI_DMA_QOS_LQOS_SHIFT)
++
++// Panics
++#define DPI_DMA_PANICS 0x38
++#define DPI_DMA_PANICS_UPPER_COUNT_SHIFT 0
++#define DPI_DMA_PANICS_UPPER_COUNT_MASK \
++ (0x0000FFFF << DPI_DMA_PANICS_UPPER_COUNT_SHIFT)
++#define DPI_DMA_PANICS_LOWER_COUNT_SHIFT 16
++#define DPI_DMA_PANICS_LOWER_COUNT_MASK \
++ (0x0000FFFF << DPI_DMA_PANICS_LOWER_COUNT_SHIFT)
++
++// DMA Address Lower:
++#define DPI_DMA_DMA_ADDR_L 0x10
++
++// DMA Address Upper:
++#define DPI_DMA_DMA_ADDR_H 0x40
++
++// DMA stride
++#define DPI_DMA_DMA_STRIDE 0x14
++
++// Visible Area
++#define DPI_DMA_VISIBLE_AREA 0x18
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT 0
++#define DPI_DMA_VISIBLE_AREA_ROWSM1_MASK (0x0FFF << DPI_DMA_VISIBLE_AREA_ROWSM1_SHIFT)
++#define DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT 16
++#define DPI_DMA_VISIBLE_AREA_COLSM1_MASK (0x0FFF << DPI_DMA_VISIBLE_AREA_COLSM1_SHIFT)
++
++// Sync width
++#define DPI_DMA_SYNC_WIDTH 0x1C
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT 0
++#define DPI_DMA_SYNC_WIDTH_ROWSM1_MASK (0x0FFF << DPI_DMA_SYNC_WIDTH_ROWSM1_SHIFT)
++#define DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT 16
++#define DPI_DMA_SYNC_WIDTH_COLSM1_MASK (0x0FFF << DPI_DMA_SYNC_WIDTH_COLSM1_SHIFT)
++
++// Back porch
++#define DPI_DMA_BACK_PORCH 0x20
++#define DPI_DMA_BACK_PORCH_ROWSM1_SHIFT 0
++#define DPI_DMA_BACK_PORCH_ROWSM1_MASK (0x0FFF << DPI_DMA_BACK_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_BACK_PORCH_COLSM1_SHIFT 16
++#define DPI_DMA_BACK_PORCH_COLSM1_MASK (0x0FFF << DPI_DMA_BACK_PORCH_COLSM1_SHIFT)
++
++// Front porch
++#define DPI_DMA_FRONT_PORCH 0x24
++#define DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT 0
++#define DPI_DMA_FRONT_PORCH_ROWSM1_MASK (0x0FFF << DPI_DMA_FRONT_PORCH_ROWSM1_SHIFT)
++#define DPI_DMA_FRONT_PORCH_COLSM1_SHIFT 16
++#define DPI_DMA_FRONT_PORCH_COLSM1_MASK (0x0FFF << DPI_DMA_FRONT_PORCH_COLSM1_SHIFT)
++
++// Input masks
++#define DPI_DMA_IMASK 0x2C
++#define DPI_DMA_IMASK_R_SHIFT 0
++#define DPI_DMA_IMASK_R_MASK (0x3FF << DPI_DMA_IMASK_R_SHIFT)
++#define DPI_DMA_IMASK_G_SHIFT 10
++#define DPI_DMA_IMASK_G_MASK (0x3FF << DPI_DMA_IMASK_G_SHIFT)
++#define DPI_DMA_IMASK_B_SHIFT 20
++#define DPI_DMA_IMASK_B_MASK (0x3FF << DPI_DMA_IMASK_B_SHIFT)
++
++// Output Masks
++#define DPI_DMA_OMASK 0x30
++#define DPI_DMA_OMASK_R_SHIFT 0
++#define DPI_DMA_OMASK_R_MASK (0x3FF << DPI_DMA_OMASK_R_SHIFT)
++#define DPI_DMA_OMASK_G_SHIFT 10
++#define DPI_DMA_OMASK_G_MASK (0x3FF << DPI_DMA_OMASK_G_SHIFT)
++#define DPI_DMA_OMASK_B_SHIFT 20
++#define DPI_DMA_OMASK_B_MASK (0x3FF << DPI_DMA_OMASK_B_SHIFT)
++
++// Shifts
++#define DPI_DMA_SHIFT 0x28
++#define DPI_DMA_SHIFT_IR_SHIFT 0
++#define DPI_DMA_SHIFT_IR_MASK (0x1F << DPI_DMA_SHIFT_IR_SHIFT)
++#define DPI_DMA_SHIFT_IG_SHIFT 5
++#define DPI_DMA_SHIFT_IG_MASK (0x1F << DPI_DMA_SHIFT_IG_SHIFT)
++#define DPI_DMA_SHIFT_IB_SHIFT 10
++#define DPI_DMA_SHIFT_IB_MASK (0x1F << DPI_DMA_SHIFT_IB_SHIFT)
++#define DPI_DMA_SHIFT_OR_SHIFT 15
++#define DPI_DMA_SHIFT_OR_MASK (0x1F << DPI_DMA_SHIFT_OR_SHIFT)
++#define DPI_DMA_SHIFT_OG_SHIFT 20
++#define DPI_DMA_SHIFT_OG_MASK (0x1F << DPI_DMA_SHIFT_OG_SHIFT)
++#define DPI_DMA_SHIFT_OB_SHIFT 25
++#define DPI_DMA_SHIFT_OB_MASK (0x1F << DPI_DMA_SHIFT_OB_SHIFT)
++
++// Scaling
++#define DPI_DMA_RGBSZ 0x34
++#define DPI_DMA_RGBSZ_BPP_SHIFT 16
++#define DPI_DMA_RGBSZ_BPP_MASK (0x3 << DPI_DMA_RGBSZ_BPP_SHIFT)
++#define DPI_DMA_RGBSZ_R_SHIFT 0
++#define DPI_DMA_RGBSZ_R_MASK (0xF << DPI_DMA_RGBSZ_R_SHIFT)
++#define DPI_DMA_RGBSZ_G_SHIFT 4
++#define DPI_DMA_RGBSZ_G_MASK (0xF << DPI_DMA_RGBSZ_G_SHIFT)
++#define DPI_DMA_RGBSZ_B_SHIFT 8
++#define DPI_DMA_RGBSZ_B_MASK (0xF << DPI_DMA_RGBSZ_B_SHIFT)
++
++// Status
++#define DPI_DMA_STATUS 0x3c
++
++#define BITS(field, val) (((val) << (field ## _SHIFT)) & (field ## _MASK))
++
++static unsigned int rp1dpi_hw_read(struct rp1_dpi *dpi, unsigned int reg)
++{
++ void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
++
++ return readl(addr);
++}
++
++static void rp1dpi_hw_write(struct rp1_dpi *dpi, unsigned int reg, unsigned int val)
++{
++ void __iomem *addr = dpi->hw_base[RP1DPI_HW_BLOCK_DPI] + reg;
++
++ writel(val, addr);
++}
++
++int rp1dpi_hw_busy(struct rp1_dpi *dpi)
++{
++ return (rp1dpi_hw_read(dpi, DPI_DMA_STATUS) & 0xF8F) ? 1 : 0;
++}
++
++/* Table of supported input (in-memory/DMA) pixel formats. */
++struct rp1dpi_ipixfmt {
++ u32 format; /* DRM format code */
++ u32 mask; /* RGB masks (10 bits each, left justified) */
++ u32 shift; /* RGB MSB positions in the memory word */
++ u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */
++};
++
++#define IMASK_RGB(r, g, b) (BITS(DPI_DMA_IMASK_R, r) | \
++ BITS(DPI_DMA_IMASK_G, g) | \
++ BITS(DPI_DMA_IMASK_B, b))
++#define OMASK_RGB(r, g, b) (BITS(DPI_DMA_OMASK_R, r) | \
++ BITS(DPI_DMA_OMASK_G, g) | \
++ BITS(DPI_DMA_OMASK_B, b))
++#define ISHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_IR, r) | \
++ BITS(DPI_DMA_SHIFT_IG, g) | \
++ BITS(DPI_DMA_SHIFT_IB, b))
++#define OSHIFT_RGB(r, g, b) (BITS(DPI_DMA_SHIFT_OR, r) | \
++ BITS(DPI_DMA_SHIFT_OG, g) | \
++ BITS(DPI_DMA_SHIFT_OB, b))
++
++static const struct rp1dpi_ipixfmt my_formats[] = {
++ {
++ .format = DRM_FORMAT_XRGB8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
++ .format = DRM_FORMAT_XBGR8888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 3),
++ },
++ {
++ .format = DRM_FORMAT_RGB888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
++ },
++ {
++ .format = DRM_FORMAT_BGR888,
++ .mask = IMASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = ISHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_BPP, 2),
++ },
++ {
++ .format = DRM_FORMAT_RGB565,
++ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
++ .shift = ISHIFT_RGB(15, 10, 4),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
++ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
++ },
++ {
++ .format = DRM_FORMAT_BGR565,
++ .mask = IMASK_RGB(0x3e0, 0x3f0, 0x3e0),
++ .shift = ISHIFT_RGB(4, 10, 15),
++ .rgbsz = BITS(DPI_DMA_RGBSZ_R, 5) | BITS(DPI_DMA_RGBSZ_G, 6) |
++ BITS(DPI_DMA_RGBSZ_B, 5) | BITS(DPI_DMA_RGBSZ_BPP, 1),
++ }
++};
++
++static u32 set_output_format(u32 bus_format, u32 *shift, u32 *imask, u32 *rgbsz)
++{
++ switch (bus_format) {
++ case MEDIA_BUS_FMT_RGB565_1X16:
++ if (*shift == ISHIFT_RGB(15, 10, 4)) {
++ /* When framebuffer is RGB565, we can output RGB565 */
++ *shift = ISHIFT_RGB(15, 7, 0) | OSHIFT_RGB(19, 9, 0);
++ *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
++ return OMASK_RGB(0x3fc, 0x3fc, 0);
++ }
++
++ /* due to a HW limitation, bit-depth is effectively RGB535 */
++ *shift |= OSHIFT_RGB(19, 14, 6);
++ *imask &= IMASK_RGB(0x3e0, 0x380, 0x3e0);
++ *rgbsz = BITS(DPI_DMA_RGBSZ_G, 5) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
++ return OMASK_RGB(0x3e0, 0x39c, 0x3e0);
++
++ case MEDIA_BUS_FMT_RGB666_1X18:
++ case MEDIA_BUS_FMT_BGR666_1X18:
++ /* due to a HW limitation, bit-depth is effectively RGB444 */
++ *shift |= OSHIFT_RGB(23, 15, 7);
++ *imask &= IMASK_RGB(0x3c0, 0x3c0, 0x3c0);
++ *rgbsz = BITS(DPI_DMA_RGBSZ_R, 2) | (*rgbsz & DPI_DMA_RGBSZ_BPP_MASK);
++ return OMASK_RGB(0x330, 0x3c0, 0x3c0);
++
++ case MEDIA_BUS_FMT_RGB888_1X24:
++ case MEDIA_BUS_FMT_BGR888_1X24:
++ case MEDIA_BUS_FMT_RGB101010_1X30:
++ /* The full 24 bits can be output. Note that RP1's internal wiring means
++ * that 8.8.8 to GPIO pads can share with 10.10.10 to the onboard VDAC.
++ */
++ *shift |= OSHIFT_RGB(29, 19, 9);
++ return OMASK_RGB(0x3fc, 0x3fc, 0x3fc);
++
++ default:
++ /* RGB666_1x24_CPADHI, BGR666_1X24_CPADHI and "RGB565_666" formats */
++ *shift |= OSHIFT_RGB(27, 17, 7);
++ *rgbsz &= DPI_DMA_RGBSZ_BPP_MASK;
++ return OMASK_RGB(0x3f0, 0x3f0, 0x3f0);
++ }
++}
++
++#define BUS_FMT_IS_BGR(fmt) ( \
++ ((fmt) == MEDIA_BUS_FMT_BGR666_1X18) || \
++ ((fmt) == MEDIA_BUS_FMT_BGR666_1X24_CPADHI) || \
++ ((fmt) == MEDIA_BUS_FMT_BGR888_1X24))
++
++void rp1dpi_hw_setup(struct rp1_dpi *dpi,
++ u32 in_format, u32 bus_format, bool de_inv,
++ struct drm_display_mode const *mode)
++{
++ u32 shift, imask, omask, rgbsz;
++ int i;
++
++ pr_info("%s: in_fmt=\'%c%c%c%c\' bus_fmt=0x%x mode=%dx%d total=%dx%d %dkHz %cH%cV%cD%cC",
++ __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24, bus_format,
++ mode->hdisplay, mode->vdisplay,
++ mode->htotal, mode->vtotal,
++ mode->clock,
++ (mode->flags & DRM_MODE_FLAG_NHSYNC) ? '-' : '+',
++ (mode->flags & DRM_MODE_FLAG_NVSYNC) ? '-' : '+',
++ de_inv ? '-' : '+',
++ dpi->clk_inv ? '-' : '+');
++
++ /*
++ * Configure all DPI/DMA block registers, except base address.
++ * DMA will not actually start until a FB base address is specified
++ * using rp1dpi_hw_update().
++ */
++ rp1dpi_hw_write(dpi, DPI_DMA_VISIBLE_AREA,
++ BITS(DPI_DMA_VISIBLE_AREA_ROWSM1, mode->vdisplay - 1) |
++ BITS(DPI_DMA_VISIBLE_AREA_COLSM1, mode->hdisplay - 1));
++
++ rp1dpi_hw_write(dpi, DPI_DMA_SYNC_WIDTH,
++ BITS(DPI_DMA_SYNC_WIDTH_ROWSM1, mode->vsync_end - mode->vsync_start - 1) |
++ BITS(DPI_DMA_SYNC_WIDTH_COLSM1, mode->hsync_end - mode->hsync_start - 1));
++
++ /* In these registers, "back porch" time includes sync width */
++ rp1dpi_hw_write(dpi, DPI_DMA_BACK_PORCH,
++ BITS(DPI_DMA_BACK_PORCH_ROWSM1, mode->vtotal - mode->vsync_start - 1) |
++ BITS(DPI_DMA_BACK_PORCH_COLSM1, mode->htotal - mode->hsync_start - 1));
++
++ rp1dpi_hw_write(dpi, DPI_DMA_FRONT_PORCH,
++ BITS(DPI_DMA_FRONT_PORCH_ROWSM1, mode->vsync_start - mode->vdisplay - 1) |
++ BITS(DPI_DMA_FRONT_PORCH_COLSM1, mode->hsync_start - mode->hdisplay - 1));
++
++ /* Input to output pixel format conversion */
++ for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
++ if (my_formats[i].format == in_format)
++ break;
++ }
++ if (i >= ARRAY_SIZE(my_formats)) {
++ pr_err("%s: bad input format\n", __func__);
++ i = 4;
++ }
++ if (BUS_FMT_IS_BGR(bus_format))
++ i ^= 1;
++ shift = my_formats[i].shift;
++ imask = my_formats[i].mask;
++ rgbsz = my_formats[i].rgbsz;
++ omask = set_output_format(bus_format, &shift, &imask, &rgbsz);
++
++ rp1dpi_hw_write(dpi, DPI_DMA_IMASK, imask);
++ rp1dpi_hw_write(dpi, DPI_DMA_OMASK, omask);
++ rp1dpi_hw_write(dpi, DPI_DMA_SHIFT, shift);
++ rp1dpi_hw_write(dpi, DPI_DMA_RGBSZ, rgbsz);
++
++ rp1dpi_hw_write(dpi, DPI_DMA_QOS,
++ BITS(DPI_DMA_QOS_DQOS, 0x0) |
++ BITS(DPI_DMA_QOS_ULEV, 0xb) |
++ BITS(DPI_DMA_QOS_UQOS, 0x2) |
++ BITS(DPI_DMA_QOS_LLEV, 0x8) |
++ BITS(DPI_DMA_QOS_LQOS, 0x7));
++
++ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, -1);
++ rp1dpi_hw_vblank_ctrl(dpi, 1);
++
++ i = rp1dpi_hw_busy(dpi);
++ if (i)
++ pr_warn("%s: Unexpectedly busy at start!", __func__);
++
++ rp1dpi_hw_write(dpi, DPI_DMA_CONTROL,
++ BITS(DPI_DMA_CONTROL_ARM, !i) |
++ BITS(DPI_DMA_CONTROL_AUTO_REPEAT, 1) |
++ BITS(DPI_DMA_CONTROL_HIGH_WATER, 448) |
++ BITS(DPI_DMA_CONTROL_DEN_POL, de_inv) |
++ BITS(DPI_DMA_CONTROL_HSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NHSYNC)) |
++ BITS(DPI_DMA_CONTROL_VSYNC_POL, !!(mode->flags & DRM_MODE_FLAG_NVSYNC)) |
++ BITS(DPI_DMA_CONTROL_COLORM, 0) |
++ BITS(DPI_DMA_CONTROL_SHUTDN, 0) |
++ BITS(DPI_DMA_CONTROL_HBP_EN, (mode->htotal != mode->hsync_end)) |
++ BITS(DPI_DMA_CONTROL_HFP_EN, (mode->hsync_start != mode->hdisplay)) |
++ BITS(DPI_DMA_CONTROL_VBP_EN, (mode->vtotal != mode->vsync_end)) |
++ BITS(DPI_DMA_CONTROL_VFP_EN, (mode->vsync_start != mode->vdisplay)) |
++ BITS(DPI_DMA_CONTROL_HSYNC_EN, (mode->hsync_end != mode->hsync_start)) |
++ BITS(DPI_DMA_CONTROL_VSYNC_EN, (mode->vsync_end != mode->vsync_start)));
++}
++
++void rp1dpi_hw_update(struct rp1_dpi *dpi, dma_addr_t addr, u32 offset, u32 stride)
++{
++ u64 a = addr + offset;
++
++ /*
++ * Update STRIDE, DMAH and DMAL only. When called after rp1dpi_hw_setup(),
++ * DMA starts immediately; if already running, the buffer will flip at
++ * the next vertical sync event.
++ */
++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_STRIDE, stride);
++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_H, a >> 32);
++ rp1dpi_hw_write(dpi, DPI_DMA_DMA_ADDR_L, a & 0xFFFFFFFFu);
++}
++
++void rp1dpi_hw_stop(struct rp1_dpi *dpi)
++{
++ u32 ctrl;
++
++ /*
++ * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
++ * the current and any queued frame to end. "Force drain" flags are not used,
++ * as they seem to prevent DMA from re-starting properly; it's safer to wait.
++ */
++ reinit_completion(&dpi->finished);
++ ctrl = rp1dpi_hw_read(dpi, DPI_DMA_CONTROL);
++ ctrl &= ~(DPI_DMA_CONTROL_ARM_MASK | DPI_DMA_CONTROL_AUTO_REPEAT_MASK);
++ rp1dpi_hw_write(dpi, DPI_DMA_CONTROL, ctrl);
++ if (!wait_for_completion_timeout(&dpi->finished, HZ / 10))
++ drm_err(dpi->drm, "%s: timed out waiting for idle\n", __func__);
++ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN, 0);
++}
++
++void rp1dpi_hw_vblank_ctrl(struct rp1_dpi *dpi, int enable)
++{
++ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_EN,
++ BITS(DPI_DMA_IRQ_EN_AFIFO_EMPTY, 1) |
++ BITS(DPI_DMA_IRQ_EN_UNDERFLOW, 1) |
++ BITS(DPI_DMA_IRQ_EN_DMA_READY, !!enable) |
++ BITS(DPI_DMA_IRQ_EN_MATCH_LINE, 4095));
++}
++
++irqreturn_t rp1dpi_hw_isr(int irq, void *dev)
++{
++ struct rp1_dpi *dpi = dev;
++ u32 u = rp1dpi_hw_read(dpi, DPI_DMA_IRQ_FLAGS);
++
++ if (u) {
++ rp1dpi_hw_write(dpi, DPI_DMA_IRQ_FLAGS, u);
++ if (dpi) {
++ if (u & DPI_DMA_IRQ_FLAGS_UNDERFLOW_MASK)
++ drm_err_ratelimited(dpi->drm,
++ "Underflow! (panics=0x%08x)\n",
++ rp1dpi_hw_read(dpi, DPI_DMA_PANICS));
++ if (u & DPI_DMA_IRQ_FLAGS_DMA_READY_MASK)
++ drm_crtc_handle_vblank(&dpi->pipe.crtc);
++ if (u & DPI_DMA_IRQ_FLAGS_AFIFO_EMPTY_MASK)
++ complete(&dpi->finished);
++ }
++ }
++ return u ? IRQ_HANDLED : IRQ_NONE;
++}
--- /dev/null
+From 09c2c6aad0fed44182defecd274579770feb0ae2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Tue, 19 Sep 2023 17:54:41 +0100
+Subject: [PATCH] drm: Add RP1 VEC driver
+
+Add support for the RP1 VEC hardware.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/rp1/rp1-vec/Kconfig | 12 +
+ drivers/gpu/drm/rp1/rp1-vec/Makefile | 5 +
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c | 539 ++++++++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h | 79 ++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_cfg.c | 508 ++++++++
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 469 +++++++
+ drivers/gpu/drm/rp1/rp1-vec/vec_regs.h | 1420 +++++++++++++++++++++
+ 7 files changed, 3032 insertions(+)
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/Kconfig
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/Makefile
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec_cfg.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+ create mode 100644 drivers/gpu/drm/rp1/rp1-vec/vec_regs.h
+
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/Kconfig
+@@ -0,0 +1,12 @@
++# SPDX-License-Identifier: GPL-2.0-only
++config DRM_RP1_VEC
++ tristate "DRM Support for RP1 VEC"
++ depends on DRM
++ select MFD_RP1
++ select DRM_GEM_DMA_HELPER
++ select DRM_KMS_HELPER
++ select DRM_VRAM_HELPER
++ select DRM_TTM
++ select DRM_TTM_HELPER
++ help
++ Choose this option to enable Video Out on RP1
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/Makefile
+@@ -0,0 +1,5 @@
++# SPDX-License-Identifier: GPL-2.0-only
++
++drm-rp1-vec-y := rp1_vec.o rp1_vec_hw.o rp1_vec_cfg.o
++
++obj-$(CONFIG_DRM_RP1_VEC) += drm-rp1-vec.o
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+@@ -0,0 +1,539 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for VEC output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/mm.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/list.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/printk.h>
++#include <linux/console.h>
++#include <linux/debugfs.h>
++#include <linux/uaccess.h>
++#include <linux/io.h>
++#include <linux/dma-mapping.h>
++#include <linux/cred.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_mm.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_managed.h>
++#include <drm/drm_crtc.h>
++#include <drm/drm_crtc_helper.h>
++#include <drm/drm_encoder.h>
++#include <drm/drm_fb_helper.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_gem.h>
++#include <drm/drm_gem_atomic_helper.h>
++#include <drm/drm_gem_dma_helper.h>
++#include <drm/drm_gem_framebuffer_helper.h>
++#include <drm/drm_simple_kms_helper.h>
++#include <drm/drm_probe_helper.h>
++#include <drm/drm_modeset_helper_vtables.h>
++#include <drm/drm_vblank.h>
++#include <drm/drm_of.h>
++
++#include "rp1_vec.h"
++
++/*
++ * Default TV standard parameter; it may be overridden by the OF
++ * property "tv_norm" (which should be one of the strings below).
++ *
++ * The default (empty string) supports various 60Hz and 50Hz modes,
++ * and will automatically select NTSC[-M] or PAL[-BDGHIKL]; the two
++ * "fake" 60Hz standards NTSC-443 and PAL60 also support 50Hz PAL.
++ * Other values will restrict the set of video modes offered.
++ *
++ * Finally, the DRM connector property "mode" (which is an integer)
++ * can be used to override this value, but it does not prevent the
++ * selection of an inapplicable video mode.
++ */
++
++static char *rp1vec_tv_norm_str;
++module_param_named(tv_norm, rp1vec_tv_norm_str, charp, 0600);
++MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
++ "\t\tSupported: NTSC, NTSC-J, NTSC-443, PAL, PAL-M, PAL-N,\n"
++ "\t\t\tPAL60.\n"
++ "\t\tDefault: empty string: infer PAL for a 50 Hz mode,\n"
++ "\t\t\tNTSC otherwise");
++
++const char * const rp1vec_tvstd_names[] = {
++ [RP1VEC_TVSTD_NTSC] = "NTSC",
++ [RP1VEC_TVSTD_NTSC_J] = "NTSC-J",
++ [RP1VEC_TVSTD_NTSC_443] = "NTSC-443",
++ [RP1VEC_TVSTD_PAL] = "PAL",
++ [RP1VEC_TVSTD_PAL_M] = "PAL-M",
++ [RP1VEC_TVSTD_PAL_N] = "PAL-N",
++ [RP1VEC_TVSTD_PAL60] = "PAL60",
++ [RP1VEC_TVSTD_DEFAULT] = "",
++};
++
++static int rp1vec_parse_tv_norm(const char *str)
++{
++ int i;
++
++ if (str && *str) {
++ for (i = 0; i < ARRAY_SIZE(rp1vec_tvstd_names); ++i) {
++ if (strcasecmp(str, rp1vec_tvstd_names[i]) == 0)
++ return i;
++ }
++ }
++ return RP1VEC_TVSTD_DEFAULT;
++}
++
++static void rp1vec_pipe_update(struct drm_simple_display_pipe *pipe,
++ struct drm_plane_state *old_state)
++{
++ struct drm_pending_vblank_event *event;
++ unsigned long flags;
++ struct drm_framebuffer *fb = pipe->plane.state->fb;
++ struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++ struct drm_gem_object *gem = fb ? drm_gem_fb_get_obj(fb, 0) : NULL;
++ struct drm_gem_dma_object *dma_obj = gem ? to_drm_gem_dma_obj(gem) : NULL;
++ bool can_update = fb && dma_obj && vec && vec->pipe_enabled;
++
++ /* (Re-)start VEC where required; and update FB address */
++ if (can_update) {
++ if (!vec->vec_running || fb->format->format != vec->cur_fmt) {
++ if (vec->vec_running && fb->format->format != vec->cur_fmt) {
++ rp1vec_hw_stop(vec);
++ vec->vec_running = false;
++ }
++ if (!vec->vec_running) {
++ rp1vec_hw_setup(vec,
++ fb->format->format,
++ &pipe->crtc.state->mode,
++ vec->connector.state->tv.mode);
++ vec->vec_running = true;
++ }
++ vec->cur_fmt = fb->format->format;
++ drm_crtc_vblank_on(&pipe->crtc);
++ }
++ rp1vec_hw_update(vec, dma_obj->dma_addr, fb->offsets[0], fb->pitches[0]);
++ }
++
++ /* Check if VBLANK callback needs to be armed (or sent immediately in some error cases).
++ * Note there is a tiny probability of a race between rp1vec_dma_update() and IRQ;
++ * ordering it this way around is safe, but theoretically might delay an extra frame.
++ */
++ spin_lock_irqsave(&pipe->crtc.dev->event_lock, flags);
++ event = pipe->crtc.state->event;
++ if (event) {
++ pipe->crtc.state->event = NULL;
++ if (can_update && drm_crtc_vblank_get(&pipe->crtc) == 0)
++ drm_crtc_arm_vblank_event(&pipe->crtc, event);
++ else
++ drm_crtc_send_vblank_event(&pipe->crtc, event);
++ }
++ spin_unlock_irqrestore(&pipe->crtc.dev->event_lock, flags);
++}
++
++static void rp1vec_pipe_enable(struct drm_simple_display_pipe *pipe,
++ struct drm_crtc_state *crtc_state,
++ struct drm_plane_state *plane_state)
++{
++ struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++ dev_info(&vec->pdev->dev, __func__);
++ vec->pipe_enabled = true;
++ vec->cur_fmt = 0xdeadbeef;
++ rp1vec_vidout_setup(vec);
++ rp1vec_pipe_update(pipe, 0);
++}
++
++static void rp1vec_pipe_disable(struct drm_simple_display_pipe *pipe)
++{
++ struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++ dev_info(&vec->pdev->dev, __func__);
++ drm_crtc_vblank_off(&pipe->crtc);
++ if (vec) {
++ if (vec->vec_running) {
++ rp1vec_hw_stop(vec);
++ vec->vec_running = false;
++ }
++ vec->pipe_enabled = false;
++ }
++}
++
++static int rp1vec_pipe_enable_vblank(struct drm_simple_display_pipe *pipe)
++{
++ if (pipe && pipe->crtc.dev) {
++ struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++ if (vec)
++ rp1vec_hw_vblank_ctrl(vec, 1);
++ }
++ return 0;
++}
++
++static void rp1vec_pipe_disable_vblank(struct drm_simple_display_pipe *pipe)
++{
++ if (pipe && pipe->crtc.dev) {
++ struct rp1_vec *vec = pipe->crtc.dev->dev_private;
++
++ if (vec)
++ rp1vec_hw_vblank_ctrl(vec, 0);
++ }
++}
++
++static const struct drm_simple_display_pipe_funcs rp1vec_pipe_funcs = {
++ .enable = rp1vec_pipe_enable,
++ .update = rp1vec_pipe_update,
++ .disable = rp1vec_pipe_disable,
++ .prepare_fb = drm_gem_simple_display_pipe_prepare_fb,
++ .enable_vblank = rp1vec_pipe_enable_vblank,
++ .disable_vblank = rp1vec_pipe_disable_vblank,
++};
++
++static void rp1vec_connector_destroy(struct drm_connector *connector)
++{
++ drm_connector_unregister(connector);
++ drm_connector_cleanup(connector);
++}
++
++static const struct drm_display_mode rp1vec_modes[4] = {
++ { /* Full size 525/60i with Rec.601 pixel rate */
++ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
++ 720, 720 + 14, 720 + 14 + 64, 858, 0,
++ 480, 480 + 7, 480 + 7 + 6, 525, 0,
++ DRM_MODE_FLAG_INTERLACE)
++ },
++ { /* Cropped and horizontally squashed to be TV-safe */
++ DRM_MODE("704x432i", DRM_MODE_TYPE_DRIVER, 15429,
++ 704, 704 + 72, 704 + 72 + 72, 980, 0,
++ 432, 432 + 31, 432 + 31 + 6, 525, 0,
++ DRM_MODE_FLAG_INTERLACE)
++ },
++ { /* Full size 625/50i with Rec.601 pixel rate */
++ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
++ 720, 720 + 20, 720 + 20 + 64, 864, 0,
++ 576, 576 + 4, 576 + 4 + 6, 625, 0,
++ DRM_MODE_FLAG_INTERLACE)
++ },
++ { /* Cropped and squashed, for square(ish) pixels */
++ DRM_MODE("704x512i", DRM_MODE_TYPE_DRIVER, 15429,
++ 704, 704 + 80, 704 + 80 + 72, 987, 0,
++ 512, 512 + 36, 512 + 36 + 6, 625, 0,
++ DRM_MODE_FLAG_INTERLACE)
++ }
++};
++
++static int rp1vec_connector_get_modes(struct drm_connector *connector)
++{
++ struct rp1_vec *vec = container_of(connector, struct rp1_vec, connector);
++ bool ok525 = RP1VEC_TVSTD_SUPPORT_525(vec->tv_norm);
++ bool ok625 = RP1VEC_TVSTD_SUPPORT_625(vec->tv_norm);
++ int i, prog, n = 0;
++
++ for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
++ if ((rp1vec_modes[i].vtotal == 625) ? ok625 : ok525) {
++ for (prog = 0; prog < 2; prog++) {
++ struct drm_display_mode *mode =
++ drm_mode_duplicate(connector->dev,
++ &rp1vec_modes[i]);
++
++ if (prog) {
++ mode->flags &= ~DRM_MODE_FLAG_INTERLACE;
++ mode->vdisplay >>= 1;
++ mode->vsync_start >>= 1;
++ mode->vsync_end >>= 1;
++ mode->vtotal >>= 1;
++ }
++
++ if (mode->hdisplay == 704 &&
++ mode->vtotal == ((ok525) ? 525 : 625))
++ mode->type |= DRM_MODE_TYPE_PREFERRED;
++
++ drm_mode_set_name(mode);
++ drm_mode_probed_add(connector, mode);
++ n++;
++ }
++ }
++ }
++
++ return n;
++}
++
++static void rp1vec_connector_reset(struct drm_connector *connector)
++{
++ struct rp1_vec *vec = container_of(connector, struct rp1_vec, connector);
++
++ drm_atomic_helper_connector_reset(connector);
++ if (connector->state)
++ connector->state->tv.mode = vec->tv_norm;
++}
++
++static int rp1vec_connector_atomic_check(struct drm_connector *conn,
++ struct drm_atomic_state *state)
++{ struct drm_connector_state *old_state =
++ drm_atomic_get_old_connector_state(state, conn);
++ struct drm_connector_state *new_state =
++ drm_atomic_get_new_connector_state(state, conn);
++
++ if (new_state->crtc && old_state->tv.mode != new_state->tv.mode) {
++ struct drm_crtc_state *crtc_state =
++ drm_atomic_get_new_crtc_state(state, new_state->crtc);
++
++ crtc_state->mode_changed = true;
++ }
++
++ return 0;
++}
++
++static enum drm_mode_status rp1vec_mode_valid(struct drm_device *dev,
++ const struct drm_display_mode *mode)
++{
++ /*
++ * Check the mode roughly matches one of our standard modes
++ * (optionally half-height and progressive). Ignore H/V sync
++ * timings which for interlaced TV are approximate at best.
++ */
++ int i, prog;
++
++ prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
++
++ for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
++ const struct drm_display_mode *ref = rp1vec_modes + i;
++
++ if (mode->hdisplay == ref->hdisplay &&
++ mode->vdisplay == (ref->vdisplay >> prog) &&
++ mode->clock + 2 >= ref->clock &&
++ mode->clock <= ref->clock + 2 &&
++ mode->htotal + 2 >= ref->htotal &&
++ mode->htotal <= ref->htotal + 2 &&
++ mode->vtotal + 2 >= (ref->vtotal >> prog) &&
++ mode->vtotal <= (ref->vtotal >> prog) + 2)
++ return MODE_OK;
++ }
++ return MODE_BAD;
++}
++
++static const struct drm_connector_helper_funcs rp1vec_connector_helper_funcs = {
++ .get_modes = rp1vec_connector_get_modes,
++ .atomic_check = rp1vec_connector_atomic_check,
++};
++
++static const struct drm_connector_funcs rp1vec_connector_funcs = {
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .destroy = rp1vec_connector_destroy,
++ .reset = rp1vec_connector_reset,
++ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
++ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
++};
++
++static const struct drm_mode_config_funcs rp1vec_mode_funcs = {
++ .fb_create = drm_gem_fb_create,
++ .atomic_check = drm_atomic_helper_check,
++ .atomic_commit = drm_atomic_helper_commit,
++ .mode_valid = rp1vec_mode_valid,
++};
++
++static const u32 rp1vec_formats[] = {
++ DRM_FORMAT_XRGB8888,
++ DRM_FORMAT_XBGR8888,
++ DRM_FORMAT_RGB888,
++ DRM_FORMAT_BGR888,
++ DRM_FORMAT_RGB565
++};
++
++static void rp1vec_stopall(struct drm_device *drm)
++{
++ if (drm->dev_private) {
++ struct rp1_vec *vec = drm->dev_private;
++
++ if (vec->vec_running || rp1vec_hw_busy(vec)) {
++ rp1vec_hw_stop(vec);
++ vec->vec_running = false;
++ }
++ rp1vec_vidout_poweroff(vec);
++ }
++}
++
++DEFINE_DRM_GEM_DMA_FOPS(rp1vec_fops);
++
++static struct drm_driver rp1vec_driver = {
++ .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
++ .fops = &rp1vec_fops,
++ .name = "drm-rp1-vec",
++ .desc = "drm-rp1-vec",
++ .date = "0",
++ .major = 1,
++ .minor = 0,
++ DRM_GEM_DMA_DRIVER_OPS,
++ .release = rp1vec_stopall,
++};
++
++static int rp1vec_platform_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct drm_device *drm;
++ struct rp1_vec *vec;
++ const char *str;
++ int i, ret;
++
++ dev_info(dev, __func__);
++ drm = drm_dev_alloc(&rp1vec_driver, dev);
++ if (IS_ERR(drm)) {
++ ret = PTR_ERR(drm);
++ dev_err(dev, "%s drm_dev_alloc %d", __func__, ret);
++ return ret;
++ }
++
++ vec = drmm_kzalloc(drm, sizeof(*vec), GFP_KERNEL);
++ if (!vec) {
++ dev_err(dev, "%s drmm_kzalloc failed", __func__);
++ ret = -ENOMEM;
++ goto err_free_drm;
++ }
++ init_completion(&vec->finished);
++ vec->drm = drm;
++ vec->pdev = pdev;
++ drm->dev_private = vec;
++ platform_set_drvdata(pdev, drm);
++
++ str = rp1vec_tv_norm_str;
++ of_property_read_string(dev->of_node, "tv_norm", &str);
++ vec->tv_norm = rp1vec_parse_tv_norm(str);
++
++ for (i = 0; i < RP1VEC_NUM_HW_BLOCKS; i++) {
++ vec->hw_base[i] =
++ devm_ioremap_resource(dev,
++ platform_get_resource(vec->pdev, IORESOURCE_MEM, i));
++ if (IS_ERR(vec->hw_base[i])) {
++ ret = PTR_ERR(vec->hw_base[i]);
++ dev_err(dev, "Error memory mapping regs[%d]\n", i);
++ goto err_free_drm;
++ }
++ }
++ ret = platform_get_irq(vec->pdev, 0);
++ if (ret > 0)
++ ret = devm_request_irq(dev, ret, rp1vec_hw_isr,
++ IRQF_SHARED, "rp1-vec", vec);
++ if (ret) {
++ dev_err(dev, "Unable to request interrupt\n");
++ ret = -EINVAL;
++ goto err_free_drm;
++ }
++ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));
++
++ vec->vec_clock = devm_clk_get(dev, NULL);
++ if (IS_ERR(vec->vec_clock)) {
++ ret = PTR_ERR(vec->vec_clock);
++ goto err_free_drm;
++ }
++ ret = clk_prepare_enable(vec->vec_clock);
++
++ ret = drmm_mode_config_init(drm);
++ if (ret)
++ goto err_free_drm;
++ drm->mode_config.max_width = 768;
++ drm->mode_config.max_height = 576;
++ drm->mode_config.fb_base = 0;
++ drm->mode_config.preferred_depth = 32;
++ drm->mode_config.prefer_shadow = 0;
++ drm->mode_config.prefer_shadow_fbdev = 1;
++ //drm->mode_config.fbdev_use_iomem = false;
++ drm->mode_config.quirk_addfb_prefer_host_byte_order = true;
++ drm->mode_config.funcs = &rp1vec_mode_funcs;
++ drm_vblank_init(drm, 1);
++
++ ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(rp1vec_tvstd_names),
++ rp1vec_tvstd_names);
++ if (ret)
++ goto err_free_drm;
++
++ drm_connector_init(drm, &vec->connector, &rp1vec_connector_funcs,
++ DRM_MODE_CONNECTOR_Composite);
++ if (ret)
++ goto err_free_drm;
++
++ vec->connector.interlace_allowed = true;
++ drm_connector_helper_add(&vec->connector, &rp1vec_connector_helper_funcs);
++
++ drm_object_attach_property(&vec->connector.base,
++ drm->mode_config.tv_mode_property,
++ vec->tv_norm);
++
++ ret = drm_simple_display_pipe_init(drm,
++ &vec->pipe,
++ &rp1vec_pipe_funcs,
++ rp1vec_formats,
++ ARRAY_SIZE(rp1vec_formats),
++ NULL,
++ &vec->connector);
++ if (ret)
++ goto err_free_drm;
++
++ drm_mode_config_reset(drm);
++
++ ret = drm_dev_register(drm, 0);
++ if (ret)
++ goto err_free_drm;
++
++ drm_fbdev_generic_setup(drm, 32); /* the "32" is preferred BPP */
++ return ret;
++
++err_free_drm:
++ dev_info(dev, "%s fail %d", __func__, ret);
++ drm_dev_put(drm);
++ return ret;
++}
++
++static int rp1vec_platform_remove(struct platform_device *pdev)
++{
++ struct drm_device *drm = platform_get_drvdata(pdev);
++
++ rp1vec_stopall(drm);
++ drm_dev_unregister(drm);
++ drm_atomic_helper_shutdown(drm);
++ drm_dev_put(drm);
++
++ return 0;
++}
++
++static void rp1vec_platform_shutdown(struct platform_device *pdev)
++{
++ struct drm_device *drm = platform_get_drvdata(pdev);
++
++ rp1vec_stopall(drm);
++}
++
++static const struct of_device_id rp1vec_of_match[] = {
++ {
++ .compatible = "raspberrypi,rp1vec",
++ },
++ { /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rp1vec_of_match);
++
++static struct platform_driver rp1vec_platform_driver = {
++ .probe = rp1vec_platform_probe,
++ .remove = rp1vec_platform_remove,
++ .shutdown = rp1vec_platform_shutdown,
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .of_match_table = rp1vec_of_match,
++ },
++};
++
++module_platform_driver(rp1vec_platform_driver);
++
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("DRM driver for Composite Video on Raspberry Pi RP1");
++MODULE_AUTHOR("Nick Hollinghurst");
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.h
+@@ -0,0 +1,79 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/types.h>
++#include <linux/io.h>
++#include <linux/clk.h>
++#include <drm/drm_device.h>
++#include <drm/drm_simple_kms_helper.h>
++
++#define MODULE_NAME "drm-rp1-vec"
++#define DRIVER_NAME "drm-rp1-vec"
++
++/* ---------------------------------------------------------------------- */
++
++#define RP1VEC_HW_BLOCK_VEC 0
++#define RP1VEC_HW_BLOCK_CFG 1
++#define RP1VEC_NUM_HW_BLOCKS 2
++
++enum {
++ RP1VEC_TVSTD_NTSC = 0, /* +525 => NTSC 625 => PAL */
++ RP1VEC_TVSTD_NTSC_J, /* +525 => NTSC-J 625 => PAL */
++ RP1VEC_TVSTD_NTSC_443, /* +525 => NTSC-443 +625 => PAL */
++ RP1VEC_TVSTD_PAL, /* 525 => NTSC +625 => PAL */
++ RP1VEC_TVSTD_PAL_M, /* +525 => PAL-M 625 => PAL */
++ RP1VEC_TVSTD_PAL_N, /* 525 => NTSC +625 => PAL-N */
++ RP1VEC_TVSTD_PAL60, /* +525 => PAL60 +625 => PAL */
++ RP1VEC_TVSTD_DEFAULT, /* +525 => NTSC +625 => PAL */
++};
++
++/* Which standards support which modes? Those marked with + above */
++#define RP1VEC_TVSTD_SUPPORT_525(n) ((0xD7 >> (n)) & 1)
++#define RP1VEC_TVSTD_SUPPORT_625(n) ((0xEC >> (n)) & 1)
++
++/* ---------------------------------------------------------------------- */
++
++struct rp1_vec {
++ /* DRM and platform device pointers */
++ struct drm_device *drm;
++ struct platform_device *pdev;
++
++ /* Framework and helper objects */
++ struct drm_simple_display_pipe pipe;
++ struct drm_connector connector;
++
++ /* Clock. We assume this is always at 108 MHz. */
++ struct clk *vec_clock;
++
++ /* Block (VCC, CFG) base addresses, and current state */
++ void __iomem *hw_base[RP1VEC_NUM_HW_BLOCKS];
++ u32 cur_fmt;
++ int tv_norm;
++ bool vec_running, pipe_enabled;
++ struct completion finished;
++};
++
++extern const char * const rp1vec_tvstd_names[];
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the VEC/DMA block */
++
++void rp1vec_hw_setup(struct rp1_vec *vec,
++ u32 in_format,
++ struct drm_display_mode const *mode,
++ int tvstd);
++void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride);
++void rp1vec_hw_stop(struct rp1_vec *vec);
++int rp1vec_hw_busy(struct rp1_vec *vec);
++irqreturn_t rp1vec_hw_isr(int irq, void *dev);
++void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable);
++
++/* ---------------------------------------------------------------------- */
++/* Functions to control the VIDEO OUT CFG block and check RP1 platform */
++
++void rp1vec_vidout_setup(struct rp1_vec *vec);
++void rp1vec_vidout_poweroff(struct rp1_vec *vec);
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_cfg.c
+@@ -0,0 +1,508 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for DSI output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <linux/rp1_platform.h>
++
++#include "rp1_vec.h"
++
++// =============================================================================
++// Register : VIDEO_OUT_CFG_SEL
++// JTAG access : synchronous
++// Description : Selects source: VEC or DPI
++#define VIDEO_OUT_CFG_SEL_OFFSET 0x00000000
++#define VIDEO_OUT_CFG_SEL_BITS 0x00000013
++#define VIDEO_OUT_CFG_SEL_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_SEL_PCLK_INV
++// Description : Select dpi_pclk output port polarity inversion.
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_RESET 0x0
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_BITS 0x00000010
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_MSB 4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_LSB 4
++#define VIDEO_OUT_CFG_SEL_PCLK_INV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_SEL_PAD_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_RESET 0x0
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_BITS 0x00000002
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_MSB 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_LSB 1
++#define VIDEO_OUT_CFG_SEL_PAD_MUX_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_SEL_VDAC_MUX
++// Description : VEC 1 DPI 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_RESET 0x0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS 0x00000001
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_MSB 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_LSB 0
++#define VIDEO_OUT_CFG_SEL_VDAC_MUX_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_VDAC_CFG
++// JTAG access : synchronous
++// Description : Configure SNPS VDAC
++#define VIDEO_OUT_CFG_VDAC_CFG_OFFSET 0x00000004
++#define VIDEO_OUT_CFG_VDAC_CFG_BITS 0x1fffffff
++#define VIDEO_OUT_CFG_VDAC_CFG_RESET 0x0003ffff
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENCTR
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_BITS 0x1c000000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_MSB 28
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_LSB 26
++#define VIDEO_OUT_CFG_VDAC_CFG_ENCTR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENSC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_BITS 0x03800000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_MSB 25
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_LSB 23
++#define VIDEO_OUT_CFG_VDAC_CFG_ENSC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENDAC
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_BITS 0x00700000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_MSB 22
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_LSB 20
++#define VIDEO_OUT_CFG_VDAC_CFG_ENDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENVBG
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_BITS 0x00080000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_MSB 19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_LSB 19
++#define VIDEO_OUT_CFG_VDAC_CFG_ENVBG_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_BITS 0x00040000
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_MSB 18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_LSB 18
++#define VIDEO_OUT_CFG_VDAC_CFG_ENEXTREF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC2GC
++// Description : dac2 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_RESET 0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_BITS 0x0003f000
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_MSB 17
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_LSB 12
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC2GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC1GC
++// Description : dac1 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_RESET 0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_BITS 0x00000fc0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_MSB 11
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_LSB 6
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC1GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_CFG_DAC0GC
++// Description : dac0 gain control
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_RESET 0x3f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_BITS 0x0000003f
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_MSB 5
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_LSB 0
++#define VIDEO_OUT_CFG_VDAC_CFG_DAC0GC_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_VDAC_STATUS
++// JTAG access : synchronous
++// Description : Read VDAC status
++#define VIDEO_OUT_CFG_VDAC_STATUS_OFFSET 0x00000008
++#define VIDEO_OUT_CFG_VDAC_STATUS_BITS 0x00000017
++#define VIDEO_OUT_CFG_VDAC_STATUS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_RESET 0x0
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_BITS 0x00000010
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_MSB 4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_LSB 4
++#define VIDEO_OUT_CFG_VDAC_STATUS_ENCTR3_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT
++// Description : None
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_RESET "-"
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_BITS 0x00000007
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_MSB 2
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_LSB 0
++#define VIDEO_OUT_CFG_VDAC_STATUS_CABLEOUT_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_MEM_PD
++// JTAG access : synchronous
++// Description : Control memory power down
++#define VIDEO_OUT_CFG_MEM_PD_OFFSET 0x0000000c
++#define VIDEO_OUT_CFG_MEM_PD_BITS 0x00000003
++#define VIDEO_OUT_CFG_MEM_PD_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_MEM_PD_VEC
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_MEM_PD_VEC_BITS 0x00000002
++#define VIDEO_OUT_CFG_MEM_PD_VEC_MSB 1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_LSB 1
++#define VIDEO_OUT_CFG_MEM_PD_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_MEM_PD_DPI
++// Description : None
++#define VIDEO_OUT_CFG_MEM_PD_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_BITS 0x00000001
++#define VIDEO_OUT_CFG_MEM_PD_DPI_MSB 0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_LSB 0
++#define VIDEO_OUT_CFG_MEM_PD_DPI_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_TEST_OVERRIDE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_OFFSET 0x00000010
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_BITS 0xffffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_PAD
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_RESET 0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_BITS 0x80000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_MSB 31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_LSB 31
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_PAD_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_RESET 0x0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_BITS 0x40000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_MSB 30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_LSB 30
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_VDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL
++// Description : None
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_RESET 0x00000000
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_BITS 0x3fffffff
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_MSB 29
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_LSB 0
++#define VIDEO_OUT_CFG_TEST_OVERRIDE_RGBVAL_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTR
++// JTAG access : synchronous
++// Description : Raw Interrupts
++#define VIDEO_OUT_CFG_INTR_OFFSET 0x00000014
++#define VIDEO_OUT_CFG_INTR_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTR_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTR_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTR_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTR_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTR_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTR_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTR_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTR_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTR_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTR_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTR_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTR_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTR_VEC_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTE
++// JTAG access : synchronous
++// Description : Interrupt Enable
++#define VIDEO_OUT_CFG_INTE_OFFSET 0x00000018
++#define VIDEO_OUT_CFG_INTE_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTE_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTE_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTE_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTE_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTE_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTE_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTE_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTE_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTE_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTE_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTE_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTE_VEC_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTF
++// JTAG access : synchronous
++// Description : Interrupt Force
++#define VIDEO_OUT_CFG_INTF_OFFSET 0x0000001c
++#define VIDEO_OUT_CFG_INTF_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTF_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTF_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTF_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTF_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTF_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTF_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTF_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTF_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTF_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTF_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTF_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTF_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTF_VEC_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INTS
++// JTAG access : synchronous
++// Description : Interrupt status after masking & forcing
++#define VIDEO_OUT_CFG_INTS_OFFSET 0x00000020
++#define VIDEO_OUT_CFG_INTS_BITS 0x00000003
++#define VIDEO_OUT_CFG_INTS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTS_DPI
++// Description : None
++#define VIDEO_OUT_CFG_INTS_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_INTS_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_INTS_DPI_MSB 1
++#define VIDEO_OUT_CFG_INTS_DPI_LSB 1
++#define VIDEO_OUT_CFG_INTS_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_INTS_VEC
++// Description : None
++#define VIDEO_OUT_CFG_INTS_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_INTS_VEC_BITS 0x00000001
++#define VIDEO_OUT_CFG_INTS_VEC_MSB 0
++#define VIDEO_OUT_CFG_INTS_VEC_LSB 0
++#define VIDEO_OUT_CFG_INTS_VEC_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_BLOCK_ID
++// JTAG access : synchronous
++// Description : Block Identifier
++// Hexadecimal representation of "VOCF"
++#define VIDEO_OUT_CFG_BLOCK_ID_OFFSET 0x00000024
++#define VIDEO_OUT_CFG_BLOCK_ID_BITS 0xffffffff
++#define VIDEO_OUT_CFG_BLOCK_ID_RESET 0x564f4346
++#define VIDEO_OUT_CFG_BLOCK_ID_MSB 31
++#define VIDEO_OUT_CFG_BLOCK_ID_LSB 0
++#define VIDEO_OUT_CFG_BLOCK_ID_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_INSTANCE_ID
++// JTAG access : synchronous
++// Description : Block Instance Identifier
++#define VIDEO_OUT_CFG_INSTANCE_ID_OFFSET 0x00000028
++#define VIDEO_OUT_CFG_INSTANCE_ID_BITS 0x0000000f
++#define VIDEO_OUT_CFG_INSTANCE_ID_RESET 0x00000000
++#define VIDEO_OUT_CFG_INSTANCE_ID_MSB 3
++#define VIDEO_OUT_CFG_INSTANCE_ID_LSB 0
++#define VIDEO_OUT_CFG_INSTANCE_ID_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_AUTO
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_OFFSET 0x0000002c
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_RESET 0x00000007
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER
++// Description : 1 = reset is controlled by the sequencer
++// 0 = reset is controlled by rstseq_ctrl
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_AUTO_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_PARALLEL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_OFFSET 0x00000030
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_RESET 0x00000006
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_RESET 0x1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER
++// Description : Is this reset parallel (i.e. not part of the sequence)
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_PARALLEL_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_CTRL
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_OFFSET 0x00000034
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_VEC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_DPI_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER
++// Description : 1 = keep the reset asserted
++// 0 = keep the reset deasserted
++// This is ignored if rstseq_auto=1
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_CTRL_BUSADAPTER_ACCESS "RW"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_TRIG
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_OFFSET 0x00000038
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_VEC_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_DPI_ACCESS "SC"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER
++// Description : Pulses the reset output
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_TRIG_BUSADAPTER_ACCESS "SC"
++// =============================================================================
++// Register : VIDEO_OUT_CFG_RSTSEQ_DONE
++// JTAG access : synchronous
++// Description : None
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_OFFSET 0x0000003c
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BITS 0x00000007
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_VEC
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_BITS 0x00000004
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_MSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_LSB 2
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_VEC_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_DPI
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_BITS 0x00000002
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_MSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_LSB 1
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_DPI_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER
++// Description : Indicates the current state of the reset
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_RESET 0x0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_BITS 0x00000001
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_MSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_LSB 0
++#define VIDEO_OUT_CFG_RSTSEQ_DONE_BUSADAPTER_ACCESS "RO"
++// =============================================================================
++
++#define CFG_WRITE(reg, val) writel((val), vec->hw_base[RP1VEC_HW_BLOCK_CFG] + (reg ## _OFFSET))
++#define CFG_READ(reg) readl(vec->hw_base[RP1VEC_HW_BLOCK_CFG] + (reg ## _OFFSET))
++
++void rp1vec_vidout_setup(struct rp1_vec *vec)
++{
++ /*
++ * We assume DPI and VEC can't be used at the same time (due to
++ * clashing requirements for PLL_VIDEO, and potentially for VDAC).
++ * We therefore leave DPI memories powered down.
++ */
++ CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_DPI_BITS);
++ CFG_WRITE(VIDEO_OUT_CFG_TEST_OVERRIDE, 0x00000000);
++
++ /* DPI->Pads; VEC->VDAC */
++ CFG_WRITE(VIDEO_OUT_CFG_SEL, VIDEO_OUT_CFG_SEL_VDAC_MUX_BITS);
++
++ /* configure VDAC for 1 channel, bandgap on, 1.28V swing */
++ CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0x0019ffff);
++
++ /* enable VEC interrupt */
++ CFG_WRITE(VIDEO_OUT_CFG_INTE, VIDEO_OUT_CFG_INTE_VEC_BITS);
++}
++
++void rp1vec_vidout_poweroff(struct rp1_vec *vec)
++{
++ /* disable VEC interrupt */
++ CFG_WRITE(VIDEO_OUT_CFG_INTE, 0);
++
++ /* Ensure VDAC is turned off; power down DPI,VEC memories */
++ CFG_WRITE(VIDEO_OUT_CFG_VDAC_CFG, 0);
++ CFG_WRITE(VIDEO_OUT_CFG_MEM_PD, VIDEO_OUT_CFG_MEM_PD_BITS);
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+@@ -0,0 +1,469 @@
++// SPDX-License-Identifier: GPL-2.0-or-later
++/*
++ * DRM Driver for VEC output on Raspberry Pi RP1
++ *
++ * Copyright (c) 2023 Raspberry Pi Limited.
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/printk.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_print.h>
++#include <drm/drm_vblank.h>
++
++#include "rp1_vec.h"
++#include "vec_regs.h"
++
++#define BITS(field, val) (((val) << (field ## _LSB)) & (field ## _BITS))
++
++#define VEC_WRITE(reg, val) writel((val), vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
++#define VEC_READ(reg) readl(vec->hw_base[RP1VEC_HW_BLOCK_VEC] + (reg ## _OFFSET))
++
++int rp1vec_hw_busy(struct rp1_vec *vec)
++{
++ /* Read the undocumented "pline_busy" flag */
++ return VEC_READ(VEC_STATUS) & 1;
++}
++
++/* Table of supported input (in-memory/DMA) pixel formats. */
++struct rp1vec_ipixfmt {
++ u32 format; /* DRM format code */
++ u32 mask; /* RGB masks (10 bits each, left justified) */
++ u32 shift; /* RGB MSB positions in the memory word */
++ u32 rgbsz; /* Shifts used for scaling; also (BPP/8-1) */
++};
++
++#define MASK_RGB(r, g, b) \
++ (BITS(VEC_IMASK_MASK_R, r) | BITS(VEC_IMASK_MASK_G, g) | BITS(VEC_IMASK_MASK_B, b))
++#define SHIFT_RGB(r, g, b) \
++ (BITS(VEC_SHIFT_SHIFT_R, r) | BITS(VEC_SHIFT_SHIFT_G, g) | BITS(VEC_SHIFT_SHIFT_B, b))
++
++static const struct rp1vec_ipixfmt my_formats[] = {
++ {
++ .format = DRM_FORMAT_XRGB8888,
++ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = SHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
++ },
++ {
++ .format = DRM_FORMAT_XBGR8888,
++ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = SHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 3),
++ },
++ {
++ .format = DRM_FORMAT_RGB888,
++ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = SHIFT_RGB(23, 15, 7),
++ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
++ },
++ {
++ .format = DRM_FORMAT_BGR888,
++ .mask = MASK_RGB(0x3fc, 0x3fc, 0x3fc),
++ .shift = SHIFT_RGB(7, 15, 23),
++ .rgbsz = BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 2),
++ },
++ {
++ .format = DRM_FORMAT_RGB565,
++ .mask = MASK_RGB(0x3e0, 0x3f0, 0x3e0),
++ .shift = SHIFT_RGB(15, 10, 4),
++ .rgbsz = BITS(VEC_RGBSZ_SCALE_R, 5) |
++ BITS(VEC_RGBSZ_SCALE_G, 6) |
++ BITS(VEC_RGBSZ_SCALE_B, 5) |
++ BITS(VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1, 1),
++ }
++};
++
++/*
++ * Hardware mode descriptions (@ 108 MHz clock rate).
++ * These rely largely on "canned" register settings.
++ * TODO: Port the generating software from FP to integer,
++ * or better factorize the differences between modes.
++ */
++
++struct rp1vec_hwmode {
++ u16 total_cols; /* active columns, plus padding for filter context */
++ u16 rows_per_field; /* active lines per field (including partial ones) */
++ bool interlaced; /* set for interlaced */
++ bool first_field_odd; /* set for interlaced and 30fps */
++ u32 yuv_scaling; /* three 10-bit fields {Y, U, V} in 2.8 format */
++ u32 back_end_regs[28]; /* All registers 0x80 .. 0xEC */
++};
++
++/* { NTSC, PAL, PAL-M } x { progressive, interlaced } x { 13.5 MHz, 15.428571 MHz } */
++static const struct rp1vec_hwmode rp1vec_hwmodes[3][2][2] = {
++ {
++ /* NTSC */
++ {
++ {
++ .total_cols = 724,
++ .rows_per_field = 240,
++ .interlaced = false,
++ .first_field_odd = false,
++ .yuv_scaling = 0x1071d0cf,
++ .back_end_regs = {
++ 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++ 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++ 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++ 0x00000000, 0x00170106, 0x00000000, 0x004c020e,
++ 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++ 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ec,
++ },
++ }, {
++ .total_cols = 815,
++ .rows_per_field = 240,
++ .interlaced = false,
++ .first_field_odd = false,
++ .yuv_scaling = 0x1c131962,
++ .back_end_regs = {
++ 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++ 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++ 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++ 0x00000000, 0x00170106, 0x00000000, 0x004c020e,
++ 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++ 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ac,
++ },
++ },
++ }, {
++ {
++ .total_cols = 724,
++ .rows_per_field = 243,
++ .interlaced = true,
++ .first_field_odd = true,
++ .yuv_scaling = 0x1071d0cf,
++ .back_end_regs = {
++ 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++ 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++ 0x000a0107, 0x0111020d, 0x00000000, 0x00000000,
++ 0x011c020d, 0x00150106, 0x0107011b, 0x004c020d,
++ 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++ 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x00094dee,
++ },
++ }, {
++ .total_cols = 815,
++ .rows_per_field = 243,
++ .interlaced = true,
++ .first_field_odd = true,
++ .yuv_scaling = 0x1c131962,
++ .back_end_regs = {
++ 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023d034c,
++ 0x00f80b6d, 0x00000005, 0x0006000b, 0x000c0011,
++ 0x000a0107, 0x0111020d, 0x00000000, 0x00000000,
++ 0x011c020d, 0x00150106, 0x0107011b, 0x004c020d,
++ 0x00000000, 0x007bffff, 0x38518c9a, 0x11195561,
++ 0x02000200, 0xc1f07c1f, 0x087c1f07, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x00094dae,
++ },
++ },
++ },
++ }, {
++ /* PAL */
++ {
++ {
++ .total_cols = 724,
++ .rows_per_field = 288,
++ .interlaced = false,
++ .first_field_odd = false,
++ .yuv_scaling = 0x11c1f8e0,
++ .back_end_regs = {
++ 0x04061aa6, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++ 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++ 0x00070135, 0x00000000, 0x00000000, 0x00000000,
++ 0x00000000, 0x00170136, 0x00000000, 0x000a0270,
++ 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++ 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ed,
++ },
++ }, {
++ .total_cols = 804,
++ .rows_per_field = 288,
++ .interlaced = false,
++ .first_field_odd = false,
++ .yuv_scaling = 0x1e635d7f,
++ .back_end_regs = {
++ 0x045b1a57, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++ 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++ 0x00070135, 0x00000000, 0x00000000, 0x00000000,
++ 0x00000000, 0x00170136, 0x00000000, 0x000a0270,
++ 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++ 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ad,
++ },
++ },
++ }, {
++ {
++ .total_cols = 724,
++ .rows_per_field = 288,
++ .interlaced = true,
++ .first_field_odd = false,
++ .yuv_scaling = 0x11c1f8e0,
++ .back_end_regs = {
++ 0x04061aa6, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++ 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++ 0x00070135, 0x013f026d, 0x00060136, 0x0140026e,
++ 0x0150026e, 0x00180136, 0x026f0017, 0x000a0271,
++ 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++ 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddef,
++ },
++ }, {
++ .total_cols = 804,
++ .rows_per_field = 288,
++ .interlaced = true,
++ .first_field_odd = false,
++ .yuv_scaling = 0x1e635d7f,
++ .back_end_regs = {
++ 0x045b1a57, 0x046e0cee, 0x0d8001fb, 0x025c034f,
++ 0x00fd0b84, 0x026c0270, 0x00000004, 0x00050009,
++ 0x00070135, 0x013f026d, 0x00060136, 0x0140026e,
++ 0x0150026e, 0x00180136, 0x026f0017, 0x000a0271,
++ 0x00000000, 0x007bffff, 0x3b1389d8, 0x0caf53b5,
++ 0x02000200, 0xcc48c1d1, 0x0a8262b2, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddaf,
++ },
++ },
++ },
++ }, {
++ /* PAL-M */
++ {
++ {
++ .total_cols = 724,
++ .rows_per_field = 240,
++ .interlaced = false,
++ .first_field_odd = false,
++ .yuv_scaling = 0x11c1f8e0,
++ .back_end_regs = {
++ 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++ 0x00f80b6e, 0x00000005, 0x0006000b, 0x000c0011,
++ 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++ 0x00000000, 0x00170106, 0x00000000, 0x000a020c,
++ 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++ 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ed,
++ },
++ }, {
++ .total_cols = 815,
++ .rows_per_field = 240,
++ .interlaced = false,
++ .first_field_odd = false,
++ .yuv_scaling = 0x1e635d7f,
++ .back_end_regs = {
++ 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++ 0x00f80b6e, 0x00000005, 0x0006000b, 0x000c0011,
++ 0x000a0106, 0x00000000, 0x00000000, 0x00000000,
++ 0x00000000, 0x00170106, 0x00000000, 0x000a020c,
++ 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++ 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x000801ad,
++ },
++ },
++ }, {
++ {
++ .total_cols = 724,
++ .rows_per_field = 243,
++ .interlaced = true,
++ .first_field_odd = true,
++ .yuv_scaling = 0x11c1f8e0,
++ .back_end_regs = {
++ 0x039f1a3f, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++ 0x00f80b6e, 0x00140019, 0x00000005, 0x0006000b,
++ 0x00090103, 0x010f0209, 0x00080102, 0x010e020a,
++ 0x0119020a, 0x00120103, 0x01040118, 0x000a020d,
++ 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++ 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddef,
++ },
++ }, {
++ .total_cols = 815,
++ .rows_per_field = 243,
++ .interlaced = true,
++ .first_field_odd = true,
++ .yuv_scaling = 0x1e635d7f,
++ .back_end_regs = {
++ 0x03ce1a17, 0x03e10cc6, 0x0d6801fb, 0x023c034c,
++ 0x00f80b6e, 0x00140019, 0x00000005, 0x0006000b,
++ 0x00090103, 0x010f0209, 0x00080102, 0x010e020a,
++ 0x0119020a, 0x00120103, 0x01040118, 0x000a020d,
++ 0x00000000, 0x007bffff, 0x385189d8, 0x0d5c53b5,
++ 0x02000200, 0xd6d33ea8, 0x0879bbf8, 0x00000000,
++ 0x0be20200, 0x20f0f800, 0x265c7f00, 0x0009ddaf,
++ },
++ },
++ },
++ },
++};
++
++void rp1vec_hw_setup(struct rp1_vec *vec,
++ u32 in_format,
++ struct drm_display_mode const *mode,
++ int tvstd)
++{
++ unsigned int i, mode_family, mode_ilaced, mode_narrow;
++ const struct rp1vec_hwmode *hwm;
++ unsigned int w, h;
++
++ /* Pick the appropriate "base" mode, which we may modify */
++ mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
++ if (mode->vtotal > 263 * (1 + mode_ilaced))
++ mode_family = 1;
++ else
++ mode_family = (tvstd == RP1VEC_TVSTD_PAL_M || tvstd == RP1VEC_TVSTD_PAL60) ? 2 : 0;
++ mode_narrow = (mode->clock >= 14336);
++ hwm = &rp1vec_hwmodes[mode_family][mode_ilaced][mode_narrow];
++ dev_info(&vec->pdev->dev,
++ "%s: in_fmt=\'%c%c%c%c\' mode=%dx%d%s [%d%d%d] tvstd=%d (%s)",
++ __func__, in_format, in_format >> 8, in_format >> 16, in_format >> 24,
++ mode->hdisplay, mode->vdisplay, (mode_ilaced) ? "i" : "",
++ mode_family, mode_ilaced, mode_narrow,
++ tvstd, rp1vec_tvstd_names[tvstd]);
++
++ w = mode->hdisplay;
++ h = mode->vdisplay;
++ if (mode_ilaced)
++ h >>= 1;
++ if (w > hwm->total_cols)
++ w = hwm->total_cols;
++ if (h > hwm->rows_per_field)
++ w = hwm->rows_per_field;
++
++ /* Configure the hardware */
++ VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
++ VEC_WRITE(VEC_QOS,
++ BITS(VEC_QOS_DQOS, 0x0) |
++ BITS(VEC_QOS_ULEV, 0x8) |
++ BITS(VEC_QOS_UQOS, 0x2) |
++ BITS(VEC_QOS_LLEV, 0x4) |
++ BITS(VEC_QOS_LQOS, 0x7));
++ VEC_WRITE(VEC_DMA_AREA,
++ BITS(VEC_DMA_AREA_COLS_MINUS1, w - 1) |
++ BITS(VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1, h - 1));
++ VEC_WRITE(VEC_YUV_SCALING, hwm->yuv_scaling);
++ VEC_WRITE(VEC_BACK_PORCH,
++ BITS(VEC_BACK_PORCH_HBP_MINUS1, (hwm->total_cols - w - 1) >> 1) |
++ BITS(VEC_BACK_PORCH_VBP_MINUS1, (hwm->rows_per_field - h - 1) >> 1));
++ VEC_WRITE(VEC_FRONT_PORCH,
++ BITS(VEC_FRONT_PORCH_HFP_MINUS1, (hwm->total_cols - w - 2) >> 1) |
++ BITS(VEC_FRONT_PORCH_VFP_MINUS1, (hwm->rows_per_field - h - 2) >> 1));
++ VEC_WRITE(VEC_MODE,
++ BITS(VEC_MODE_HIGH_WATER, 0xE0) |
++ BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15)) |
++ BITS(VEC_MODE_VFP_EN, (hwm->rows_per_field > h + 1)) |
++ BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h)) |
++ BITS(VEC_MODE_HFP_EN, (hwm->total_cols > w + 1)) |
++ BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w)) |
++ BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
++ BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
++ for (i = 0; i < ARRAY_SIZE(hwm->back_end_regs); ++i) {
++ writel(hwm->back_end_regs[i],
++ vec->hw_base[RP1VEC_HW_BLOCK_VEC] + 0x80 + 4 * i);
++ }
++
++ /* Apply modifications */
++ if (tvstd == RP1VEC_TVSTD_NTSC_J && mode_family == 0) {
++ /* Reduce pedestal (not quite to zero, for FIR overshoot); increase gain */
++ VEC_WRITE(VEC_DAC_BC,
++ BITS(VEC_DAC_BC_S11_PEDESTAL, 10) |
++ (hwm->back_end_regs[(0xBC - 0x80) / 4] & ~VEC_DAC_BC_S11_PEDESTAL_BITS));
++ VEC_WRITE(VEC_DAC_C8,
++ BITS(VEC_DAC_C8_U16_SCALE_LUMA, 0x9400) |
++ (hwm->back_end_regs[(0xC8 - 0x80) / 4] &
++ ~VEC_DAC_C8_U16_SCALE_LUMA_BITS));
++ } else if ((tvstd == RP1VEC_TVSTD_NTSC_443 || tvstd == RP1VEC_TVSTD_PAL60) &&
++ mode_family != 1) {
++ /* Change colour carrier frequency to 4433618.75 Hz; disable hard sync */
++ VEC_WRITE(VEC_DAC_D4, 0xcc48c1d1);
++ VEC_WRITE(VEC_DAC_D8, 0x0a8262b2);
++ VEC_WRITE(VEC_DAC_EC,
++ hwm->back_end_regs[(0xEC - 0x80) / 4] & ~VEC_DAC_EC_SEQ_EN_BITS);
++ } else if (tvstd == RP1VEC_TVSTD_PAL_N && mode_family == 1) {
++ /* Change colour carrier frequency to 3582056.25 Hz */
++ VEC_WRITE(VEC_DAC_D4, 0x9ce075f7);
++ VEC_WRITE(VEC_DAC_D8, 0x087da511);
++ }
++
++ /* Input pixel format conversion */
++ for (i = 0; i < ARRAY_SIZE(my_formats); ++i) {
++ if (my_formats[i].format == in_format)
++ break;
++ }
++ if (i >= ARRAY_SIZE(my_formats)) {
++ dev_err(&vec->pdev->dev, "%s: bad input format\n", __func__);
++ i = 0;
++ }
++ VEC_WRITE(VEC_IMASK, my_formats[i].mask);
++ VEC_WRITE(VEC_SHIFT, my_formats[i].shift);
++ VEC_WRITE(VEC_RGBSZ, my_formats[i].rgbsz);
++
++ VEC_WRITE(VEC_IRQ_FLAGS, 0xffffffff);
++ rp1vec_hw_vblank_ctrl(vec, 1);
++
++ i = rp1vec_hw_busy(vec);
++ if (i)
++ dev_warn(&vec->pdev->dev,
++ "%s: VEC unexpectedly busy at start (0x%08x)",
++ __func__, VEC_READ(VEC_STATUS));
++
++ VEC_WRITE(VEC_CONTROL,
++ BITS(VEC_CONTROL_START_ARM, (!i)) |
++ BITS(VEC_CONTROL_AUTO_REPEAT, 1));
++}
++
++void rp1vec_hw_update(struct rp1_vec *vec, dma_addr_t addr, u32 offset, u32 stride)
++{
++ /*
++ * Update STRIDE, DMAH and DMAL only. When called after rp1vec_hw_setup(),
++ * DMA starts immediately; if already running, the buffer will flip at
++ * the next vertical sync event.
++ */
++ u64 a = addr + offset;
++
++ VEC_WRITE(VEC_DMA_STRIDE, stride);
++ VEC_WRITE(VEC_DMA_ADDR_H, a >> 32);
++ VEC_WRITE(VEC_DMA_ADDR_L, a & 0xFFFFFFFFu);
++}
++
++void rp1vec_hw_stop(struct rp1_vec *vec)
++{
++ /*
++ * Stop DMA by turning off the Auto-Repeat flag, and wait up to 100ms for
++ * the current and any queued frame to end. "Force drain" flags are not used,
++ * as they seem to prevent DMA from re-starting properly; it's safer to wait.
++ */
++
++ reinit_completion(&vec->finished);
++ VEC_WRITE(VEC_CONTROL, 0);
++ if (!wait_for_completion_timeout(&vec->finished, HZ / 10))
++ drm_err(vec->drm, "%s: timed out waiting for idle\n", __func__);
++ VEC_WRITE(VEC_IRQ_ENABLES, 0);
++}
++
++void rp1vec_hw_vblank_ctrl(struct rp1_vec *vec, int enable)
++{
++ VEC_WRITE(VEC_IRQ_ENABLES,
++ BITS(VEC_IRQ_ENABLES_DONE, 1) |
++ BITS(VEC_IRQ_ENABLES_DMA, (enable ? 1 : 0)) |
++ BITS(VEC_IRQ_ENABLES_MATCH_ROW, 1023));
++}
++
++irqreturn_t rp1vec_hw_isr(int irq, void *dev)
++{
++ struct rp1_vec *vec = dev;
++ u32 u = VEC_READ(VEC_IRQ_FLAGS);
++
++ if (u) {
++ VEC_WRITE(VEC_IRQ_FLAGS, u);
++ if (u & VEC_IRQ_FLAGS_DMA_BITS)
++ drm_crtc_handle_vblank(&vec->pipe.crtc);
++ if (u & VEC_IRQ_FLAGS_DONE_BITS)
++ complete(&vec->finished);
++ }
++ return u ? IRQ_HANDLED : IRQ_NONE;
++}
+--- /dev/null
++++ b/drivers/gpu/drm/rp1/rp1-vec/vec_regs.h
+@@ -0,0 +1,1420 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++// =============================================================================
++// Copyright Raspberry Pi Ltd. 2023
++// vrbuild version: 56aac1a23c016cbbd229108f3b6efc1343842156-clean
++// THIS FILE IS GENERATED BY VRBUILD - DO NOT EDIT
++// =============================================================================
++// Register block : VEC
++// Version : 1
++// Bus type : apb
++// Description : None
++// =============================================================================
++#ifndef VEC_REGS_DEFINED
++#define VEC_REGS_DEFINED
++#define VEC_REGS_RWTYPE_MSB 13
++#define VEC_REGS_RWTYPE_LSB 12
++// =============================================================================
++// Register : VEC_CONTROL
++// JTAG access : synchronous
++// Description : None
++#define VEC_CONTROL_OFFSET 0x00000000
++#define VEC_CONTROL_BITS 0x00000007
++#define VEC_CONTROL_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_CONTROL_BARS
++// Description : Write '1' to display colour bar test pattern
++#define VEC_CONTROL_BARS_RESET 0x0
++#define VEC_CONTROL_BARS_BITS 0x00000004
++#define VEC_CONTROL_BARS_MSB 2
++#define VEC_CONTROL_BARS_LSB 2
++#define VEC_CONTROL_BARS_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_CONTROL_AUTO_REPEAT
++// Description : Write '1' to re-display same frame continuously
++#define VEC_CONTROL_AUTO_REPEAT_RESET 0x0
++#define VEC_CONTROL_AUTO_REPEAT_BITS 0x00000002
++#define VEC_CONTROL_AUTO_REPEAT_MSB 1
++#define VEC_CONTROL_AUTO_REPEAT_LSB 1
++#define VEC_CONTROL_AUTO_REPEAT_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_CONTROL_START_ARM
++// Description : Write '1' before first DMA address is written This bit always
++// reads back as '0'
++#define VEC_CONTROL_START_ARM_RESET 0x0
++#define VEC_CONTROL_START_ARM_BITS 0x00000001
++#define VEC_CONTROL_START_ARM_MSB 0
++#define VEC_CONTROL_START_ARM_LSB 0
++#define VEC_CONTROL_START_ARM_ACCESS "SC"
++// =============================================================================
++// Register : VEC_IRQ_ENABLES
++// JTAG access : synchronous
++// Description : None
++#define VEC_IRQ_ENABLES_OFFSET 0x00000004
++#define VEC_IRQ_ENABLES_BITS 0x03ff003f
++#define VEC_IRQ_ENABLES_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_MATCH_ROW
++// Description : Raster line at which MATCH interrupt is signalled
++#define VEC_IRQ_ENABLES_MATCH_ROW_RESET 0x000
++#define VEC_IRQ_ENABLES_MATCH_ROW_BITS 0x03ff0000
++#define VEC_IRQ_ENABLES_MATCH_ROW_MSB 25
++#define VEC_IRQ_ENABLES_MATCH_ROW_LSB 16
++#define VEC_IRQ_ENABLES_MATCH_ROW_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_MATCH
++// Description : Output raster == match_row reached
++#define VEC_IRQ_ENABLES_MATCH_RESET 0x0
++#define VEC_IRQ_ENABLES_MATCH_BITS 0x00000020
++#define VEC_IRQ_ENABLES_MATCH_MSB 5
++#define VEC_IRQ_ENABLES_MATCH_LSB 5
++#define VEC_IRQ_ENABLES_MATCH_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_ERROR
++// Description : DMA address overwritten before it was taken
++#define VEC_IRQ_ENABLES_ERROR_RESET 0x0
++#define VEC_IRQ_ENABLES_ERROR_BITS 0x00000010
++#define VEC_IRQ_ENABLES_ERROR_MSB 4
++#define VEC_IRQ_ENABLES_ERROR_LSB 4
++#define VEC_IRQ_ENABLES_ERROR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_DONE
++// Description : Last word sent to DAC after end of video (= all clear)
++#define VEC_IRQ_ENABLES_DONE_RESET 0x0
++#define VEC_IRQ_ENABLES_DONE_BITS 0x00000008
++#define VEC_IRQ_ENABLES_DONE_MSB 3
++#define VEC_IRQ_ENABLES_DONE_LSB 3
++#define VEC_IRQ_ENABLES_DONE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_FRAME
++// Description : Start of frame
++#define VEC_IRQ_ENABLES_FRAME_RESET 0x0
++#define VEC_IRQ_ENABLES_FRAME_BITS 0x00000004
++#define VEC_IRQ_ENABLES_FRAME_MSB 2
++#define VEC_IRQ_ENABLES_FRAME_LSB 2
++#define VEC_IRQ_ENABLES_FRAME_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_UNDERFLOW
++// Description : Underflow has occurred
++#define VEC_IRQ_ENABLES_UNDERFLOW_RESET 0x0
++#define VEC_IRQ_ENABLES_UNDERFLOW_BITS 0x00000002
++#define VEC_IRQ_ENABLES_UNDERFLOW_MSB 1
++#define VEC_IRQ_ENABLES_UNDERFLOW_LSB 1
++#define VEC_IRQ_ENABLES_UNDERFLOW_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_ENABLES_DMA
++// Description : DMA ready to accept next frame start address
++#define VEC_IRQ_ENABLES_DMA_RESET 0x0
++#define VEC_IRQ_ENABLES_DMA_BITS 0x00000001
++#define VEC_IRQ_ENABLES_DMA_MSB 0
++#define VEC_IRQ_ENABLES_DMA_LSB 0
++#define VEC_IRQ_ENABLES_DMA_ACCESS "RW"
++// =============================================================================
++// Register : VEC_IRQ_FLAGS
++// JTAG access : synchronous
++// Description : None
++#define VEC_IRQ_FLAGS_OFFSET 0x00000008
++#define VEC_IRQ_FLAGS_BITS 0x0000003f
++#define VEC_IRQ_FLAGS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_FLAGS_MATCH
++// Description : Output raster == match_row reached
++#define VEC_IRQ_FLAGS_MATCH_RESET 0x0
++#define VEC_IRQ_FLAGS_MATCH_BITS 0x00000020
++#define VEC_IRQ_FLAGS_MATCH_MSB 5
++#define VEC_IRQ_FLAGS_MATCH_LSB 5
++#define VEC_IRQ_FLAGS_MATCH_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_FLAGS_ERROR
++// Description : DMA address overwritten before it was taken
++#define VEC_IRQ_FLAGS_ERROR_RESET 0x0
++#define VEC_IRQ_FLAGS_ERROR_BITS 0x00000010
++#define VEC_IRQ_FLAGS_ERROR_MSB 4
++#define VEC_IRQ_FLAGS_ERROR_LSB 4
++#define VEC_IRQ_FLAGS_ERROR_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_FLAGS_DONE
++// Description : Last word sent to DAC after end of video (= all clear)
++#define VEC_IRQ_FLAGS_DONE_RESET 0x0
++#define VEC_IRQ_FLAGS_DONE_BITS 0x00000008
++#define VEC_IRQ_FLAGS_DONE_MSB 3
++#define VEC_IRQ_FLAGS_DONE_LSB 3
++#define VEC_IRQ_FLAGS_DONE_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_FLAGS_FRAME
++// Description : Start of frame
++#define VEC_IRQ_FLAGS_FRAME_RESET 0x0
++#define VEC_IRQ_FLAGS_FRAME_BITS 0x00000004
++#define VEC_IRQ_FLAGS_FRAME_MSB 2
++#define VEC_IRQ_FLAGS_FRAME_LSB 2
++#define VEC_IRQ_FLAGS_FRAME_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_FLAGS_UNDERFLOW
++// Description : Underflow has occurred
++#define VEC_IRQ_FLAGS_UNDERFLOW_RESET 0x0
++#define VEC_IRQ_FLAGS_UNDERFLOW_BITS 0x00000002
++#define VEC_IRQ_FLAGS_UNDERFLOW_MSB 1
++#define VEC_IRQ_FLAGS_UNDERFLOW_LSB 1
++#define VEC_IRQ_FLAGS_UNDERFLOW_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field : VEC_IRQ_FLAGS_DMA
++// Description : DMA ready to accept next frame start address
++#define VEC_IRQ_FLAGS_DMA_RESET 0x0
++#define VEC_IRQ_FLAGS_DMA_BITS 0x00000001
++#define VEC_IRQ_FLAGS_DMA_MSB 0
++#define VEC_IRQ_FLAGS_DMA_LSB 0
++#define VEC_IRQ_FLAGS_DMA_ACCESS "WC"
++// =============================================================================
++// Register : VEC_QOS
++// JTAG access : synchronous
++// Description : This register configures panic levels for the AXI ar_qos
++// quality of service field. Panic status is driven by the number
++// of rows held in the SRAM cache:
++#define VEC_QOS_OFFSET 0x0000000c
++#define VEC_QOS_BITS 0x000fffff
++#define VEC_QOS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_QOS_UQOS
++// Description : Upper AXI QOS
++#define VEC_QOS_UQOS_RESET 0x0
++#define VEC_QOS_UQOS_BITS 0x000f0000
++#define VEC_QOS_UQOS_MSB 19
++#define VEC_QOS_UQOS_LSB 16
++#define VEC_QOS_UQOS_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_QOS_ULEV
++// Description : Upper trip level (resolution = 1 / 16 of cache size)
++#define VEC_QOS_ULEV_RESET 0x0
++#define VEC_QOS_ULEV_BITS 0x0000f000
++#define VEC_QOS_ULEV_MSB 15
++#define VEC_QOS_ULEV_LSB 12
++#define VEC_QOS_ULEV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_QOS_LQOS
++// Description : Lower AXI QOS
++#define VEC_QOS_LQOS_RESET 0x0
++#define VEC_QOS_LQOS_BITS 0x00000f00
++#define VEC_QOS_LQOS_MSB 11
++#define VEC_QOS_LQOS_LSB 8
++#define VEC_QOS_LQOS_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_QOS_LLEV
++// Description : Lower trip level (resolution = 1 / 16 of cache size)
++#define VEC_QOS_LLEV_RESET 0x0
++#define VEC_QOS_LLEV_BITS 0x000000f0
++#define VEC_QOS_LLEV_MSB 7
++#define VEC_QOS_LLEV_LSB 4
++#define VEC_QOS_LLEV_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_QOS_DQOS
++// Description : Default QOS
++#define VEC_QOS_DQOS_RESET 0x0
++#define VEC_QOS_DQOS_BITS 0x0000000f
++#define VEC_QOS_DQOS_MSB 3
++#define VEC_QOS_DQOS_LSB 0
++#define VEC_QOS_DQOS_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DMA_ADDR_L
++// JTAG access : synchronous
++// Description : Lower 32-bits
++#define VEC_DMA_ADDR_L_OFFSET 0x00000010
++#define VEC_DMA_ADDR_L_BITS 0xffffffff
++#define VEC_DMA_ADDR_L_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DMA_ADDR_L_AXI_ADDR
++// Description : Byte address of DMA transfer frame buffer.
++#define VEC_DMA_ADDR_L_AXI_ADDR_RESET 0x00000000
++#define VEC_DMA_ADDR_L_AXI_ADDR_BITS 0xffffffff
++#define VEC_DMA_ADDR_L_AXI_ADDR_MSB 31
++#define VEC_DMA_ADDR_L_AXI_ADDR_LSB 0
++#define VEC_DMA_ADDR_L_AXI_ADDR_ACCESS "RWF"
++// =============================================================================
++// Register : VEC_DMA_STRIDE
++// JTAG access : synchronous
++// Description : This register sets the line byte stride.
++#define VEC_DMA_STRIDE_OFFSET 0x00000014
++#define VEC_DMA_STRIDE_BITS 0xffffffff
++#define VEC_DMA_STRIDE_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DMA_STRIDE_STRIDE
++// Description : Byte stride
++#define VEC_DMA_STRIDE_STRIDE_RESET 0x00000000
++#define VEC_DMA_STRIDE_STRIDE_BITS 0xffffffff
++#define VEC_DMA_STRIDE_STRIDE_MSB 31
++#define VEC_DMA_STRIDE_STRIDE_LSB 0
++#define VEC_DMA_STRIDE_STRIDE_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DMA_AREA
++// JTAG access : synchronous
++// Description : Interlaced pixel area. See example driver code.
++#define VEC_DMA_AREA_OFFSET 0x00000018
++#define VEC_DMA_AREA_BITS 0x03ff03ff
++#define VEC_DMA_AREA_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DMA_AREA_COLS_MINUS1
++// Description : Width
++#define VEC_DMA_AREA_COLS_MINUS1_RESET 0x000
++#define VEC_DMA_AREA_COLS_MINUS1_BITS 0x03ff0000
++#define VEC_DMA_AREA_COLS_MINUS1_MSB 25
++#define VEC_DMA_AREA_COLS_MINUS1_LSB 16
++#define VEC_DMA_AREA_COLS_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1
++// Description : Lines per field = half of lines per interlaced frame
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_RESET 0x000
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_BITS 0x000003ff
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_MSB 9
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_LSB 0
++#define VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1_ACCESS "RW"
++// =============================================================================
++// Register : VEC_YUV_SCALING
++// JTAG access : synchronous
++// Description : None
++#define VEC_YUV_SCALING_OFFSET 0x0000001c
++#define VEC_YUV_SCALING_BITS 0x3fffffff
++#define VEC_YUV_SCALING_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_YUV_SCALING_U10_SCALE_Y
++// Description : Y unsigned scaling factor - 8 binary places
++#define VEC_YUV_SCALING_U10_SCALE_Y_RESET 0x000
++#define VEC_YUV_SCALING_U10_SCALE_Y_BITS 0x3ff00000
++#define VEC_YUV_SCALING_U10_SCALE_Y_MSB 29
++#define VEC_YUV_SCALING_U10_SCALE_Y_LSB 20
++#define VEC_YUV_SCALING_U10_SCALE_Y_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_YUV_SCALING_S10_SCALE_U
++// Description : U signed scaling factor - 8 binary places
++#define VEC_YUV_SCALING_S10_SCALE_U_RESET 0x000
++#define VEC_YUV_SCALING_S10_SCALE_U_BITS 0x000ffc00
++#define VEC_YUV_SCALING_S10_SCALE_U_MSB 19
++#define VEC_YUV_SCALING_S10_SCALE_U_LSB 10
++#define VEC_YUV_SCALING_S10_SCALE_U_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_YUV_SCALING_S10_SCALE_V
++// Description : V signed scaling factor - 8 binary please
++#define VEC_YUV_SCALING_S10_SCALE_V_RESET 0x000
++#define VEC_YUV_SCALING_S10_SCALE_V_BITS 0x000003ff
++#define VEC_YUV_SCALING_S10_SCALE_V_MSB 9
++#define VEC_YUV_SCALING_S10_SCALE_V_LSB 0
++#define VEC_YUV_SCALING_S10_SCALE_V_ACCESS "RW"
++// =============================================================================
++// Register : VEC_BACK_PORCH
++// JTAG access : synchronous
++// Description : None
++#define VEC_BACK_PORCH_OFFSET 0x00000020
++#define VEC_BACK_PORCH_BITS 0x03ff03ff
++#define VEC_BACK_PORCH_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_BACK_PORCH_HBP_MINUS1
++// Description : Horizontal back porch
++#define VEC_BACK_PORCH_HBP_MINUS1_RESET 0x000
++#define VEC_BACK_PORCH_HBP_MINUS1_BITS 0x03ff0000
++#define VEC_BACK_PORCH_HBP_MINUS1_MSB 25
++#define VEC_BACK_PORCH_HBP_MINUS1_LSB 16
++#define VEC_BACK_PORCH_HBP_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_BACK_PORCH_VBP_MINUS1
++// Description : Vertical back porch
++#define VEC_BACK_PORCH_VBP_MINUS1_RESET 0x000
++#define VEC_BACK_PORCH_VBP_MINUS1_BITS 0x000003ff
++#define VEC_BACK_PORCH_VBP_MINUS1_MSB 9
++#define VEC_BACK_PORCH_VBP_MINUS1_LSB 0
++#define VEC_BACK_PORCH_VBP_MINUS1_ACCESS "RW"
++// =============================================================================
++// Register : VEC_FRONT_PORCH
++// JTAG access : synchronous
++// Description : None
++#define VEC_FRONT_PORCH_OFFSET 0x00000024
++#define VEC_FRONT_PORCH_BITS 0x03ff03ff
++#define VEC_FRONT_PORCH_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_FRONT_PORCH_HFP_MINUS1
++// Description : Horizontal front porch
++#define VEC_FRONT_PORCH_HFP_MINUS1_RESET 0x000
++#define VEC_FRONT_PORCH_HFP_MINUS1_BITS 0x03ff0000
++#define VEC_FRONT_PORCH_HFP_MINUS1_MSB 25
++#define VEC_FRONT_PORCH_HFP_MINUS1_LSB 16
++#define VEC_FRONT_PORCH_HFP_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_FRONT_PORCH_VFP_MINUS1
++// Description : Vertical front porch
++#define VEC_FRONT_PORCH_VFP_MINUS1_RESET 0x000
++#define VEC_FRONT_PORCH_VFP_MINUS1_BITS 0x000003ff
++#define VEC_FRONT_PORCH_VFP_MINUS1_MSB 9
++#define VEC_FRONT_PORCH_VFP_MINUS1_LSB 0
++#define VEC_FRONT_PORCH_VFP_MINUS1_ACCESS "RW"
++// =============================================================================
++// Register : VEC_SHIFT
++// JTAG access : synchronous
++// Description : Positions of R,G,B MS bits in the memory word. Note: due to an
++// unintended red/blue swap, these fields have been renamed since
++// a previous version. There is no functional change.
++#define VEC_SHIFT_OFFSET 0x00000028
++#define VEC_SHIFT_BITS 0x00007fff
++#define VEC_SHIFT_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_SHIFT_SHIFT_R
++// Description : Red MSB
++#define VEC_SHIFT_SHIFT_R_RESET 0x00
++#define VEC_SHIFT_SHIFT_R_BITS 0x00007c00
++#define VEC_SHIFT_SHIFT_R_MSB 14
++#define VEC_SHIFT_SHIFT_R_LSB 10
++#define VEC_SHIFT_SHIFT_R_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_SHIFT_SHIFT_G
++// Description : Green MSB
++#define VEC_SHIFT_SHIFT_G_RESET 0x00
++#define VEC_SHIFT_SHIFT_G_BITS 0x000003e0
++#define VEC_SHIFT_SHIFT_G_MSB 9
++#define VEC_SHIFT_SHIFT_G_LSB 5
++#define VEC_SHIFT_SHIFT_G_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_SHIFT_SHIFT_B
++// Description : Blue MSB
++#define VEC_SHIFT_SHIFT_B_RESET 0x00
++#define VEC_SHIFT_SHIFT_B_BITS 0x0000001f
++#define VEC_SHIFT_SHIFT_B_MSB 4
++#define VEC_SHIFT_SHIFT_B_LSB 0
++#define VEC_SHIFT_SHIFT_B_ACCESS "RW"
++// =============================================================================
++// Register : VEC_IMASK
++// JTAG access : synchronous
++// Description : Masks for R,G,B significant bits, left-justified within 10-bit
++// fields.
++#define VEC_IMASK_OFFSET 0x0000002c
++#define VEC_IMASK_BITS 0x3fffffff
++#define VEC_IMASK_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_IMASK_MASK_R
++// Description : Red mask
++#define VEC_IMASK_MASK_R_RESET 0x000
++#define VEC_IMASK_MASK_R_BITS 0x3ff00000
++#define VEC_IMASK_MASK_R_MSB 29
++#define VEC_IMASK_MASK_R_LSB 20
++#define VEC_IMASK_MASK_R_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IMASK_MASK_G
++// Description : Green mask
++#define VEC_IMASK_MASK_G_RESET 0x000
++#define VEC_IMASK_MASK_G_BITS 0x000ffc00
++#define VEC_IMASK_MASK_G_MSB 19
++#define VEC_IMASK_MASK_G_LSB 10
++#define VEC_IMASK_MASK_G_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_IMASK_MASK_B
++// Description : Blue mask
++#define VEC_IMASK_MASK_B_RESET 0x000
++#define VEC_IMASK_MASK_B_BITS 0x000003ff
++#define VEC_IMASK_MASK_B_MSB 9
++#define VEC_IMASK_MASK_B_LSB 0
++#define VEC_IMASK_MASK_B_ACCESS "RW"
++// =============================================================================
++// Register : VEC_MODE
++// JTAG access : synchronous
++// Description : None
++#define VEC_MODE_OFFSET 0x00000030
++#define VEC_MODE_BITS 0x01ff003f
++#define VEC_MODE_RESET 0x01c00000
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_HIGH_WATER
++// Description : ALWAYS WRITE 8'hE0
++#define VEC_MODE_HIGH_WATER_RESET 0xe0
++#define VEC_MODE_HIGH_WATER_BITS 0x01fe0000
++#define VEC_MODE_HIGH_WATER_MSB 24
++#define VEC_MODE_HIGH_WATER_LSB 17
++#define VEC_MODE_HIGH_WATER_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_ALIGN16
++// Description : Data: 0=BYTE aligned; 1=BEAT aligned
++#define VEC_MODE_ALIGN16_RESET 0x0
++#define VEC_MODE_ALIGN16_BITS 0x00010000
++#define VEC_MODE_ALIGN16_MSB 16
++#define VEC_MODE_ALIGN16_LSB 16
++#define VEC_MODE_ALIGN16_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_VFP_EN
++// Description : Enable vertical front porch
++#define VEC_MODE_VFP_EN_RESET 0x0
++#define VEC_MODE_VFP_EN_BITS 0x00000020
++#define VEC_MODE_VFP_EN_MSB 5
++#define VEC_MODE_VFP_EN_LSB 5
++#define VEC_MODE_VFP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_VBP_EN
++// Description : Enable vertical back porch
++#define VEC_MODE_VBP_EN_RESET 0x0
++#define VEC_MODE_VBP_EN_BITS 0x00000010
++#define VEC_MODE_VBP_EN_MSB 4
++#define VEC_MODE_VBP_EN_LSB 4
++#define VEC_MODE_VBP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_HFP_EN
++// Description : Enable horizontal front porch
++#define VEC_MODE_HFP_EN_RESET 0x0
++#define VEC_MODE_HFP_EN_BITS 0x00000008
++#define VEC_MODE_HFP_EN_MSB 3
++#define VEC_MODE_HFP_EN_LSB 3
++#define VEC_MODE_HFP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_HBP_EN
++// Description : Enable horizontal back porch
++#define VEC_MODE_HBP_EN_RESET 0x0
++#define VEC_MODE_HBP_EN_BITS 0x00000004
++#define VEC_MODE_HBP_EN_MSB 2
++#define VEC_MODE_HBP_EN_LSB 2
++#define VEC_MODE_HBP_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_FIELDS_PER_FRAME_MINUS1
++// Description : Interlaced / progressive
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_RESET 0x0
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_BITS 0x00000002
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_MSB 1
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_LSB 1
++#define VEC_MODE_FIELDS_PER_FRAME_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_MODE_FIRST_FIELD_ODD
++// Description : Interlacing order: odd/even or even/odd
++#define VEC_MODE_FIRST_FIELD_ODD_RESET 0x0
++#define VEC_MODE_FIRST_FIELD_ODD_BITS 0x00000001
++#define VEC_MODE_FIRST_FIELD_ODD_MSB 0
++#define VEC_MODE_FIRST_FIELD_ODD_LSB 0
++#define VEC_MODE_FIRST_FIELD_ODD_ACCESS "RW"
++// =============================================================================
++// Register : VEC_RGBSZ
++// JTAG access : synchronous
++// Description : None
++#define VEC_RGBSZ_OFFSET 0x00000034
++#define VEC_RGBSZ_BITS 0x00030fff
++#define VEC_RGBSZ_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1
++// Description : Pixel stride
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_RESET 0x0
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_BITS 0x00030000
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_MSB 17
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_LSB 16
++#define VEC_RGBSZ_BYTES_PER_PIXEL_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_RGBSZ_SCALE_R
++// Description : Red number of bits for shift-and-OR scaling
++#define VEC_RGBSZ_SCALE_R_RESET 0x0
++#define VEC_RGBSZ_SCALE_R_BITS 0x00000f00
++#define VEC_RGBSZ_SCALE_R_MSB 11
++#define VEC_RGBSZ_SCALE_R_LSB 8
++#define VEC_RGBSZ_SCALE_R_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_RGBSZ_SCALE_G
++// Description : Green number of bits for shift-and-OR scaling
++#define VEC_RGBSZ_SCALE_G_RESET 0x0
++#define VEC_RGBSZ_SCALE_G_BITS 0x000000f0
++#define VEC_RGBSZ_SCALE_G_MSB 7
++#define VEC_RGBSZ_SCALE_G_LSB 4
++#define VEC_RGBSZ_SCALE_G_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_RGBSZ_SCALE_B
++// Description : Blue number of bits for shift-and-OR scaling
++#define VEC_RGBSZ_SCALE_B_RESET 0x0
++#define VEC_RGBSZ_SCALE_B_BITS 0x0000000f
++#define VEC_RGBSZ_SCALE_B_MSB 3
++#define VEC_RGBSZ_SCALE_B_LSB 0
++#define VEC_RGBSZ_SCALE_B_ACCESS "RW"
++// =============================================================================
++// Register : VEC_PANICS
++// JTAG access : synchronous
++// Description : None
++#define VEC_PANICS_OFFSET 0x00000038
++#define VEC_PANICS_BITS 0xffffffff
++#define VEC_PANICS_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_PANICS_UCOUNT
++// Description : Upper panic count
++#define VEC_PANICS_UCOUNT_RESET 0x0000
++#define VEC_PANICS_UCOUNT_BITS 0xffff0000
++#define VEC_PANICS_UCOUNT_MSB 31
++#define VEC_PANICS_UCOUNT_LSB 16
++#define VEC_PANICS_UCOUNT_ACCESS "WC"
++// -----------------------------------------------------------------------------
++// Field : VEC_PANICS_LCOUNT
++// Description : Lower panic count
++#define VEC_PANICS_LCOUNT_RESET 0x0000
++#define VEC_PANICS_LCOUNT_BITS 0x0000ffff
++#define VEC_PANICS_LCOUNT_MSB 15
++#define VEC_PANICS_LCOUNT_LSB 0
++#define VEC_PANICS_LCOUNT_ACCESS "WC"
++// =============================================================================
++// Register : VEC_STATUS
++// JTAG access : synchronous
++// Description : None
++#define VEC_STATUS_OFFSET 0x0000003c
++#define VEC_STATUS_BITS 0xff000000
++#define VEC_STATUS_RESET 0x0d000000
++// -----------------------------------------------------------------------------
++// Field : VEC_STATUS_VERSION
++// Description : VEC module version code
++#define VEC_STATUS_VERSION_RESET 0x0d
++#define VEC_STATUS_VERSION_BITS 0xff000000
++#define VEC_STATUS_VERSION_MSB 31
++#define VEC_STATUS_VERSION_LSB 24
++#define VEC_STATUS_VERSION_ACCESS "RO"
++// =============================================================================
++// Register : VEC_DMA_ADDR_H
++// JTAG access : synchronous
++// Description : Upper 32-bits
++#define VEC_DMA_ADDR_H_OFFSET 0x00000040
++#define VEC_DMA_ADDR_H_BITS 0xffffffff
++#define VEC_DMA_ADDR_H_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DMA_ADDR_H_AXI_ADDR
++// Description : Byte address of DMA transfer frame buffer.
++#define VEC_DMA_ADDR_H_AXI_ADDR_RESET 0x00000000
++#define VEC_DMA_ADDR_H_AXI_ADDR_BITS 0xffffffff
++#define VEC_DMA_ADDR_H_AXI_ADDR_MSB 31
++#define VEC_DMA_ADDR_H_AXI_ADDR_LSB 0
++#define VEC_DMA_ADDR_H_AXI_ADDR_ACCESS "RW"
++// =============================================================================
++// Register : VEC_BURST_ADDR_L
++// JTAG access : synchronous
++// Description : None
++#define VEC_BURST_ADDR_L_OFFSET 0x00000044
++#define VEC_BURST_ADDR_L_BITS 0xffffffff
++#define VEC_BURST_ADDR_L_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_BURST_ADDR_L_BURST_ADDR
++// Description : the lower 32-bits of the most recent read request sent to AXI
++// memory.
++#define VEC_BURST_ADDR_L_BURST_ADDR_RESET 0x00000000
++#define VEC_BURST_ADDR_L_BURST_ADDR_BITS 0xffffffff
++#define VEC_BURST_ADDR_L_BURST_ADDR_MSB 31
++#define VEC_BURST_ADDR_L_BURST_ADDR_LSB 0
++#define VEC_BURST_ADDR_L_BURST_ADDR_ACCESS "RO"
++// =============================================================================
++// Register : VEC_APB_TIMEOUT
++// JTAG access : synchronous
++// Description : None
++#define VEC_APB_TIMEOUT_OFFSET 0x00000048
++#define VEC_APB_TIMEOUT_BITS 0x000103ff
++#define VEC_APB_TIMEOUT_RESET 0x00000014
++// -----------------------------------------------------------------------------
++// Field : VEC_APB_TIMEOUT_SLVERR_EN
++// Description : 1 = Assert PREADY and PSLVERR on timeout 0 = Assert PREADY only
++#define VEC_APB_TIMEOUT_SLVERR_EN_RESET 0x0
++#define VEC_APB_TIMEOUT_SLVERR_EN_BITS 0x00010000
++#define VEC_APB_TIMEOUT_SLVERR_EN_MSB 16
++#define VEC_APB_TIMEOUT_SLVERR_EN_LSB 16
++#define VEC_APB_TIMEOUT_SLVERR_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_APB_TIMEOUT_TIMEOUT
++// Description : Maximum AXI clock cycles to wait for responses from DAC clock
++// domain APB block
++#define VEC_APB_TIMEOUT_TIMEOUT_RESET 0x014
++#define VEC_APB_TIMEOUT_TIMEOUT_BITS 0x000003ff
++#define VEC_APB_TIMEOUT_TIMEOUT_MSB 9
++#define VEC_APB_TIMEOUT_TIMEOUT_LSB 0
++#define VEC_APB_TIMEOUT_TIMEOUT_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_80
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_80_OFFSET 0x00000080
++#define VEC_DAC_80_BITS 0x3fff3fff
++#define VEC_DAC_80_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_80_U14_DE_BGN
++// Description : Beginning of active data enable within each visible line
++#define VEC_DAC_80_U14_DE_BGN_RESET 0x0000
++#define VEC_DAC_80_U14_DE_BGN_BITS 0x3fff0000
++#define VEC_DAC_80_U14_DE_BGN_MSB 29
++#define VEC_DAC_80_U14_DE_BGN_LSB 16
++#define VEC_DAC_80_U14_DE_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_80_U14_DE_END
++// Description : End of active data enable within each visible line
++#define VEC_DAC_80_U14_DE_END_RESET 0x0000
++#define VEC_DAC_80_U14_DE_END_BITS 0x00003fff
++#define VEC_DAC_80_U14_DE_END_MSB 13
++#define VEC_DAC_80_U14_DE_END_LSB 0
++#define VEC_DAC_80_U14_DE_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_84
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_84_OFFSET 0x00000084
++#define VEC_DAC_84_BITS 0x1fff1fff
++#define VEC_DAC_84_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_84_U13_ACTIVE_RISE
++// Description : Horizontal blanking interval
++#define VEC_DAC_84_U13_ACTIVE_RISE_RESET 0x0000
++#define VEC_DAC_84_U13_ACTIVE_RISE_BITS 0x1fff0000
++#define VEC_DAC_84_U13_ACTIVE_RISE_MSB 28
++#define VEC_DAC_84_U13_ACTIVE_RISE_LSB 16
++#define VEC_DAC_84_U13_ACTIVE_RISE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_84_U13_ACTIVE_FALL
++// Description : Horizontal blanking interval
++#define VEC_DAC_84_U13_ACTIVE_FALL_RESET 0x0000
++#define VEC_DAC_84_U13_ACTIVE_FALL_BITS 0x00001fff
++#define VEC_DAC_84_U13_ACTIVE_FALL_MSB 12
++#define VEC_DAC_84_U13_ACTIVE_FALL_LSB 0
++#define VEC_DAC_84_U13_ACTIVE_FALL_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_88
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_88_OFFSET 0x00000088
++#define VEC_DAC_88_BITS 0x1fff1fff
++#define VEC_DAC_88_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_88_U13_HALF_LINE_PERIOD
++// Description : Ratio of DAC clock to horizontal line rate, halved
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_RESET 0x0000
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_BITS 0x1fff0000
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_MSB 28
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_LSB 16
++#define VEC_DAC_88_U13_HALF_LINE_PERIOD_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_88_U13_HORZ_SYNC
++// Description : Width of horizontal sync pulses
++#define VEC_DAC_88_U13_HORZ_SYNC_RESET 0x0000
++#define VEC_DAC_88_U13_HORZ_SYNC_BITS 0x00001fff
++#define VEC_DAC_88_U13_HORZ_SYNC_MSB 12
++#define VEC_DAC_88_U13_HORZ_SYNC_LSB 0
++#define VEC_DAC_88_U13_HORZ_SYNC_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_8C
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_8C_OFFSET 0x0000008c
++#define VEC_DAC_8C_BITS 0x1fff1fff
++#define VEC_DAC_8C_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_8C_U13_BURST_RISE
++// Description : Start of raised-cosine colour burst envelope
++#define VEC_DAC_8C_U13_BURST_RISE_RESET 0x0000
++#define VEC_DAC_8C_U13_BURST_RISE_BITS 0x1fff0000
++#define VEC_DAC_8C_U13_BURST_RISE_MSB 28
++#define VEC_DAC_8C_U13_BURST_RISE_LSB 16
++#define VEC_DAC_8C_U13_BURST_RISE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_8C_U13_BURST_FALL
++// Description : End of raised-cosine colour burst envelope
++#define VEC_DAC_8C_U13_BURST_FALL_RESET 0x0000
++#define VEC_DAC_8C_U13_BURST_FALL_BITS 0x00001fff
++#define VEC_DAC_8C_U13_BURST_FALL_MSB 12
++#define VEC_DAC_8C_U13_BURST_FALL_LSB 0
++#define VEC_DAC_8C_U13_BURST_FALL_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_90
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_90_OFFSET 0x00000090
++#define VEC_DAC_90_BITS 0x1fff3fff
++#define VEC_DAC_90_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_90_U13_VERT_EQ
++// Description : Width of vertical equalisation pulses (= half line minus
++// serration)
++#define VEC_DAC_90_U13_VERT_EQ_RESET 0x0000
++#define VEC_DAC_90_U13_VERT_EQ_BITS 0x1fff0000
++#define VEC_DAC_90_U13_VERT_EQ_MSB 28
++#define VEC_DAC_90_U13_VERT_EQ_LSB 16
++#define VEC_DAC_90_U13_VERT_EQ_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_90_U14_VERT_SYNC
++// Description : Width of vertical sync pulses
++#define VEC_DAC_90_U14_VERT_SYNC_RESET 0x0000
++#define VEC_DAC_90_U14_VERT_SYNC_BITS 0x00003fff
++#define VEC_DAC_90_U14_VERT_SYNC_MSB 13
++#define VEC_DAC_90_U14_VERT_SYNC_LSB 0
++#define VEC_DAC_90_U14_VERT_SYNC_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_94
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_94_OFFSET 0x00000094
++#define VEC_DAC_94_BITS 0x03ff03ff
++#define VEC_DAC_94_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_94_U10_PRE_EQ_BGN
++// Description : Half-lines, inclusive, relative to field datum, where vertical
++// pre-equalisation pulses start
++#define VEC_DAC_94_U10_PRE_EQ_BGN_RESET 0x000
++#define VEC_DAC_94_U10_PRE_EQ_BGN_BITS 0x03ff0000
++#define VEC_DAC_94_U10_PRE_EQ_BGN_MSB 25
++#define VEC_DAC_94_U10_PRE_EQ_BGN_LSB 16
++#define VEC_DAC_94_U10_PRE_EQ_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_94_U10_PRE_EQ_END
++// Description : Half-lines, inclusive, relative to field datum, where vertical
++// pre-equalisation pulses end
++#define VEC_DAC_94_U10_PRE_EQ_END_RESET 0x000
++#define VEC_DAC_94_U10_PRE_EQ_END_BITS 0x000003ff
++#define VEC_DAC_94_U10_PRE_EQ_END_MSB 9
++#define VEC_DAC_94_U10_PRE_EQ_END_LSB 0
++#define VEC_DAC_94_U10_PRE_EQ_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_98
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_98_OFFSET 0x00000098
++#define VEC_DAC_98_BITS 0x03ff03ff
++#define VEC_DAC_98_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_98_U10_FIELD_SYNC_BGN
++// Description : Half-lines containing vertical sync pulses (inclusive)
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_RESET 0x000
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_BITS 0x03ff0000
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_MSB 25
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_LSB 16
++#define VEC_DAC_98_U10_FIELD_SYNC_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_98_U10_FIELD_SYNC_END
++// Description : Half-lines containing vertical sync pulses (inclusive)
++#define VEC_DAC_98_U10_FIELD_SYNC_END_RESET 0x000
++#define VEC_DAC_98_U10_FIELD_SYNC_END_BITS 0x000003ff
++#define VEC_DAC_98_U10_FIELD_SYNC_END_MSB 9
++#define VEC_DAC_98_U10_FIELD_SYNC_END_LSB 0
++#define VEC_DAC_98_U10_FIELD_SYNC_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_9C
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_9C_OFFSET 0x0000009c
++#define VEC_DAC_9C_BITS 0x03ff03ff
++#define VEC_DAC_9C_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_9C_U10_POST_EQ_BGN
++// Description : Half-lines containing vertical post-equalisation pulses
++#define VEC_DAC_9C_U10_POST_EQ_BGN_RESET 0x000
++#define VEC_DAC_9C_U10_POST_EQ_BGN_BITS 0x03ff0000
++#define VEC_DAC_9C_U10_POST_EQ_BGN_MSB 25
++#define VEC_DAC_9C_U10_POST_EQ_BGN_LSB 16
++#define VEC_DAC_9C_U10_POST_EQ_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_9C_U10_POST_EQ_END
++// Description : Half-lines containing vertical post-equalisation pulses
++#define VEC_DAC_9C_U10_POST_EQ_END_RESET 0x000
++#define VEC_DAC_9C_U10_POST_EQ_END_BITS 0x000003ff
++#define VEC_DAC_9C_U10_POST_EQ_END_MSB 9
++#define VEC_DAC_9C_U10_POST_EQ_END_LSB 0
++#define VEC_DAC_9C_U10_POST_EQ_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_A0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_A0_OFFSET 0x000000a0
++#define VEC_DAC_A0_BITS 0x03ff03ff
++#define VEC_DAC_A0_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_A0_U10_FLD1_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_RESET 0x000
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_BITS 0x03ff0000
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_MSB 25
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_LSB 16
++#define VEC_DAC_A0_U10_FLD1_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_A0_U10_FLD1_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A0_U10_FLD1_BURST_END_RESET 0x000
++#define VEC_DAC_A0_U10_FLD1_BURST_END_BITS 0x000003ff
++#define VEC_DAC_A0_U10_FLD1_BURST_END_MSB 9
++#define VEC_DAC_A0_U10_FLD1_BURST_END_LSB 0
++#define VEC_DAC_A0_U10_FLD1_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_A4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_A4_OFFSET 0x000000a4
++#define VEC_DAC_A4_BITS 0x03ff03ff
++#define VEC_DAC_A4_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_A4_U10_FLD2_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_RESET 0x000
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_BITS 0x03ff0000
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_MSB 25
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_LSB 16
++#define VEC_DAC_A4_U10_FLD2_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_A4_U10_FLD2_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A4_U10_FLD2_BURST_END_RESET 0x000
++#define VEC_DAC_A4_U10_FLD2_BURST_END_BITS 0x000003ff
++#define VEC_DAC_A4_U10_FLD2_BURST_END_MSB 9
++#define VEC_DAC_A4_U10_FLD2_BURST_END_LSB 0
++#define VEC_DAC_A4_U10_FLD2_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_A8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_A8_OFFSET 0x000000a8
++#define VEC_DAC_A8_BITS 0x03ff03ff
++#define VEC_DAC_A8_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_A8_U10_FLD3_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_RESET 0x000
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_BITS 0x03ff0000
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_MSB 25
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_LSB 16
++#define VEC_DAC_A8_U10_FLD3_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_A8_U10_FLD3_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_A8_U10_FLD3_BURST_END_RESET 0x000
++#define VEC_DAC_A8_U10_FLD3_BURST_END_BITS 0x000003ff
++#define VEC_DAC_A8_U10_FLD3_BURST_END_MSB 9
++#define VEC_DAC_A8_U10_FLD3_BURST_END_LSB 0
++#define VEC_DAC_A8_U10_FLD3_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_AC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_AC_OFFSET 0x000000ac
++#define VEC_DAC_AC_BITS 0x03ff03ff
++#define VEC_DAC_AC_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_AC_U10_FLD4_BURST_BGN
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_RESET 0x000
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_BITS 0x03ff0000
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_MSB 25
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_LSB 16
++#define VEC_DAC_AC_U10_FLD4_BURST_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_AC_U10_FLD4_BURST_END
++// Description : First and last full frame lines (1-based numbering) within the
++// PAL/NTSC four field sequence which require a colour burst
++#define VEC_DAC_AC_U10_FLD4_BURST_END_RESET 0x000
++#define VEC_DAC_AC_U10_FLD4_BURST_END_BITS 0x000003ff
++#define VEC_DAC_AC_U10_FLD4_BURST_END_MSB 9
++#define VEC_DAC_AC_U10_FLD4_BURST_END_LSB 0
++#define VEC_DAC_AC_U10_FLD4_BURST_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_B0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_B0_OFFSET 0x000000b0
++#define VEC_DAC_B0_BITS 0x03ff03ff
++#define VEC_DAC_B0_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN
++// Description : First and last full visible lines (1-based numbering) in the
++// PAL/NTSC four field sequence
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_RESET 0x000
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_BITS 0x03ff0000
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_MSB 25
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_LSB 16
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_B0_U10_FLD24_FULL_LINE_END
++// Description : First and last full visible lines (1-based numbering) in the
++// PAL/NTSC four field sequence
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_RESET 0x000
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_BITS 0x000003ff
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_MSB 9
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_LSB 0
++#define VEC_DAC_B0_U10_FLD24_FULL_LINE_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_B4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_B4_OFFSET 0x000000b4
++#define VEC_DAC_B4_BITS 0x03ff03ff
++#define VEC_DAC_B4_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN
++// Description : First and last full visible lines (1-based numbering) in the
++// PAL/NTSC four field sequence
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_RESET 0x000
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_BITS 0x03ff0000
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_MSB 25
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_LSB 16
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_BGN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_B4_U10_FLD13_FULL_LINE_END
++// Description : First and last full visible lines (1-based numbering) in the
++// PAL/NTSC four field sequence
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_RESET 0x000
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_BITS 0x000003ff
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_MSB 9
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_LSB 0
++#define VEC_DAC_B4_U10_FLD13_FULL_LINE_END_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_B8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_B8_OFFSET 0x000000b8
++#define VEC_DAC_B8_BITS 0x03ff03ff
++#define VEC_DAC_B8_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_B8_U10_BOT_HALF_LINE
++// Description : Top and bottom visible half-lines in 1-based standard full
++// frame numbering, for interlaced modes. Set to zero to disable.
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_RESET 0x000
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_BITS 0x03ff0000
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_MSB 25
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_LSB 16
++#define VEC_DAC_B8_U10_BOT_HALF_LINE_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_B8_U10_TOP_HALF_LINE
++// Description : Top and bottom visible half-lines in 1-based standard full
++// frame numbering, for interlaced modes. Set to zero to disable.
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_RESET 0x000
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_BITS 0x000003ff
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_MSB 9
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_LSB 0
++#define VEC_DAC_B8_U10_TOP_HALF_LINE_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_BC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_BC_OFFSET 0x000000bc
++#define VEC_DAC_BC_BITS 0x07ff07ff
++#define VEC_DAC_BC_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_BC_S11_PEDESTAL
++// Description : NTSC pedestal. For 7.5 IRE, this field is 1024 * 7.5/100. For
++// PAL, or Japanese NTSC, this field should be zero.
++#define VEC_DAC_BC_S11_PEDESTAL_RESET 0x000
++#define VEC_DAC_BC_S11_PEDESTAL_BITS 0x07ff0000
++#define VEC_DAC_BC_S11_PEDESTAL_MSB 26
++#define VEC_DAC_BC_S11_PEDESTAL_LSB 16
++#define VEC_DAC_BC_S11_PEDESTAL_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_BC_U11_HALF_LINES_PER_FIELD
++// Description : Mode = 625 PAL, Lines per field = 312.5,
++// u11_half_lines_per_field = 1+2*312 Mode = 525 NTSC, Lines per
++// field = 262.5, u11_half_lines_per_field = 1+2*262
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_RESET 0x000
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_BITS 0x000007ff
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_MSB 10
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_LSB 0
++#define VEC_DAC_BC_U11_HALF_LINES_PER_FIELD_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_C0
++// JTAG access : synchronous
++// Description : Synopsis DesignWare control
++#define VEC_DAC_C0_OFFSET 0x000000c0
++#define VEC_DAC_C0_BITS 0x000fffff
++#define VEC_DAC_C0_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C0_DWC_CABLE_ENCTR3
++// Description : Synopsis test input
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_RESET 0x0
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_BITS 0x00080000
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_MSB 19
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_LSB 19
++#define VEC_DAC_C0_DWC_CABLE_ENCTR3_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C0_DWC_CABLE_CABLEOUT
++// Description : cable detect state
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_RESET 0x0
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_BITS 0x00070000
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_MSB 18
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_LSB 16
++#define VEC_DAC_C0_DWC_CABLE_CABLEOUT_ACCESS "RO"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C0_DWC_MUX_2
++// Description : Select DAC channel 2 output
++#define VEC_DAC_C0_DWC_MUX_2_RESET 0x0
++#define VEC_DAC_C0_DWC_MUX_2_BITS 0x0000c000
++#define VEC_DAC_C0_DWC_MUX_2_MSB 15
++#define VEC_DAC_C0_DWC_MUX_2_LSB 14
++#define VEC_DAC_C0_DWC_MUX_2_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C0_DWC_MUX_1
++// Description : Select DAC channel 1 output
++#define VEC_DAC_C0_DWC_MUX_1_RESET 0x0
++#define VEC_DAC_C0_DWC_MUX_1_BITS 0x00003000
++#define VEC_DAC_C0_DWC_MUX_1_MSB 13
++#define VEC_DAC_C0_DWC_MUX_1_LSB 12
++#define VEC_DAC_C0_DWC_MUX_1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C0_DWC_MUX_0
++// Description : Select DAC channel 0 output
++#define VEC_DAC_C0_DWC_MUX_0_RESET 0x0
++#define VEC_DAC_C0_DWC_MUX_0_BITS 0x00000c00
++#define VEC_DAC_C0_DWC_MUX_0_MSB 11
++#define VEC_DAC_C0_DWC_MUX_0_LSB 10
++#define VEC_DAC_C0_DWC_MUX_0_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C0_DWC_TEST
++// Description : Fixed DAC command word
++#define VEC_DAC_C0_DWC_TEST_RESET 0x000
++#define VEC_DAC_C0_DWC_TEST_BITS 0x000003ff
++#define VEC_DAC_C0_DWC_TEST_MSB 9
++#define VEC_DAC_C0_DWC_TEST_LSB 0
++#define VEC_DAC_C0_DWC_TEST_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_C4
++// JTAG access : synchronous
++// Description : Synopsis DAC control
++#define VEC_DAC_C4_OFFSET 0x000000c4
++#define VEC_DAC_C4_BITS 0x1fffffff
++#define VEC_DAC_C4_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_ENCTR
++// Description : Always write3'b000
++#define VEC_DAC_C4_ENCTR_RESET 0x0
++#define VEC_DAC_C4_ENCTR_BITS 0x1c000000
++#define VEC_DAC_C4_ENCTR_MSB 28
++#define VEC_DAC_C4_ENCTR_LSB 26
++#define VEC_DAC_C4_ENCTR_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_ENSC
++// Description : Enable cable detect - write 3'b000
++#define VEC_DAC_C4_ENSC_RESET 0x0
++#define VEC_DAC_C4_ENSC_BITS 0x03800000
++#define VEC_DAC_C4_ENSC_MSB 25
++#define VEC_DAC_C4_ENSC_LSB 23
++#define VEC_DAC_C4_ENSC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_ENDAC
++// Description : Enable DAC channel
++#define VEC_DAC_C4_ENDAC_RESET 0x0
++#define VEC_DAC_C4_ENDAC_BITS 0x00700000
++#define VEC_DAC_C4_ENDAC_MSB 22
++#define VEC_DAC_C4_ENDAC_LSB 20
++#define VEC_DAC_C4_ENDAC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_ENVBG
++// Description : Enable internal bandgap reference - write '1'
++#define VEC_DAC_C4_ENVBG_RESET 0x0
++#define VEC_DAC_C4_ENVBG_BITS 0x00080000
++#define VEC_DAC_C4_ENVBG_MSB 19
++#define VEC_DAC_C4_ENVBG_LSB 19
++#define VEC_DAC_C4_ENVBG_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_ENEXTREF
++// Description : Enable external reference - write '0'
++#define VEC_DAC_C4_ENEXTREF_RESET 0x0
++#define VEC_DAC_C4_ENEXTREF_BITS 0x00040000
++#define VEC_DAC_C4_ENEXTREF_MSB 18
++#define VEC_DAC_C4_ENEXTREF_LSB 18
++#define VEC_DAC_C4_ENEXTREF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_DAC2GC
++// Description : DAC channel 2 gain control - write 6'd63
++#define VEC_DAC_C4_DAC2GC_RESET 0x00
++#define VEC_DAC_C4_DAC2GC_BITS 0x0003f000
++#define VEC_DAC_C4_DAC2GC_MSB 17
++#define VEC_DAC_C4_DAC2GC_LSB 12
++#define VEC_DAC_C4_DAC2GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_DAC1GC
++// Description : DAC channel 1 gain control - write 6'd63
++#define VEC_DAC_C4_DAC1GC_RESET 0x00
++#define VEC_DAC_C4_DAC1GC_BITS 0x00000fc0
++#define VEC_DAC_C4_DAC1GC_MSB 11
++#define VEC_DAC_C4_DAC1GC_LSB 6
++#define VEC_DAC_C4_DAC1GC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C4_DAC0GC
++// Description : DAC channel 0 gain control - write 6'd63
++#define VEC_DAC_C4_DAC0GC_RESET 0x00
++#define VEC_DAC_C4_DAC0GC_BITS 0x0000003f
++#define VEC_DAC_C4_DAC0GC_MSB 5
++#define VEC_DAC_C4_DAC0GC_LSB 0
++#define VEC_DAC_C4_DAC0GC_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_C8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_C8_OFFSET 0x000000c8
++#define VEC_DAC_C8_BITS 0xffffffff
++#define VEC_DAC_C8_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C8_U16_SCALE_SYNC
++// Description : Scaling applied prior to final summation to form the DAC
++// command word(s)
++#define VEC_DAC_C8_U16_SCALE_SYNC_RESET 0x0000
++#define VEC_DAC_C8_U16_SCALE_SYNC_BITS 0xffff0000
++#define VEC_DAC_C8_U16_SCALE_SYNC_MSB 31
++#define VEC_DAC_C8_U16_SCALE_SYNC_LSB 16
++#define VEC_DAC_C8_U16_SCALE_SYNC_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_C8_U16_SCALE_LUMA
++// Description : Scaling applied prior to final summation to form the DAC
++// command word(s)
++#define VEC_DAC_C8_U16_SCALE_LUMA_RESET 0x0000
++#define VEC_DAC_C8_U16_SCALE_LUMA_BITS 0x0000ffff
++#define VEC_DAC_C8_U16_SCALE_LUMA_MSB 15
++#define VEC_DAC_C8_U16_SCALE_LUMA_LSB 0
++#define VEC_DAC_C8_U16_SCALE_LUMA_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_CC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_CC_OFFSET 0x000000cc
++#define VEC_DAC_CC_BITS 0xffffffff
++#define VEC_DAC_CC_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_CC_S16_SCALE_BURST
++// Description : Scaling applied prior to final summation to form the DAC
++// command word(s)
++#define VEC_DAC_CC_S16_SCALE_BURST_RESET 0x0000
++#define VEC_DAC_CC_S16_SCALE_BURST_BITS 0xffff0000
++#define VEC_DAC_CC_S16_SCALE_BURST_MSB 31
++#define VEC_DAC_CC_S16_SCALE_BURST_LSB 16
++#define VEC_DAC_CC_S16_SCALE_BURST_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_CC_S16_SCALE_CHROMA
++// Description : Scaling applied prior to final summation to form the DAC
++// command word(s)
++#define VEC_DAC_CC_S16_SCALE_CHROMA_RESET 0x0000
++#define VEC_DAC_CC_S16_SCALE_CHROMA_BITS 0x0000ffff
++#define VEC_DAC_CC_S16_SCALE_CHROMA_MSB 15
++#define VEC_DAC_CC_S16_SCALE_CHROMA_LSB 0
++#define VEC_DAC_CC_S16_SCALE_CHROMA_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_D0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_D0_OFFSET 0x000000d0
++#define VEC_DAC_D0_BITS 0xffffffff
++#define VEC_DAC_D0_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_D0_S16_OFFSET_LUMA
++// Description : These offsets are applied to the chroma and luma channels
++// before the final MUX
++#define VEC_DAC_D0_S16_OFFSET_LUMA_RESET 0x0000
++#define VEC_DAC_D0_S16_OFFSET_LUMA_BITS 0xffff0000
++#define VEC_DAC_D0_S16_OFFSET_LUMA_MSB 31
++#define VEC_DAC_D0_S16_OFFSET_LUMA_LSB 16
++#define VEC_DAC_D0_S16_OFFSET_LUMA_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_D0_S16_OFFSET_CHRO
++// Description : These offsets are applied to the chroma and luma channels
++// before the final MUX
++#define VEC_DAC_D0_S16_OFFSET_CHRO_RESET 0x0000
++#define VEC_DAC_D0_S16_OFFSET_CHRO_BITS 0x0000ffff
++#define VEC_DAC_D0_S16_OFFSET_CHRO_MSB 15
++#define VEC_DAC_D0_S16_OFFSET_CHRO_LSB 0
++#define VEC_DAC_D0_S16_OFFSET_CHRO_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_D4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_D4_OFFSET 0x000000d4
++#define VEC_DAC_D4_BITS 0xffffffff
++#define VEC_DAC_D4_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_D4_NCO_FREQ
++// Description : This 64-bit frequency command is applied to the phase
++// accumulator of the NCO (numerically controlled oscillator)
++// which generates the colour sub-carrier. This value is computed
++// as ratio of sub-carrier frequency to DAC clock multiplied by
++// 2^64.
++#define VEC_DAC_D4_NCO_FREQ_RESET 0x00000000
++#define VEC_DAC_D4_NCO_FREQ_BITS 0xffffffff
++#define VEC_DAC_D4_NCO_FREQ_MSB 31
++#define VEC_DAC_D4_NCO_FREQ_LSB 0
++#define VEC_DAC_D4_NCO_FREQ_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_D8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_D8_OFFSET 0x000000d8
++#define VEC_DAC_D8_BITS 0xffffffff
++#define VEC_DAC_D8_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_D8_NCO_FREQ
++// Description : This 64-bit frequency command is applied to the phase
++// accumulator of the NCO (numerically controlled oscillator)
++// which generates the colour sub-carrier. This value is computed
++// as ratio of sub-carrier frequency to DAC clock multiplied by
++// 2^64.
++#define VEC_DAC_D8_NCO_FREQ_RESET 0x00000000
++#define VEC_DAC_D8_NCO_FREQ_BITS 0xffffffff
++#define VEC_DAC_D8_NCO_FREQ_MSB 31
++#define VEC_DAC_D8_NCO_FREQ_LSB 0
++#define VEC_DAC_D8_NCO_FREQ_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_DC
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_DC_OFFSET 0x000000dc
++#define VEC_DAC_DC_BITS 0xffffffff
++#define VEC_DAC_DC_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_DC_FIR_COEFF_CHROMA_0_6
++// Description : FIR filter coefficients
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_RESET 0x0000
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_BITS 0xffff0000
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_MSB 31
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_LSB 16
++#define VEC_DAC_DC_FIR_COEFF_CHROMA_0_6_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_DC_FIR_COEFF_LUMA_0_6
++// Description : FIR filter coefficients
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_RESET 0x0000
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_BITS 0x0000ffff
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_MSB 15
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_LSB 0
++#define VEC_DAC_DC_FIR_COEFF_LUMA_0_6_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_E0
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_E0_OFFSET 0x000000e0
++#define VEC_DAC_E0_BITS 0xffffffff
++#define VEC_DAC_E0_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_E0_FIR_COEFF_CHROMA_1_5
++// Description : FIR filter coefficients
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_RESET 0x0000
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_BITS 0xffff0000
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_MSB 31
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_LSB 16
++#define VEC_DAC_E0_FIR_COEFF_CHROMA_1_5_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_E0_FIR_COEFF_LUMA_1_5
++// Description : FIR filter coefficients
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_RESET 0x0000
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_BITS 0x0000ffff
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_MSB 15
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_LSB 0
++#define VEC_DAC_E0_FIR_COEFF_LUMA_1_5_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_E4
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_E4_OFFSET 0x000000e4
++#define VEC_DAC_E4_BITS 0xffffffff
++#define VEC_DAC_E4_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_E4_FIR_COEFF_CHROMA_2_4
++// Description : FIR filter coefficients
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_RESET 0x0000
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_BITS 0xffff0000
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_MSB 31
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_LSB 16
++#define VEC_DAC_E4_FIR_COEFF_CHROMA_2_4_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_E4_FIR_COEFF_LUMA_2_4
++// Description : FIR filter coefficients
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_RESET 0x0000
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_BITS 0x0000ffff
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_MSB 15
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_LSB 0
++#define VEC_DAC_E4_FIR_COEFF_LUMA_2_4_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_E8
++// JTAG access : synchronous
++// Description : None
++#define VEC_DAC_E8_OFFSET 0x000000e8
++#define VEC_DAC_E8_BITS 0xffffffff
++#define VEC_DAC_E8_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_E8_FIR_COEFF_CHROMA_3
++// Description : FIR filter coefficients
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_RESET 0x0000
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_BITS 0xffff0000
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_MSB 31
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_LSB 16
++#define VEC_DAC_E8_FIR_COEFF_CHROMA_3_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_E8_FIR_COEFF_LUMA_3
++// Description : FIR filter coefficients
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_RESET 0x0000
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_BITS 0x0000ffff
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_MSB 15
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_LSB 0
++#define VEC_DAC_E8_FIR_COEFF_LUMA_3_ACCESS "RW"
++// =============================================================================
++// Register : VEC_DAC_EC
++// JTAG access : synchronous
++// Description : Misc. control
++#define VEC_DAC_EC_OFFSET 0x000000ec
++#define VEC_DAC_EC_BITS 0x001fffff
++#define VEC_DAC_EC_RESET 0x00000000
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_SLOW_CLOCK
++// Description : Doubles the raised-cosine rate
++#define VEC_DAC_EC_SLOW_CLOCK_RESET 0x0
++#define VEC_DAC_EC_SLOW_CLOCK_BITS 0x00100000
++#define VEC_DAC_EC_SLOW_CLOCK_MSB 20
++#define VEC_DAC_EC_SLOW_CLOCK_LSB 20
++#define VEC_DAC_EC_SLOW_CLOCK_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_FIR_RMINUS1
++// Description : Select 1, 3, 5 or 7 FIR taps
++#define VEC_DAC_EC_FIR_RMINUS1_RESET 0x0
++#define VEC_DAC_EC_FIR_RMINUS1_BITS 0x000c0000
++#define VEC_DAC_EC_FIR_RMINUS1_MSB 19
++#define VEC_DAC_EC_FIR_RMINUS1_LSB 18
++#define VEC_DAC_EC_FIR_RMINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_VERT_FULL_NOT_HALF
++// Description : Disable half-line pulses during VBI
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_RESET 0x0
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_BITS 0x00020000
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_MSB 17
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_LSB 17
++#define VEC_DAC_EC_VERT_FULL_NOT_HALF_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_SEQ_EN
++// Description : Enable NCO reset
++#define VEC_DAC_EC_SEQ_EN_RESET 0x0
++#define VEC_DAC_EC_SEQ_EN_BITS 0x00010000
++#define VEC_DAC_EC_SEQ_EN_MSB 16
++#define VEC_DAC_EC_SEQ_EN_LSB 16
++#define VEC_DAC_EC_SEQ_EN_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_U2_FLD_MASK
++// Description : Field sequence
++#define VEC_DAC_EC_U2_FLD_MASK_RESET 0x0
++#define VEC_DAC_EC_U2_FLD_MASK_BITS 0x0000c000
++#define VEC_DAC_EC_U2_FLD_MASK_MSB 15
++#define VEC_DAC_EC_U2_FLD_MASK_LSB 14
++#define VEC_DAC_EC_U2_FLD_MASK_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_U4_SEQ_MASK
++// Description : NCO reset sequence
++#define VEC_DAC_EC_U4_SEQ_MASK_RESET 0x0
++#define VEC_DAC_EC_U4_SEQ_MASK_BITS 0x00003c00
++#define VEC_DAC_EC_U4_SEQ_MASK_MSB 13
++#define VEC_DAC_EC_U4_SEQ_MASK_LSB 10
++#define VEC_DAC_EC_U4_SEQ_MASK_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_INTERP_RATE_MINUS1
++// Description : Interpolation rate 2<=R<=16
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_RESET 0x0
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_BITS 0x000003c0
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_MSB 9
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_LSB 6
++#define VEC_DAC_EC_INTERP_RATE_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_INTERP_SHIFT_MINUS1
++// Description : Power-of-2 scaling after interpolation
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_RESET 0x0
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_BITS 0x0000003c
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_MSB 5
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_LSB 2
++#define VEC_DAC_EC_INTERP_SHIFT_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1
++// Description : Interlaced / progressive
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_RESET 0x0
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_BITS 0x00000002
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_MSB 1
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_LSB 1
++#define VEC_DAC_EC_FIELDS_PER_FRAME_MINUS1_ACCESS "RW"
++// -----------------------------------------------------------------------------
++// Field : VEC_DAC_EC_PAL_EN
++// Description : Enable phase alternate line (PAL) mode
++#define VEC_DAC_EC_PAL_EN_RESET 0x0
++#define VEC_DAC_EC_PAL_EN_BITS 0x00000001
++#define VEC_DAC_EC_PAL_EN_MSB 0
++#define VEC_DAC_EC_PAL_EN_LSB 0
++#define VEC_DAC_EC_PAL_EN_ACCESS "RW"
++// =============================================================================
++#endif // VEC_REGS_DEFINED
--- /dev/null
+From 3a419974ba02d32795a5ecfaf3c020f23173b6a1 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 14 Feb 2023 20:58:59 +0000
+Subject: [PATCH] v4l2: Add pisp compression format support to v4l2
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 12 ++++++++----
+ include/uapi/linux/media-bus-format.h | 14 ++++++++++++++
+ include/uapi/linux/videodev2.h | 12 ++++++++----
+ 3 files changed, 30 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1507,10 +1507,14 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_QC08C: descr = "QCOM Compressed 8-bit Format"; break;
+ case V4L2_PIX_FMT_QC10C: descr = "QCOM Compressed 10-bit Format"; break;
+ case V4L2_PIX_FMT_RPI_BE: descr = "PiSP Opaque Format"; break;
+- case V4L2_PIX_FMT_PISP_COMP_RGGB:
+- case V4L2_PIX_FMT_PISP_COMP_GRBG:
+- case V4L2_PIX_FMT_PISP_COMP_GBRG:
+- case V4L2_PIX_FMT_PISP_COMP_BGGR: descr = "PiSP Bayer Compressed Format"; break;
++ case V4L2_PIX_FMT_PISP_COMP1_RGGB:
++ case V4L2_PIX_FMT_PISP_COMP1_GRBG:
++ case V4L2_PIX_FMT_PISP_COMP1_GBRG:
++ case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP Bayer Comp 1"; break;
++ case V4L2_PIX_FMT_PISP_COMP2_RGGB:
++ case V4L2_PIX_FMT_PISP_COMP2_GRBG:
++ case V4L2_PIX_FMT_PISP_COMP2_GBRG:
++ case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP Bayer Comp 2"; break;
+ default:
+ if (fmt->description[0])
+ return;
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -175,4 +175,18 @@
+ /* Sensor ancillary metadata formats - next is 0x7002 */
+ #define MEDIA_BUS_FMT_SENSOR_DATA 0x7002
+
++/* PiSP Formats */
++#define MEDIA_BUS_FMT_PISP_COMP1_RGGB 0x8001
++#define MEDIA_BUS_FMT_PISP_COMP1_GRBG 0x8002
++#define MEDIA_BUS_FMT_PISP_COMP1_GBRG 0x8003
++#define MEDIA_BUS_FMT_PISP_COMP1_BGGR 0x8004
++#define MEDIA_BUS_FMT_PISP_COMP2_RGGB 0x8005
++#define MEDIA_BUS_FMT_PISP_COMP2_GRBG 0x8006
++#define MEDIA_BUS_FMT_PISP_COMP2_GBRG 0x8007
++#define MEDIA_BUS_FMT_PISP_COMP2_BGGR 0x8008
++
++#define MEDIA_BUS_FMT_PISP_FE_CONFIG 0x8100
++#define MEDIA_BUS_FMT_PISP_FE_STATS 0x8101
++#define MEDIA_BUS_FMT_PISP_BE_CONFIG 0x8200
++
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -795,10 +795,14 @@ struct v4l2_pix_format {
+
+ /* The pixel format for all our buffers (the precise format is found in the config buffer). */
+ #define V4L2_PIX_FMT_RPI_BE v4l2_fourcc('R', 'P', 'B', 'P')
+-#define V4L2_PIX_FMT_PISP_COMP_RGGB v4l2_fourcc('P', 'C', 'R', 'G')
+-#define V4L2_PIX_FMT_PISP_COMP_GRBG v4l2_fourcc('P', 'C', 'G', 'R')
+-#define V4L2_PIX_FMT_PISP_COMP_GBRG v4l2_fourcc('P', 'C', 'G', 'B')
+-#define V4L2_PIX_FMT_PISP_COMP_BGGR v4l2_fourcc('P', 'C', 'B', 'G')
++#define V4L2_PIX_FMT_PISP_COMP1_RGGB v4l2_fourcc('P', 'C', '1', 'R')
++#define V4L2_PIX_FMT_PISP_COMP1_GRBG v4l2_fourcc('P', 'C', '1', 'G')
++#define V4L2_PIX_FMT_PISP_COMP1_GBRG v4l2_fourcc('P', 'C', '1', 'g')
++#define V4L2_PIX_FMT_PISP_COMP1_BGGR v4l2_fourcc('P', 'C', '1', 'B')
++#define V4L2_PIX_FMT_PISP_COMP2_RGGB v4l2_fourcc('P', 'C', '2', 'R')
++#define V4L2_PIX_FMT_PISP_COMP2_GRBG v4l2_fourcc('P', 'C', '2', 'G')
++#define V4L2_PIX_FMT_PISP_COMP2_GBRG v4l2_fourcc('P', 'C', '2', 'g')
++#define V4L2_PIX_FMT_PISP_COMP2_BGGR v4l2_fourcc('P', 'C', '2', 'B')
+
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
--- /dev/null
+From 8a31623de7f034f6521b348e9a510e78a6e7e493 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 14 Feb 2023 17:30:12 +0000
+Subject: [PATCH] media: rp1: Add CFE (Camera Front End) support
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/Kconfig | 1 +
+ drivers/media/platform/raspberrypi/Makefile | 1 +
+ .../platform/raspberrypi/rp1_cfe/Kconfig | 14 +
+ .../platform/raspberrypi/rp1_cfe/Makefile | 6 +
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 2186 +++++++++++++++++
+ .../media/platform/raspberrypi/rp1_cfe/cfe.h | 40 +
+ .../platform/raspberrypi/rp1_cfe/cfe_fmts.h | 294 +++
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 446 ++++
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h | 75 +
+ .../media/platform/raspberrypi/rp1_cfe/dphy.c | 177 ++
+ .../media/platform/raspberrypi/rp1_cfe/dphy.h | 26 +
+ .../raspberrypi/rp1_cfe/pisp_common.h | 69 +
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 563 +++++
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.h | 53 +
+ .../raspberrypi/rp1_cfe/pisp_fe_config.h | 272 ++
+ .../raspberrypi/rp1_cfe/pisp_statistics.h | 62 +
+ .../platform/raspberrypi/rp1_cfe/pisp_types.h | 144 ++
+ 17 files changed, 4429 insertions(+)
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/Kconfig
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/Makefile
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
+ create mode 100644 drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
+
+--- a/drivers/media/platform/raspberrypi/Kconfig
++++ b/drivers/media/platform/raspberrypi/Kconfig
+@@ -3,3 +3,4 @@
+ comment "Raspberry Pi media platform drivers"
+
+ source "drivers/media/platform/raspberrypi/pisp_be/Kconfig"
++source "drivers/media/platform/raspberrypi/rp1_cfe/Kconfig"
+--- a/drivers/media/platform/raspberrypi/Makefile
++++ b/drivers/media/platform/raspberrypi/Makefile
+@@ -1,3 +1,4 @@
+ # SPDX-License-Identifier: GPL-2.0
+
+ obj-y += pisp_be/
++obj-y += rp1_cfe/
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/Kconfig
+@@ -0,0 +1,14 @@
++# RP1 V4L2 camera support
++
++config VIDEO_RP1_CFE
++ tristate "RP1 Camera Frond End (CFE) video capture driver"
++ depends on VIDEO_DEV
++ select VIDEO_V4L2_SUBDEV_API
++ select MEDIA_CONTROLLER
++ select VIDEOBUF2_DMA_CONTIG
++ select V4L2_FWNODE
++ help
++ Say Y here to enable support for the RP1 Camera Front End.
++
++ To compile this driver as a module, choose M here. The module will be
++ called rp1-cfe.
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/Makefile
+@@ -0,0 +1,6 @@
++# SPDX-License-Identifier: GPL-2.0
++#
++# Makefile for RP1 Camera Front End driver
++#
++rp1-cfe-objs := cfe.o csi2.o pisp_fe.o dphy.o
++obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -0,0 +1,2186 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RP1 Camera Front End Driver
++ *
++ * Copyright (C) 2021-2022 - Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/atomic.h>
++#include <linux/clk.h>
++#include <linux/debugfs.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/of_device.h>
++#include <linux/of_graph.h>
++#include <linux/phy/phy.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/videodev2.h>
++
++#include <media/v4l2-async.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-dv-timings.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-ioctl.h>
++#include <media/videobuf2-dma-contig.h>
++
++#include "cfe.h"
++#include "cfe_fmts.h"
++#include "csi2.h"
++#include "pisp_fe.h"
++#include "pisp_fe_config.h"
++#include "pisp_statistics.h"
++
++#define CFE_MODULE_NAME "rp1-cfe"
++#define CFE_VERSION "1.0"
++
++bool cfe_debug_irq;
++
++#define cfe_dbg_irq(fmt, arg...) \
++ do { \
++ if (cfe_debug_irq) \
++ dev_dbg(&cfe->pdev->dev, fmt, ##arg); \
++ } while (0)
++#define cfe_dbg(fmt, arg...) dev_dbg(&cfe->pdev->dev, fmt, ##arg)
++#define cfe_info(fmt, arg...) dev_info(&cfe->pdev->dev, fmt, ##arg)
++#define cfe_err(fmt, arg...) dev_err(&cfe->pdev->dev, fmt, ##arg)
++
++/* MIPICFG registers */
++#define MIPICFG_CFG 0x004
++#define MIPICFG_INTR 0x028
++#define MIPICFG_INTE 0x02c
++#define MIPICFG_INTF 0x030
++#define MIPICFG_INTS 0x034
++
++#define MIPICFG_CFG_SEL_CSI BIT(0)
++
++#define MIPICFG_INT_CSI_DMA BIT(0)
++#define MIPICFG_INT_CSI_HOST BIT(2)
++#define MIPICFG_INT_PISP_FE BIT(4)
++
++#define BPL_ALIGNMENT 16
++#define MAX_BYTESPERLINE 0xffffff00
++#define MAX_BUFFER_SIZE 0xffffff00
++/*
++ * Max width is therefore determined by the max stride divided by the number of
++ * bits per pixel.
++ *
++ * However, to avoid overflow issues let's use a 16k maximum. This lets us
++ * calculate 16k * 16k * 4 with 32bits. If we need higher maximums, a careful
++ * review and adjustment of the code is needed so that it will deal with
++ * overflows correctly.
++ */
++#define MAX_WIDTH 16384
++#define MAX_HEIGHT MAX_WIDTH
++/* Define a nominal minimum image size */
++#define MIN_WIDTH 16
++#define MIN_HEIGHT 16
++/* Default size of the embedded buffer */
++#define DEFAULT_EMBEDDED_SIZE 8192
++
++const struct v4l2_mbus_framefmt cfe_default_format = {
++ .width = 640,
++ .height = 480,
++ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
++ .field = V4L2_FIELD_NONE,
++ .colorspace = V4L2_COLORSPACE_RAW,
++ .ycbcr_enc = V4L2_YCBCR_ENC_601,
++ .quantization = V4L2_QUANTIZATION_FULL_RANGE,
++ .xfer_func = V4L2_XFER_FUNC_NONE,
++};
++
++const struct v4l2_mbus_framefmt cfe_default_meta_format = {
++ .width = 8192,
++ .height = 1,
++ .code = MEDIA_BUS_FMT_SENSOR_DATA,
++};
++
++enum node_ids {
++ /* CSI2 HW output nodes first. */
++ CSI2_CH0,
++ CSI2_CH1_EMBEDDED,
++ CSI2_CH2,
++ CSI2_CH3,
++ /* FE only nodes from here on. */
++ FE_OUT0,
++ FE_OUT1,
++ FE_STATS,
++ FE_CONFIG,
++ NUM_NODES
++};
++
++struct node_description {
++ unsigned int id;
++ const char *name;
++ enum v4l2_buf_type buf_type;
++ unsigned int cap;
++ unsigned int pad_flags;
++ unsigned int link_pad;
++};
++
++/* Must match the ordering of enum ids */
++static const struct node_description node_desc[NUM_NODES] = {
++ [CSI2_CH0] = {
++ .name = "csi2_ch0",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ .cap = V4L2_CAP_VIDEO_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = CSI2_NUM_CHANNELS + 0
++ },
++ /* This node is assigned for the embedded data channel! */
++ [CSI2_CH1_EMBEDDED] = {
++ .name = "embedded",
++ .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
++ .cap = V4L2_CAP_META_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = CSI2_NUM_CHANNELS + 1
++ },
++ [CSI2_CH2] = {
++ .name = "csi2_ch2",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ .cap = V4L2_CAP_META_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = CSI2_NUM_CHANNELS + 2
++ },
++ [CSI2_CH3] = {
++ .name = "csi2_ch3",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ .cap = V4L2_CAP_META_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = CSI2_NUM_CHANNELS + 3
++ },
++ [FE_OUT0] = {
++ .name = "fe_image0",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ .cap = V4L2_CAP_VIDEO_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = FE_OUTPUT0_PAD
++ },
++ [FE_OUT1] = {
++ .name = "fe_image1",
++ .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ .cap = V4L2_CAP_VIDEO_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = FE_OUTPUT1_PAD
++ },
++ [FE_STATS] = {
++ .name = "fe_stats",
++ .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
++ .cap = V4L2_CAP_META_CAPTURE,
++ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = FE_STATS_PAD
++ },
++ [FE_CONFIG] = {
++ .name = "fe_config",
++ .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
++ .cap = V4L2_CAP_META_OUTPUT,
++ .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT,
++ .link_pad = FE_CONFIG_PAD
++ },
++};
++
++#define is_fe_node(node) (((node)->id) >= FE_OUT0)
++#define is_csi2_node(node) (!is_fe_node(node))
++#define is_image_output_node(node) \
++ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++#define is_meta_output_node(node) \
++ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_CAPTURE)
++#define is_meta_input_node(node) \
++ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_OUTPUT)
++#define is_meta_node(node) (is_meta_output_node(node) || is_meta_input_node(node))
++
++/* To track state across all nodes. */
++#define NUM_STATES 5
++#define NODE_REGISTERED BIT(0)
++#define NODE_ENABLED BIT(1)
++#define NODE_STREAMING BIT(2)
++#define FS_INT BIT(3)
++#define FE_INT BIT(4)
++
++struct cfe_buffer {
++ struct vb2_v4l2_buffer vb;
++ struct list_head list;
++};
++
++struct cfe_config_buffer {
++ struct cfe_buffer buf;
++ struct pisp_fe_config config;
++};
++
++static inline struct cfe_buffer *to_cfe_buffer(struct vb2_buffer *vb)
++{
++ return container_of(vb, struct cfe_buffer, vb.vb2_buf);
++}
++
++static inline
++struct cfe_config_buffer *to_cfe_config_buffer(struct cfe_buffer *buf)
++{
++ return container_of(buf, struct cfe_config_buffer, buf);
++}
++
++struct cfe_node {
++ unsigned int id;
++ /* Pointer pointing to current v4l2_buffer */
++ struct cfe_buffer *cur_frm;
++ /* Pointer pointing to next v4l2_buffer */
++ struct cfe_buffer *next_frm;
++ /* Used to store current pixel format */
++ struct v4l2_format fmt;
++ /* Buffer queue used in video-buf */
++ struct vb2_queue buffer_queue;
++ /* Queue of filled frames */
++ struct list_head dma_queue;
++ /* lock used to access this structure */
++ struct mutex lock;
++ /* Identifies video device for this channel */
++ struct video_device video_dev;
++ /* Pointer to the parent handle */
++ struct cfe_device *cfe;
++ struct media_pad pad;
++};
++
++struct cfe_device {
++ struct dentry *debugfs;
++ struct kref kref;
++
++ /* V4l2 specific parameters */
++ struct v4l2_async_subdev asd;
++
++ /* peripheral base address */
++ void __iomem *mipi_cfg_base;
++
++ struct clk *clk;
++
++ /* V4l2 device */
++ struct v4l2_device v4l2_dev;
++ struct media_device mdev;
++ struct media_pipeline pipe;
++
++ /* IRQ lock for node state and DMA queues */
++ spinlock_t state_lock;
++ bool job_ready;
++ bool job_queued;
++
++ /* parent device */
++ struct platform_device *pdev;
++ /* subdevice async Notifier */
++ struct v4l2_async_notifier notifier;
++
++ /* ptr to sub device */
++ struct v4l2_subdev *sensor;
++
++ struct cfe_node node[NUM_NODES];
++ DECLARE_BITMAP(node_flags, NUM_STATES * NUM_NODES);
++
++ struct csi2_device csi2;
++ struct pisp_fe_device fe;
++
++ bool sensor_embedded_data;
++ int fe_csi2_channel;
++
++ unsigned int sequence;
++ u64 ts;
++};
++
++static inline bool is_fe_enabled(struct cfe_device *cfe)
++{
++ return cfe->fe_csi2_channel != -1;
++}
++
++static inline struct cfe_device *to_cfe_device(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct cfe_device, v4l2_dev);
++}
++
++static inline u32 cfg_reg_read(struct cfe_device *cfe, u32 offset)
++{
++ return readl(cfe->mipi_cfg_base + offset);
++}
++
++static inline void cfg_reg_write(struct cfe_device *cfe, u32 offset, u32 val)
++{
++ writel(val, cfe->mipi_cfg_base + offset);
++}
++
++static bool check_state(struct cfe_device *cfe, unsigned long state,
++ unsigned int node_id)
++{
++ unsigned long bit;
++
++ for_each_set_bit(bit, &state, sizeof(state)) {
++ if (!test_bit(bit + (node_id * NUM_STATES), cfe->node_flags))
++ return false;
++ }
++ return true;
++}
++
++static void set_state(struct cfe_device *cfe, unsigned long state,
++ unsigned int node_id)
++{
++ unsigned long bit;
++
++ for_each_set_bit(bit, &state, sizeof(state))
++ set_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
++}
++
++static void clear_state(struct cfe_device *cfe, unsigned long state,
++ unsigned int node_id)
++{
++ unsigned long bit;
++
++ for_each_set_bit(bit, &state, sizeof(state))
++ clear_bit(bit + (node_id * NUM_STATES), cfe->node_flags);
++}
++
++static bool test_any_node(struct cfe_device *cfe, unsigned long cond)
++{
++ unsigned int i;
++
++ for (i = 0; i < NUM_NODES; i++) {
++ if (check_state(cfe, cond, i))
++ return true;
++ }
++
++ return false;
++}
++
++static bool test_all_nodes(struct cfe_device *cfe, unsigned long precond,
++ unsigned long cond)
++{
++ unsigned int i;
++
++ for (i = 0; i < NUM_NODES; i++) {
++ if (check_state(cfe, precond, i)) {
++ if (!check_state(cfe, cond, i))
++ return false;
++ }
++ }
++
++ return true;
++}
++
++static void clear_all_nodes(struct cfe_device *cfe, unsigned long precond,
++ unsigned long state)
++{
++ unsigned int i;
++
++ for (i = 0; i < NUM_NODES; i++) {
++ if (check_state(cfe, precond, i))
++ clear_state(cfe, state, i);
++ }
++}
++
++static int mipi_cfg_regs_show(struct seq_file *s, void *data)
++{
++ struct cfe_device *cfe = s->private;
++ int ret;
++
++ ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
++ if (ret)
++ return ret;
++
++#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", cfg_reg_read(cfe, reg))
++ DUMP(MIPICFG_CFG);
++ DUMP(MIPICFG_INTR);
++ DUMP(MIPICFG_INTE);
++ DUMP(MIPICFG_INTF);
++ DUMP(MIPICFG_INTS);
++#undef DUMP
++
++ pm_runtime_put(&cfe->pdev->dev);
++
++ return 0;
++}
++
++static int format_show(struct seq_file *s, void *data)
++{
++ struct cfe_device *cfe = s->private;
++ unsigned int i;
++
++ for (i = 0; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++ unsigned long sb, state = 0;
++
++ for (sb = 0; sb < NUM_STATES; sb++) {
++ if (check_state(cfe, BIT(sb), i))
++ state |= BIT(sb);
++ }
++
++ seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i,
++ node_desc[i].name, state);
++
++ if (is_image_output_node(node))
++ seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\n"
++ "resolution: %ux%u\nbpl: %u\nsize: %u\n",
++ V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat),
++ node->fmt.fmt.pix.pixelformat,
++ node->fmt.fmt.pix.width,
++ node->fmt.fmt.pix.height,
++ node->fmt.fmt.pix.bytesperline,
++ node->fmt.fmt.pix.sizeimage);
++ else
++ seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\nsize: %u\n",
++ V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat),
++ node->fmt.fmt.meta.dataformat,
++ node->fmt.fmt.meta.buffersize);
++ }
++
++ return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(mipi_cfg_regs);
++DEFINE_SHOW_ATTRIBUTE(format);
++
++/* Format setup functions */
++const struct cfe_fmt *find_format_by_code(u32 code)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(formats); i++) {
++ if (formats[i].code == code)
++ return &formats[i];
++ }
++
++ return NULL;
++}
++
++static const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(formats); i++) {
++ if (formats[i].fourcc == pixelformat)
++ return &formats[i];
++ }
++
++ return NULL;
++}
++
++static int cfe_calc_format_size_bpl(struct cfe_device *cfe,
++ const struct cfe_fmt *fmt,
++ struct v4l2_format *f)
++{
++ unsigned int min_bytesperline;
++
++ v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2,
++ &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, 0);
++
++ min_bytesperline =
++ ALIGN((f->fmt.pix.width * fmt->depth) >> 3, BPL_ALIGNMENT);
++
++ if (f->fmt.pix.bytesperline > min_bytesperline &&
++ f->fmt.pix.bytesperline <= MAX_BYTESPERLINE)
++ f->fmt.pix.bytesperline =
++ ALIGN(f->fmt.pix.bytesperline, BPL_ALIGNMENT);
++ else
++ f->fmt.pix.bytesperline = min_bytesperline;
++
++ f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
++
++ cfe_dbg("%s: " V4L2_FOURCC_CONV " size: %ux%u bpl:%u img_size:%u\n",
++ __func__, V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat),
++ f->fmt.pix.width, f->fmt.pix.height,
++ f->fmt.pix.bytesperline, f->fmt.pix.sizeimage);
++
++ return 0;
++}
++
++static void cfe_schedule_next_csi2_job(struct cfe_device *cfe)
++{
++ struct cfe_buffer *buf;
++ unsigned int i;
++ dma_addr_t addr;
++
++ for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
++ struct cfe_node *node = &cfe->node[i];
++ unsigned int stride, size;
++
++ if (!check_state(cfe, NODE_STREAMING, i))
++ continue;
++
++ buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
++ list);
++ node->next_frm = buf;
++ list_del(&buf->list);
++
++ cfe_dbg("%s: [%s] buffer:%p\n",
++ __func__, node_desc[node->id].name, &buf->vb.vb2_buf);
++
++ if (is_meta_node(node)) {
++ size = node->fmt.fmt.meta.buffersize;
++ stride = 0;
++ } else {
++ size = node->fmt.fmt.pix.sizeimage;
++ stride = node->fmt.fmt.pix.bytesperline;
++ }
++
++ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
++ csi2_set_buffer(&cfe->csi2, node->id, addr, stride, size);
++ }
++}
++
++static void cfe_schedule_next_pisp_job(struct cfe_device *cfe)
++{
++ struct vb2_buffer *vb2_bufs[FE_NUM_PADS] = { 0 };
++ struct cfe_config_buffer *config_buf;
++ struct cfe_buffer *buf;
++ unsigned int i;
++
++ for (i = CSI2_NUM_CHANNELS; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++
++ if (!check_state(cfe, NODE_STREAMING, i))
++ continue;
++
++ buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
++ list);
++
++ cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, &buf->vb.vb2_buf);
++
++ node->next_frm = buf;
++ vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf;
++ list_del(&buf->list);
++ }
++
++ config_buf = to_cfe_config_buffer(cfe->node[FE_CONFIG].next_frm);
++ pisp_fe_submit_job(&cfe->fe, vb2_bufs, &config_buf->config);
++}
++
++static bool cfe_check_job_ready(struct cfe_device *cfe)
++{
++ unsigned int i;
++
++ for (i = 0; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++
++ if (!check_state(cfe, NODE_ENABLED, i))
++ continue;
++
++ if (list_empty(&node->dma_queue)) {
++ cfe_dbg_irq("%s: [%s] has no buffer, unable to schedule job\n",
++ __func__, node_desc[i].name);
++ return false;
++ }
++ }
++
++ return true;
++}
++
++static void cfe_prepare_next_job(struct cfe_device *cfe)
++{
++ cfe->job_queued = true;
++ cfe_schedule_next_csi2_job(cfe);
++ if (is_fe_enabled(cfe))
++ cfe_schedule_next_pisp_job(cfe);
++
++ /* Flag if another job is ready after this. */
++ cfe->job_ready = cfe_check_job_ready(cfe);
++
++ cfe_dbg_irq("%s: end with scheduled job\n", __func__);
++}
++
++static void cfe_process_buffer_complete(struct cfe_node *node,
++ unsigned int sequence)
++{
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
++ &node->cur_frm->vb.vb2_buf);
++
++ node->cur_frm->vb.sequence = sequence;
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
++}
++
++static void cfe_queue_event_sof(struct cfe_node *node)
++{
++ struct v4l2_event event = {
++ .type = V4L2_EVENT_FRAME_SYNC,
++ .u.frame_sync.frame_sequence = node->cfe->sequence,
++ };
++
++ v4l2_event_queue(&node->video_dev, &event);
++}
++
++static void cfe_sof_isr_handler(struct cfe_node *node)
++{
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++ cfe->sequence);
++
++ node->cur_frm = node->next_frm;
++ node->next_frm = NULL;
++
++ /*
++ * If this is the first node to see a frame start, sample the
++ * timestamp to use for all frames across all channels.
++ */
++ if (!test_any_node(cfe, NODE_STREAMING | FS_INT))
++ cfe->ts = ktime_get_ns();
++
++ set_state(cfe, FS_INT, node->id);
++
++ /* If all nodes have seen a frame start, we can queue another job. */
++ if (test_all_nodes(cfe, NODE_STREAMING, FS_INT))
++ cfe->job_queued = false;
++
++ if (node->cur_frm)
++ node->cur_frm->vb.vb2_buf.timestamp = cfe->ts;
++
++ if (is_image_output_node(node))
++ cfe_queue_event_sof(node);
++}
++
++static void cfe_eof_isr_handler(struct cfe_node *node)
++{
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++ cfe->sequence);
++
++ if (node->cur_frm)
++ cfe_process_buffer_complete(node, cfe->sequence);
++
++ node->cur_frm = NULL;
++ set_state(cfe, FE_INT, node->id);
++
++ /*
++ * If all nodes have seen a frame end, we can increment
++ * the sequence counter now.
++ */
++ if (test_all_nodes(cfe, NODE_STREAMING, FE_INT)) {
++ cfe->sequence++;
++ clear_all_nodes(cfe, NODE_STREAMING, FE_INT | FS_INT);
++ }
++}
++
++static irqreturn_t cfe_isr(int irq, void *dev)
++{
++ struct cfe_device *cfe = dev;
++ unsigned int i;
++ bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0}, lci[NUM_NODES] = {0};
++ u32 sts;
++
++ sts = cfg_reg_read(cfe, MIPICFG_INTS);
++
++ if (sts & MIPICFG_INT_CSI_DMA)
++ csi2_isr(&cfe->csi2, sof, eof, lci);
++
++ if (sts & MIPICFG_INT_PISP_FE)
++ pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
++ eof + CSI2_NUM_CHANNELS);
++
++ spin_lock(&cfe->state_lock);
++
++ for (i = 0; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++
++ /*
++ * The check_state(NODE_STREAMING) is to ensure we do not loop
++ * over the CSI2_CHx nodes when the FE is active since they
++ * generate interrupts even though the node is not streaming.
++ */
++ if (!check_state(cfe, NODE_STREAMING, i) ||
++ !(sof[i] || eof[i] || lci[i]))
++ continue;
++
++ /*
++ * There are 3 cases where we could get FS + FE_ACK at
++ * the same time:
++ * 1) FE of the current frame, and FS of the next frame.
++ * 2) FS + FE of the same frame.
++ * 3) FE of the current frame, and FS + FE of the next
++ * frame. To handle this, see the sof handler below.
++ *
++ * (1) is handled implicitly by the ordering of the FE and FS
++ * handlers below.
++ */
++ if (eof[i]) {
++ /*
++ * The condition below tests for (2). Run the FS handler
++ * first before the FE handler, both for the current
++ * frame.
++ */
++ if (sof[i] && !check_state(cfe, FS_INT, i)) {
++ cfe_sof_isr_handler(node);
++ sof[i] = false;
++ }
++
++ cfe_eof_isr_handler(node);
++ }
++
++ if (sof[i]) {
++ /*
++ * The condition below tests for (3). In such cases, we
++ * come in here with FS flag set in the node state from
++ * the previous frame since it only gets cleared in
++ * eof_isr_handler(). Handle the FE for the previous
++ * frame first before the FS handler for the current
++ * frame.
++ */
++ if (check_state(cfe, FS_INT, node->id)) {
++ cfe_dbg("%s: [%s] Handling missing previous FE interrupt\n",
++ __func__, node_desc[node->id].name);
++ cfe_eof_isr_handler(node);
++ }
++
++ cfe_sof_isr_handler(node);
++ }
++
++ if (!cfe->job_queued && cfe->job_ready)
++ cfe_prepare_next_job(cfe);
++ }
++
++ spin_unlock(&cfe->state_lock);
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * Stream helpers
++ */
++
++static void cfe_start_channel(struct cfe_node *node)
++{
++ struct cfe_device *cfe = node->cfe;
++ struct v4l2_subdev_state *state;
++ struct v4l2_mbus_framefmt *source_fmt;
++ const struct cfe_fmt *fmt;
++ unsigned long flags;
++ unsigned int width = 0, height = 0;
++ bool start_fe = is_fe_enabled(cfe) &&
++ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (start_fe || is_image_output_node(node)) {
++ width = node->fmt.fmt.pix.width;
++ height = node->fmt.fmt.pix.height;
++ }
++
++ state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
++
++ if (start_fe) {
++ WARN_ON(!is_fe_enabled(cfe));
++ cfe_dbg("%s: %s using csi2 channel %d\n",
++ __func__, node_desc[FE_OUT0].name,
++ cfe->fe_csi2_channel);
++
++ source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, cfe->fe_csi2_channel);
++ fmt = find_format_by_code(source_fmt->code);
++
++ /*
++ * Start the associated CSI2 Channel as well.
++ *
++ * Must write to the ADDR register to latch the ctrl values
++ * even if we are connected to the front end. Once running,
++ * this is handled by the CSI2 AUTO_ARM mode.
++ */
++ csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel,
++ fmt->csi_dt, CSI2_MODE_FE_STREAMING,
++ true, false, width, height);
++ csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1);
++ pisp_fe_start(&cfe->fe);
++ }
++
++ if (is_csi2_node(node)) {
++ u32 mode = CSI2_MODE_NORMAL;
++
++ source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state,
++ node_desc[node->id].link_pad - CSI2_NUM_CHANNELS);
++ fmt = find_format_by_code(source_fmt->code);
++
++ if (is_image_output_node(node)) {
++ if (node->fmt.fmt.pix.pixelformat ==
++ fmt->remap[CFE_REMAP_16BIT])
++ mode = CSI2_MODE_REMAP;
++ else if (node->fmt.fmt.pix.pixelformat ==
++ fmt->remap[CFE_REMAP_COMPRESSED]) {
++ mode = CSI2_MODE_COMPRESSED;
++ csi2_set_compression(&cfe->csi2, node->id,
++ CSI2_COMPRESSION_DELTA, 0,
++ 0);
++ }
++ }
++ /* Unconditionally start this CSI2 channel. */
++ csi2_start_channel(&cfe->csi2, node->id, fmt->csi_dt,
++ mode,
++ /* Auto arm */
++ false,
++ /* Pack bytes */
++ node->id == CSI2_CH1_EMBEDDED ? true : false,
++ width, height);
++ }
++
++ v4l2_subdev_unlock_state(state);
++
++ spin_lock_irqsave(&cfe->state_lock, flags);
++ if (cfe->job_ready && test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING))
++ cfe_prepare_next_job(cfe);
++ spin_unlock_irqrestore(&cfe->state_lock, flags);
++}
++
++static void cfe_stop_channel(struct cfe_node *node, bool fe_stop)
++{
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg("%s: [%s] fe_stop %u\n", __func__,
++ node_desc[node->id].name, fe_stop);
++
++ if (fe_stop) {
++ csi2_stop_channel(&cfe->csi2, cfe->fe_csi2_channel);
++ pisp_fe_stop(&cfe->fe);
++ }
++
++ if (is_csi2_node(node))
++ csi2_stop_channel(&cfe->csi2, node->id);
++}
++
++static void cfe_return_buffers(struct cfe_node *node,
++ enum vb2_buffer_state state)
++{
++ struct cfe_device *cfe = node->cfe;
++ struct cfe_buffer *buf, *tmp;
++ unsigned long flags;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ spin_lock_irqsave(&cfe->state_lock, flags);
++ list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) {
++ list_del(&buf->list);
++ vb2_buffer_done(&buf->vb.vb2_buf, state);
++ }
++
++ if (node->cur_frm)
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
++ if (node->next_frm && node->cur_frm != node->next_frm)
++ vb2_buffer_done(&node->next_frm->vb.vb2_buf, state);
++
++ node->cur_frm = NULL;
++ node->next_frm = NULL;
++ spin_unlock_irqrestore(&cfe->state_lock, flags);
++}
++
++/*
++ * vb2 ops
++ */
++
++static int cfe_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers,
++ unsigned int *nplanes, unsigned int sizes[],
++ struct device *alloc_devs[])
++{
++ struct cfe_node *node = vb2_get_drv_priv(vq);
++ struct cfe_device *cfe = node->cfe;
++ unsigned int size = is_image_output_node(node) ?
++ node->fmt.fmt.pix.sizeimage :
++ node->fmt.fmt.meta.buffersize;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (vq->num_buffers + *nbuffers < 3)
++ *nbuffers = 3 - vq->num_buffers;
++
++ if (*nplanes) {
++ if (sizes[0] < size) {
++ cfe_err("sizes[0] %i < size %u\n", sizes[0], size);
++ return -EINVAL;
++ }
++ size = sizes[0];
++ }
++
++ *nplanes = 1;
++ sizes[0] = size;
++
++ return 0;
++}
++
++static int cfe_buffer_prepare(struct vb2_buffer *vb)
++{
++ struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
++ struct cfe_device *cfe = node->cfe;
++ struct cfe_buffer *buf = to_cfe_buffer(vb);
++ unsigned long size;
++
++ cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
++ vb);
++
++ size = is_image_output_node(node) ? node->fmt.fmt.pix.sizeimage :
++ node->fmt.fmt.meta.buffersize;
++ if (vb2_plane_size(vb, 0) < size) {
++ cfe_err("data will not fit into plane (%lu < %lu)\n",
++ vb2_plane_size(vb, 0), size);
++ return -EINVAL;
++ }
++
++ vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size);
++
++ if (node->id == FE_CONFIG) {
++ struct cfe_config_buffer *b = to_cfe_config_buffer(buf);
++ void *addr = vb2_plane_vaddr(vb, 0);
++
++ memcpy(&b->config, addr, sizeof(struct pisp_fe_config));
++ return pisp_fe_validate_config(&cfe->fe, &b->config,
++ &cfe->node[FE_OUT0].fmt,
++ &cfe->node[FE_OUT1].fmt);
++ }
++
++ return 0;
++}
++
++static void cfe_buffer_queue(struct vb2_buffer *vb)
++{
++ struct cfe_node *node = vb2_get_drv_priv(vb->vb2_queue);
++ struct cfe_device *cfe = node->cfe;
++ struct cfe_buffer *buf = to_cfe_buffer(vb);
++ unsigned long flags;
++
++ cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
++ vb);
++
++ spin_lock_irqsave(&cfe->state_lock, flags);
++
++ list_add_tail(&buf->list, &node->dma_queue);
++
++ if (!cfe->job_ready)
++ cfe->job_ready = cfe_check_job_ready(cfe);
++
++ if (!cfe->job_queued && cfe->job_ready &&
++ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
++ cfe_dbg("Preparing job immediately for channel %u\n",
++ node->id);
++ cfe_prepare_next_job(cfe);
++ }
++
++ spin_unlock_irqrestore(&cfe->state_lock, flags);
++}
++
++static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
++{
++ struct v4l2_mbus_config mbus_config = { 0 };
++ struct cfe_node *node = vb2_get_drv_priv(vq);
++ struct cfe_device *cfe = node->cfe;
++ int ret;
++
++ cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name);
++
++ if (!check_state(cfe, NODE_ENABLED, node->id)) {
++ cfe_err("%s node link is not enabled.\n",
++ node_desc[node->id].name);
++ return -EINVAL;
++ }
++
++ ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
++ if (ret < 0) {
++ cfe_err("pm_runtime_resume_and_get failed\n");
++ goto err_streaming;
++ }
++
++ ret = media_pipeline_start(&node->pad, &cfe->pipe);
++ if (ret < 0) {
++ cfe_err("Failed to start media pipeline: %d\n", ret);
++ goto err_pm_put;
++ }
++
++ clear_state(cfe, FS_INT | FE_INT, node->id);
++ set_state(cfe, NODE_STREAMING, node->id);
++ cfe_start_channel(node);
++
++ if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
++ cfe_dbg("Not all nodes are set to streaming yet!\n");
++ return 0;
++ }
++
++ cfg_reg_write(cfe, MIPICFG_CFG, MIPICFG_CFG_SEL_CSI);
++ cfg_reg_write(cfe, MIPICFG_INTE, MIPICFG_INT_CSI_DMA | MIPICFG_INT_PISP_FE);
++
++ cfe->csi2.active_data_lanes = cfe->csi2.dphy.num_lanes;
++ cfe_dbg("Running with %u data lanes\n", cfe->csi2.active_data_lanes);
++
++ ret = v4l2_subdev_call(cfe->sensor, pad, get_mbus_config, 0,
++ &mbus_config);
++ if (ret < 0 && ret != -ENOIOCTLCMD) {
++ cfe_err("g_mbus_config failed\n");
++ goto err_pm_put;
++ }
++
++ cfe->csi2.active_data_lanes = mbus_config.bus.mipi_csi2.num_data_lanes;
++ if (!cfe->csi2.active_data_lanes)
++ cfe->csi2.active_data_lanes = cfe->csi2.dphy.num_lanes;
++ if (cfe->csi2.active_data_lanes > cfe->csi2.dphy.num_lanes) {
++ cfe_err("Device has requested %u data lanes, which is >%u configured in DT\n",
++ cfe->csi2.active_data_lanes, cfe->csi2.dphy.num_lanes);
++ ret = -EINVAL;
++ goto err_disable_cfe;
++ }
++
++ cfe_dbg("Starting sensor streaming\n");
++
++ csi2_open_rx(&cfe->csi2);
++
++ cfe->sequence = 0;
++ ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);
++ if (ret < 0) {
++ cfe_err("stream on failed in subdev\n");
++ goto err_disable_cfe;
++ }
++
++ cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name);
++
++ return 0;
++
++err_disable_cfe:
++ csi2_close_rx(&cfe->csi2);
++ cfe_stop_channel(node, true);
++ media_pipeline_stop(&node->pad);
++err_pm_put:
++ pm_runtime_put(&cfe->pdev->dev);
++err_streaming:
++ cfe_return_buffers(node, VB2_BUF_STATE_QUEUED);
++ clear_state(cfe, NODE_STREAMING, node->id);
++
++ return ret;
++}
++
++static void cfe_stop_streaming(struct vb2_queue *vq)
++{
++ struct cfe_node *node = vb2_get_drv_priv(vq);
++ struct cfe_device *cfe = node->cfe;
++ unsigned long flags;
++ bool fe_stop;
++
++ cfe_dbg("%s: [%s] begin.\n", __func__, node_desc[node->id].name);
++
++ spin_lock_irqsave(&cfe->state_lock, flags);
++ fe_stop = is_fe_enabled(cfe) &&
++ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
++
++ cfe->job_ready = false;
++ clear_state(cfe, NODE_STREAMING, node->id);
++ spin_unlock_irqrestore(&cfe->state_lock, flags);
++
++ cfe_stop_channel(node, fe_stop);
++
++ if (!test_any_node(cfe, NODE_STREAMING)) {
++ /* Stop streaming the sensor and disable the peripheral. */
++ if (v4l2_subdev_call(cfe->sensor, video, s_stream, 0) < 0)
++ cfe_err("stream off failed in subdev\n");
++
++ csi2_close_rx(&cfe->csi2);
++
++ cfg_reg_write(cfe, MIPICFG_INTE, 0);
++ }
++
++ media_pipeline_stop(&node->pad);
++
++ /* Clear all queued buffers for the node */
++ cfe_return_buffers(node, VB2_BUF_STATE_ERROR);
++
++ pm_runtime_put(&cfe->pdev->dev);
++
++ cfe_dbg("%s: [%s] end.\n", __func__, node_desc[node->id].name);
++}
++
++static const struct vb2_ops cfe_video_qops = {
++ .wait_prepare = vb2_ops_wait_prepare,
++ .wait_finish = vb2_ops_wait_finish,
++ .queue_setup = cfe_queue_setup,
++ .buf_prepare = cfe_buffer_prepare,
++ .buf_queue = cfe_buffer_queue,
++ .start_streaming = cfe_start_streaming,
++ .stop_streaming = cfe_stop_streaming,
++};
++
++/*
++ * v4l2 ioctl ops
++ */
++
++static int cfe_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++
++ strscpy(cap->driver, CFE_MODULE_NAME, sizeof(cap->driver));
++ strscpy(cap->card, CFE_MODULE_NAME, sizeof(cap->card));
++
++ snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s",
++ dev_name(&cfe->pdev->dev));
++
++ cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE |
++ V4L2_CAP_META_OUTPUT;
++
++ return 0;
++}
++
++static int cfe_enum_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++ unsigned int i, j;
++
++ if (!is_image_output_node(node))
++ return -EINVAL;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ for (i = 0, j = 0; i < ARRAY_SIZE(formats); i++) {
++ if (f->mbus_code && formats[i].code != f->mbus_code)
++ continue;
++
++ if (formats[i].flags & CFE_FORMAT_FLAG_META_OUT ||
++ formats[i].flags & CFE_FORMAT_FLAG_META_CAP)
++ continue;
++
++ if (is_fe_node(node) &&
++ !(formats[i].flags & CFE_FORMAT_FLAG_FE_OUT))
++ continue;
++
++ if (j == f->index) {
++ f->pixelformat = formats[i].fourcc;
++ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ return 0;
++ }
++ j++;
++ }
++
++ return -EINVAL;
++}
++
++static int cfe_g_fmt(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (f->type != node->buffer_queue.type)
++ return -EINVAL;
++
++ *f = node->fmt;
++
++ return 0;
++}
++
++static int try_fmt_vid_cap(struct cfe_node *node, struct v4l2_format *f)
++{
++ struct cfe_device *cfe = node->cfe;
++ const struct cfe_fmt *fmt;
++
++ cfe_dbg("%s: [%s] %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n",
++ __func__, node_desc[node->id].name,
++ f->fmt.pix.width, f->fmt.pix.height,
++ V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat));
++
++ if (!is_image_output_node(node))
++ return -EINVAL;
++
++ /*
++ * Default to a format that works for both CSI2 and FE.
++ */
++ fmt = find_format_by_pix(f->fmt.pix.pixelformat);
++ if (!fmt)
++ fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++
++ f->fmt.pix.pixelformat = fmt->fourcc;
++
++ if (is_fe_node(node) && fmt->remap[CFE_REMAP_16BIT]) {
++ f->fmt.pix.pixelformat = fmt->remap[CFE_REMAP_16BIT];
++ fmt = find_format_by_pix(f->fmt.pix.pixelformat);
++ }
++
++ f->fmt.pix.field = V4L2_FIELD_NONE;
++
++ cfe_calc_format_size_bpl(cfe, fmt, f);
++
++ return 0;
++}
++
++static int cfe_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++ struct vb2_queue *q = &node->buffer_queue;
++ int ret;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (vb2_is_busy(q))
++ return -EBUSY;
++
++ ret = try_fmt_vid_cap(node, f);
++ if (ret)
++ return ret;
++
++ node->fmt = *f;
++
++ cfe_dbg("%s: Set %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", __func__,
++ node->fmt.fmt.pix.width, node->fmt.fmt.pix.height,
++ V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat));
++
++ return 0;
++}
++
++static int cfe_try_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ return try_fmt_vid_cap(node, f);
++}
++
++static int cfe_enum_fmt_meta(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (!is_meta_node(node) || f->index != 0)
++ return -EINVAL;
++
++ switch (node->id) {
++ case CSI2_CH1_EMBEDDED:
++ f->pixelformat = V4L2_META_FMT_SENSOR_DATA;
++ return 0;
++ case FE_STATS:
++ f->pixelformat = V4L2_META_FMT_RPI_FE_STATS;
++ return 0;
++ case FE_CONFIG:
++ f->pixelformat = V4L2_META_FMT_RPI_FE_CFG;
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f)
++{
++ switch (node->id) {
++ case CSI2_CH1_EMBEDDED:
++ f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA;
++ if (!f->fmt.meta.buffersize)
++ f->fmt.meta.buffersize = DEFAULT_EMBEDDED_SIZE;
++ f->fmt.meta.buffersize =
++ min_t(u32, f->fmt.meta.buffersize, MAX_BUFFER_SIZE);
++ f->fmt.meta.buffersize =
++ ALIGN(f->fmt.meta.buffersize, BPL_ALIGNMENT);
++ return 0;
++ case FE_STATS:
++ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_STATS;
++ f->fmt.meta.buffersize = sizeof(struct pisp_statistics);
++ return 0;
++ case FE_CONFIG:
++ f->fmt.meta.dataformat = V4L2_META_FMT_RPI_FE_CFG;
++ f->fmt.meta.buffersize = sizeof(struct pisp_fe_config);
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++ struct vb2_queue *q = &node->buffer_queue;
++ int ret;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (vb2_is_busy(q))
++ return -EBUSY;
++
++ if (f->type != node->buffer_queue.type)
++ return -EINVAL;
++
++ ret = try_fmt_meta(node, f);
++ if (ret)
++ return ret;
++
++ node->fmt = *f;
++
++ cfe_dbg("%s: Set " V4L2_FOURCC_CONV "\n", __func__,
++ V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat));
++
++ return 0;
++}
++
++static int cfe_try_fmt_meta(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++ return try_fmt_meta(node, f);
++}
++
++static int cfe_enum_framesizes(struct file *file, void *priv,
++ struct v4l2_frmsizeenum *fsize)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++ const struct cfe_fmt *fmt;
++
++ cfe_dbg("%s [%s]\n", __func__, node_desc[node->id].name);
++
++ if (fsize->index > 0)
++ return -EINVAL;
++
++ /* check for valid format */
++ fmt = find_format_by_pix(fsize->pixel_format);
++ if (!fmt) {
++ cfe_dbg("Invalid pixel code: %x\n", fsize->pixel_format);
++ return -EINVAL;
++ }
++
++ /* TODO: Do we have limits on the step_width? */
++
++ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
++ fsize->stepwise.min_width = MIN_WIDTH;
++ fsize->stepwise.max_width = MAX_WIDTH;
++ fsize->stepwise.step_width = 2;
++ fsize->stepwise.min_height = MIN_HEIGHT;
++ fsize->stepwise.max_height = MAX_HEIGHT;
++ fsize->stepwise.step_height = 1;
++
++ return 0;
++}
++
++static int cfe_subscribe_event(struct v4l2_fh *fh,
++ const struct v4l2_event_subscription *sub)
++{
++ struct cfe_node *node = video_get_drvdata(fh->vdev);
++
++ switch (sub->type) {
++ case V4L2_EVENT_FRAME_SYNC:
++ if (!is_image_output_node(node))
++ break;
++
++ return v4l2_event_subscribe(fh, sub, 2, NULL);
++ case V4L2_EVENT_SOURCE_CHANGE:
++ if (is_meta_input_node(node))
++ break;
++
++ return v4l2_event_subscribe(fh, sub, 4, NULL);
++ }
++
++ return v4l2_ctrl_subscribe_event(fh, sub);
++}
++
++static const struct v4l2_ioctl_ops cfe_ioctl_ops = {
++ .vidioc_querycap = cfe_querycap,
++ .vidioc_enum_fmt_vid_cap = cfe_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = cfe_g_fmt,
++ .vidioc_s_fmt_vid_cap = cfe_s_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap,
++
++ .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta,
++ .vidioc_g_fmt_meta_cap = cfe_g_fmt,
++ .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta,
++ .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta,
++
++ .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta,
++ .vidioc_g_fmt_meta_out = cfe_g_fmt,
++ .vidioc_s_fmt_meta_out = cfe_s_fmt_meta,
++ .vidioc_try_fmt_meta_out = cfe_try_fmt_meta,
++
++ .vidioc_enum_framesizes = cfe_enum_framesizes,
++
++ .vidioc_reqbufs = vb2_ioctl_reqbufs,
++ .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
++ .vidioc_querybuf = vb2_ioctl_querybuf,
++ .vidioc_qbuf = vb2_ioctl_qbuf,
++ .vidioc_dqbuf = vb2_ioctl_dqbuf,
++ .vidioc_expbuf = vb2_ioctl_expbuf,
++ .vidioc_streamon = vb2_ioctl_streamon,
++ .vidioc_streamoff = vb2_ioctl_streamoff,
++
++ .vidioc_subscribe_event = cfe_subscribe_event,
++ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
++};
++
++static void cfe_notify(struct v4l2_subdev *sd, unsigned int notification,
++ void *arg)
++{
++ struct cfe_device *cfe = to_cfe_device(sd->v4l2_dev);
++ unsigned int i;
++
++ switch (notification) {
++ case V4L2_DEVICE_NOTIFY_EVENT:
++ for (i = 0; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++
++ if (check_state(cfe, NODE_REGISTERED, i))
++ continue;
++
++ v4l2_event_queue(&node->video_dev, arg);
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++/* cfe capture driver file operations */
++static const struct v4l2_file_operations cfe_fops = {
++ .owner = THIS_MODULE,
++ .open = v4l2_fh_open,
++ .release = vb2_fop_release,
++ .poll = vb2_fop_poll,
++ .unlocked_ioctl = video_ioctl2,
++ .mmap = vb2_fop_mmap,
++};
++
++static int cfe_video_link_validate(struct media_link *link)
++{
++ struct video_device *vd = container_of(link->sink->entity,
++ struct video_device, entity);
++ struct cfe_node *node = container_of(vd, struct cfe_node, video_dev);
++ struct cfe_device *cfe = node->cfe;
++ struct v4l2_mbus_framefmt *source_fmt;
++ struct v4l2_subdev_state *state;
++ struct v4l2_subdev *source_sd;
++ int ret = 0;
++
++ cfe_dbg("%s: [%s] link \"%s\":%u -> \"%s\":%u\n", __func__,
++ node_desc[node->id].name,
++ link->source->entity->name, link->source->index,
++ link->sink->entity->name, link->sink->index);
++
++ if (!media_entity_remote_source_pad_unique(link->sink->entity)) {
++ cfe_err("video node %s pad not connected\n", vd->name);
++ return -ENOTCONN;
++ }
++
++ source_sd = media_entity_to_v4l2_subdev(link->source->entity);
++
++ state = v4l2_subdev_lock_and_get_active_state(source_sd);
++
++ source_fmt = v4l2_subdev_get_pad_format(source_sd, state,
++ link->source->index);
++ if (!source_fmt) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (is_image_output_node(node)) {
++ struct v4l2_pix_format *pix_fmt = &node->fmt.fmt.pix;
++ const struct cfe_fmt *fmt;
++
++ if (source_fmt->width != pix_fmt->width ||
++ source_fmt->height != pix_fmt->height) {
++ cfe_err("Wrong width or height %ux%u (remote pad set to %ux%u)\n",
++ pix_fmt->width, pix_fmt->height,
++ source_fmt->width,
++ source_fmt->height);
++ ret = -EINVAL;
++ goto out;
++ }
++
++ fmt = find_format_by_code(source_fmt->code);
++ if (!fmt || fmt->fourcc != pix_fmt->pixelformat) {
++ cfe_err("Format mismatch!\n");
++ ret = -EINVAL;
++ goto out;
++ }
++ } else if (node->id == CSI2_CH1_EMBEDDED) {
++ struct v4l2_meta_format *meta_fmt = &node->fmt.fmt.meta;
++
++ if (source_fmt->width * source_fmt->height !=
++ meta_fmt->buffersize ||
++ source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) {
++ cfe_err("WARNING: Wrong metadata width/height/code %ux%u %08x (remote pad set to %ux%u %08x)\n",
++ meta_fmt->buffersize, 1,
++ MEDIA_BUS_FMT_SENSOR_DATA,
++ source_fmt->width,
++ source_fmt->height,
++ source_fmt->code);
++ /* TODO: this should throw an error eventually */
++ }
++ }
++
++out:
++ v4l2_subdev_unlock_state(state);
++
++ return ret;
++}
++
++static const struct media_entity_operations cfe_media_entity_ops = {
++ .link_validate = cfe_video_link_validate,
++};
++
++static int cfe_video_link_notify(struct media_link *link, u32 flags,
++ unsigned int notification)
++{
++ struct media_device *mdev = link->graph_obj.mdev;
++ struct cfe_device *cfe = container_of(mdev, struct cfe_device, mdev);
++ struct media_entity *fe = &cfe->fe.sd.entity;
++ struct media_entity *csi2 = &cfe->csi2.sd.entity;
++ unsigned long lock_flags;
++ unsigned int i;
++
++ if (notification != MEDIA_DEV_NOTIFY_POST_LINK_CH)
++ return 0;
++
++ cfe_dbg("%s: %s[%u] -> %s[%u] 0x%x", __func__,
++ link->source->entity->name, link->source->index,
++ link->sink->entity->name, link->sink->index, flags);
++
++ spin_lock_irqsave(&cfe->state_lock, lock_flags);
++
++ for (i = 0; i < NUM_NODES; i++) {
++ if (link->sink->entity != &cfe->node[i].video_dev.entity &&
++ link->source->entity != &cfe->node[i].video_dev.entity)
++ continue;
++
++ if (link->flags & MEDIA_LNK_FL_ENABLED)
++ set_state(cfe, NODE_ENABLED, i);
++ else
++ clear_state(cfe, NODE_ENABLED, i);
++
++ break;
++ }
++
++ spin_unlock_irqrestore(&cfe->state_lock, lock_flags);
++
++ if (link->source->entity != csi2)
++ return 0;
++ if (link->sink->index != 0)
++ return 0;
++ if (link->source->index == node_desc[CSI2_CH1_EMBEDDED].link_pad)
++ return 0;
++
++ cfe->fe_csi2_channel = -1;
++ if (link->sink->entity == fe && (link->flags & MEDIA_LNK_FL_ENABLED)) {
++ if (link->source->index == node_desc[CSI2_CH0].link_pad)
++ cfe->fe_csi2_channel = CSI2_CH0;
++ else if (link->source->index == node_desc[CSI2_CH2].link_pad)
++ cfe->fe_csi2_channel = CSI2_CH2;
++ else if (link->source->index == node_desc[CSI2_CH3].link_pad)
++ cfe->fe_csi2_channel = CSI2_CH3;
++ }
++
++ if (is_fe_enabled(cfe))
++ cfe_dbg("%s: Found CSI2:%d -> FE:0 link\n", __func__,
++ cfe->fe_csi2_channel);
++ else
++ cfe_dbg("%s: Unable to find CSI2:x -> FE:0 link\n", __func__);
++
++ return 0;
++}
++
++static const struct media_device_ops cfe_media_device_ops = {
++ .link_notify = cfe_video_link_notify,
++};
++
++static void cfe_release(struct kref *kref)
++{
++ struct cfe_device *cfe = container_of(kref, struct cfe_device, kref);
++
++ media_device_cleanup(&cfe->mdev);
++
++ kfree(cfe);
++}
++
++static void cfe_put(struct cfe_device *cfe)
++{
++ kref_put(&cfe->kref, cfe_release);
++}
++
++static void cfe_get(struct cfe_device *cfe)
++{
++ kref_get(&cfe->kref);
++}
++
++static void cfe_node_release(struct video_device *vdev)
++{
++ struct cfe_node *node = video_get_drvdata(vdev);
++
++ cfe_put(node->cfe);
++}
++
++static int cfe_register_node(struct cfe_device *cfe, int id)
++{
++ struct video_device *vdev;
++ const struct cfe_fmt *fmt;
++ struct vb2_queue *q;
++ struct cfe_node *node = &cfe->node[id];
++ int ret;
++
++ node->cfe = cfe;
++ node->id = id;
++
++ if (is_image_output_node(node)) {
++ fmt = find_format_by_code(cfe_default_format.code);
++ if (!fmt) {
++ cfe_err("Failed to find format code\n");
++ return -EINVAL;
++ }
++
++ node->fmt.fmt.pix.pixelformat = fmt->fourcc;
++ v4l2_fill_pix_format(&node->fmt.fmt.pix, &cfe_default_format);
++
++ ret = try_fmt_vid_cap(node, &node->fmt);
++ if (ret)
++ return ret;
++ } else {
++ ret = try_fmt_meta(node, &node->fmt);
++ if (ret)
++ return ret;
++ }
++ node->fmt.type = node_desc[id].buf_type;
++
++ mutex_init(&node->lock);
++
++ q = &node->buffer_queue;
++ q->type = node_desc[id].buf_type;
++ q->io_modes = VB2_MMAP | VB2_DMABUF;
++ q->drv_priv = node;
++ q->ops = &cfe_video_qops;
++ q->mem_ops = &vb2_dma_contig_memops;
++ q->buf_struct_size = id == FE_CONFIG ? sizeof(struct cfe_config_buffer)
++ : sizeof(struct cfe_buffer);
++ q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
++ q->lock = &node->lock;
++ q->min_buffers_needed = 1;
++ q->dev = &cfe->pdev->dev;
++
++ ret = vb2_queue_init(q);
++ if (ret) {
++ cfe_err("vb2_queue_init() failed\n");
++ return ret;
++ }
++
++ INIT_LIST_HEAD(&node->dma_queue);
++
++ vdev = &node->video_dev;
++ vdev->release = cfe_node_release;
++ vdev->fops = &cfe_fops;
++ vdev->ioctl_ops = &cfe_ioctl_ops;
++ vdev->entity.ops = &cfe_media_entity_ops;
++ vdev->v4l2_dev = &cfe->v4l2_dev;
++ vdev->vfl_dir = (is_image_output_node(node) || is_meta_output_node(node))
++ ? VFL_DIR_RX : VFL_DIR_TX;
++ vdev->queue = q;
++ vdev->lock = &node->lock;
++ vdev->device_caps = node_desc[id].cap;
++ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
++
++ /* Define the device names */
++ snprintf(vdev->name, sizeof(vdev->name), "%s-%s", CFE_MODULE_NAME,
++ node_desc[id].name);
++
++ video_set_drvdata(vdev, node);
++ if (node->id == FE_OUT0)
++ vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT;
++ node->pad.flags = node_desc[id].pad_flags;
++ media_entity_pads_init(&vdev->entity, 1, &node->pad);
++
++ if (is_meta_node(node)) {
++ v4l2_disable_ioctl(&node->video_dev,
++ VIDIOC_ENUM_FRAMEINTERVALS);
++ v4l2_disable_ioctl(&node->video_dev,
++ VIDIOC_ENUM_FRAMESIZES);
++ }
++
++ ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1);
++ if (ret) {
++ cfe_err("Unable to register video device %s\n", vdev->name);
++ return ret;
++ }
++
++ cfe_info("Registered [%s] node id %d successfully as /dev/video%u\n",
++ vdev->name, id, vdev->num);
++
++ /*
++ * Acquire a reference to cfe, which will be released when the video
++ * device will be unregistered and userspace will have closed all open
++ * file handles.
++ */
++ cfe_get(cfe);
++ set_state(cfe, NODE_REGISTERED, id);
++
++ return 0;
++}
++
++static void cfe_unregister_nodes(struct cfe_device *cfe)
++{
++ unsigned int i;
++
++ for (i = 0; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++
++ if (check_state(cfe, NODE_REGISTERED, i)) {
++ clear_state(cfe, NODE_REGISTERED, i);
++ video_unregister_device(&node->video_dev);
++ }
++ }
++}
++
++static int cfe_link_node_pads(struct cfe_device *cfe)
++{
++ unsigned int i;
++ int ret;
++
++ for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
++ struct cfe_node *node = &cfe->node[i];
++
++ if (!check_state(cfe, NODE_REGISTERED, i))
++ continue;
++
++ if (i < cfe->sensor->entity.num_pads) {
++ /* Sensor -> CSI2 */
++ ret = media_create_pad_link(&cfe->sensor->entity, i,
++ &cfe->csi2.sd.entity, i,
++ MEDIA_LNK_FL_IMMUTABLE |
++ MEDIA_LNK_FL_ENABLED);
++ if (ret)
++ return ret;
++ }
++
++ /* CSI2 channel # -> /dev/video# */
++ ret = media_create_pad_link(&cfe->csi2.sd.entity,
++ node_desc[i].link_pad,
++ &node->video_dev.entity, 0, 0);
++ if (ret)
++ return ret;
++
++ if (node->id != CSI2_CH1_EMBEDDED) {
++ /* CSI2 channel # -> FE Input */
++ ret = media_create_pad_link(&cfe->csi2.sd.entity,
++ node_desc[i].link_pad,
++ &cfe->fe.sd.entity,
++ FE_STREAM_PAD, 0);
++ if (ret)
++ return ret;
++ }
++ }
++
++ for (; i < NUM_NODES; i++) {
++ struct cfe_node *node = &cfe->node[i];
++ struct media_entity *src, *dst;
++ unsigned int src_pad, dst_pad;
++
++ if (node_desc[i].pad_flags & MEDIA_PAD_FL_SINK) {
++ /* FE -> /dev/video# */
++ src = &cfe->fe.sd.entity;
++ src_pad = node_desc[i].link_pad;
++ dst = &node->video_dev.entity;
++ dst_pad = 0;
++ } else {
++ /* /dev/video# -> FE */
++ dst = &cfe->fe.sd.entity;
++ dst_pad = node_desc[i].link_pad;
++ src = &node->video_dev.entity;
++ src_pad = 0;
++ }
++
++ ret = media_create_pad_link(src, src_pad, dst, dst_pad, 0);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int cfe_probe_complete(struct cfe_device *cfe)
++{
++ unsigned int i;
++ int ret;
++
++ cfe->v4l2_dev.notify = cfe_notify;
++
++ cfe->sensor_embedded_data = (cfe->sensor->entity.num_pads >= 2);
++
++ for (i = 0; i < NUM_NODES; i++) {
++ ret = cfe_register_node(cfe, i);
++ if (ret) {
++ cfe_err("Unable to register video node %u.\n", i);
++ goto unregister;
++ }
++ }
++
++ ret = cfe_link_node_pads(cfe);
++ if (ret) {
++ cfe_err("Unable to link node pads.\n");
++ goto unregister;
++ }
++
++ ret = v4l2_device_register_subdev_nodes(&cfe->v4l2_dev);
++ if (ret) {
++ cfe_err("Unable to register subdev nodes.\n");
++ goto unregister;
++ }
++
++ /*
++ * Release the initial reference, all references are now owned by the
++ * video devices.
++ */
++ cfe_put(cfe);
++ return 0;
++
++unregister:
++ cfe_unregister_nodes(cfe);
++ cfe_put(cfe);
++
++ return ret;
++}
++
++static int cfe_async_bound(struct v4l2_async_notifier *notifier,
++ struct v4l2_subdev *subdev,
++ struct v4l2_async_subdev *asd)
++{
++ struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
++
++ if (cfe->sensor) {
++ cfe_info("Rejecting subdev %s (Already set!!)", subdev->name);
++ return 0;
++ }
++
++ cfe->sensor = subdev;
++ cfe_info("Using sensor %s for capture\n", subdev->name);
++
++ return 0;
++}
++
++static int cfe_async_complete(struct v4l2_async_notifier *notifier)
++{
++ struct cfe_device *cfe = to_cfe_device(notifier->v4l2_dev);
++
++ return cfe_probe_complete(cfe);
++}
++
++static const struct v4l2_async_notifier_operations cfe_async_ops = {
++ .bound = cfe_async_bound,
++ .complete = cfe_async_complete,
++};
++
++static int of_cfe_connect_subdevs(struct cfe_device *cfe)
++{
++ struct platform_device *pdev = cfe->pdev;
++ struct v4l2_fwnode_endpoint ep = { .bus_type = V4L2_MBUS_CSI2_DPHY };
++ struct device_node *node = pdev->dev.of_node;
++ struct device_node *ep_node;
++ struct device_node *sensor_node;
++ unsigned int lane;
++ int ret = -EINVAL;
++
++ /* Get the local endpoint and remote device. */
++ ep_node = of_graph_get_next_endpoint(node, NULL);
++ if (!ep_node) {
++ cfe_err("can't get next endpoint\n");
++ return -EINVAL;
++ }
++
++ cfe_dbg("ep_node is %pOF\n", ep_node);
++
++ sensor_node = of_graph_get_remote_port_parent(ep_node);
++ if (!sensor_node) {
++ cfe_err("can't get remote parent\n");
++ goto cleanup_exit;
++ }
++
++ cfe_info("found subdevice %pOF\n", sensor_node);
++
++ /* Parse the local endpoint and validate its configuration. */
++ v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep);
++
++ cfe->csi2.multipacket_line =
++ fwnode_property_present(of_fwnode_handle(ep_node),
++ "multipacket-line");
++
++ if (ep.bus_type != V4L2_MBUS_CSI2_DPHY) {
++ cfe_err("endpoint node type != CSI2\n");
++ return -EINVAL;
++ }
++
++ for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) {
++ if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) {
++ cfe_err("subdevice %pOF: data lanes reordering not supported\n",
++ sensor_node);
++ goto cleanup_exit;
++ }
++ }
++
++ /* TODO: Get the frequency from devicetree */
++ cfe->csi2.dphy.dphy_freq = 999;
++ cfe->csi2.dphy.num_lanes = ep.bus.mipi_csi2.num_data_lanes;
++ cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags;
++
++ cfe_dbg("subdevice %pOF: %u data lanes, flags=0x%08x, multipacket_line=%u\n",
++ sensor_node, cfe->csi2.dphy.num_lanes, cfe->csi2.bus_flags,
++ cfe->csi2.multipacket_line);
++
++ /* Initialize and register the async notifier. */
++ v4l2_async_nf_init(&cfe->notifier);
++ cfe->notifier.ops = &cfe_async_ops;
++
++ cfe->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
++ cfe->asd.match.fwnode = of_fwnode_handle(sensor_node);
++ ret = __v4l2_async_nf_add_subdev(&cfe->notifier, &cfe->asd);
++ if (ret) {
++ cfe_err("Error adding subdevice: %d\n", ret);
++ goto cleanup_exit;
++ }
++
++ ret = v4l2_async_nf_register(&cfe->v4l2_dev, &cfe->notifier);
++ if (ret) {
++ cfe_err("Error registering async notifier: %d\n", ret);
++ ret = -EINVAL;
++ }
++
++cleanup_exit:
++ of_node_put(sensor_node);
++ of_node_put(ep_node);
++
++ return ret;
++}
++
++static int cfe_probe(struct platform_device *pdev)
++{
++ struct cfe_device *cfe;
++ char debugfs_name[32];
++ int ret;
++
++ cfe = kzalloc(sizeof(*cfe), GFP_KERNEL);
++ if (!cfe)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, cfe);
++
++ kref_init(&cfe->kref);
++ cfe->pdev = pdev;
++ cfe->fe_csi2_channel = -1;
++ spin_lock_init(&cfe->state_lock);
++
++ cfe->csi2.base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(cfe->csi2.base)) {
++ dev_err(&pdev->dev, "Failed to get dma io block\n");
++ ret = PTR_ERR(cfe->csi2.base);
++ goto err_cfe_put;
++ }
++
++ cfe->csi2.dphy.base = devm_platform_ioremap_resource(pdev, 1);
++ if (IS_ERR(cfe->csi2.dphy.base)) {
++ dev_err(&pdev->dev, "Failed to get host io block\n");
++ ret = PTR_ERR(cfe->csi2.dphy.base);
++ goto err_cfe_put;
++ }
++
++ cfe->mipi_cfg_base = devm_platform_ioremap_resource(pdev, 2);
++ if (IS_ERR(cfe->mipi_cfg_base)) {
++ dev_err(&pdev->dev, "Failed to get mipi cfg io block\n");
++ ret = PTR_ERR(cfe->mipi_cfg_base);
++ goto err_cfe_put;
++ }
++
++ cfe->fe.base = devm_platform_ioremap_resource(pdev, 3);
++ if (IS_ERR(cfe->fe.base)) {
++ dev_err(&pdev->dev, "Failed to get pisp fe io block\n");
++ ret = PTR_ERR(cfe->fe.base);
++ goto err_cfe_put;
++ }
++
++ ret = platform_get_irq(pdev, 0);
++ if (ret <= 0) {
++ dev_err(&pdev->dev, "No IRQ resource\n");
++ ret = -EINVAL;
++ goto err_cfe_put;
++ }
++
++ ret = devm_request_irq(&pdev->dev, ret, cfe_isr, 0, "rp1-cfe", cfe);
++ if (ret) {
++ dev_err(&pdev->dev, "Unable to request interrupt\n");
++ ret = -EINVAL;
++ goto err_cfe_put;
++ }
++
++ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
++ if (ret) {
++ dev_err(&pdev->dev, "DMA enable failed\n");
++ goto err_cfe_put;
++ }
++
++ /* TODO: Enable clock only when running. */
++ cfe->clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(cfe->clk))
++ return dev_err_probe(&pdev->dev, PTR_ERR(cfe->clk),
++ "clock not found\n");
++
++ cfe->mdev.dev = &pdev->dev;
++ cfe->mdev.ops = &cfe_media_device_ops;
++ strscpy(cfe->mdev.model, CFE_MODULE_NAME, sizeof(cfe->mdev.model));
++ strscpy(cfe->mdev.serial, "", sizeof(cfe->mdev.serial));
++ snprintf(cfe->mdev.bus_info, sizeof(cfe->mdev.bus_info), "platform:%s",
++ dev_name(&pdev->dev));
++
++ media_device_init(&cfe->mdev);
++
++ cfe->v4l2_dev.mdev = &cfe->mdev;
++
++ ret = v4l2_device_register(&pdev->dev, &cfe->v4l2_dev);
++ if (ret) {
++ cfe_err("Unable to register v4l2 device.\n");
++ goto err_cfe_put;
++ }
++
++ snprintf(debugfs_name, sizeof(debugfs_name), "rp1-cfe:%s",
++ dev_name(&pdev->dev));
++ cfe->debugfs = debugfs_create_dir(debugfs_name, NULL);
++ debugfs_create_file("format", 0444, cfe->debugfs, cfe, &format_fops);
++ debugfs_create_file("regs", 0444, cfe->debugfs, cfe,
++ &mipi_cfg_regs_fops);
++
++ /* Enable the block power domain */
++ pm_runtime_enable(&pdev->dev);
++
++ ret = pm_runtime_resume_and_get(&cfe->pdev->dev);
++ if (ret)
++ goto err_runtime_disable;
++
++ cfe->csi2.v4l2_dev = &cfe->v4l2_dev;
++ ret = csi2_init(&cfe->csi2, cfe->debugfs);
++ if (ret) {
++ cfe_err("Failed to init csi2 (%d)\n", ret);
++ goto err_runtime_put;
++ }
++
++ cfe->fe.v4l2_dev = &cfe->v4l2_dev;
++ ret = pisp_fe_init(&cfe->fe, cfe->debugfs);
++ if (ret) {
++ cfe_err("Failed to init pisp fe (%d)\n", ret);
++ goto err_csi2_uninit;
++ }
++
++ cfe->mdev.hw_revision = cfe->fe.hw_revision;
++ ret = media_device_register(&cfe->mdev);
++ if (ret < 0) {
++ cfe_err("Unable to register media-controller device.\n");
++ goto err_pisp_fe_uninit;
++ }
++
++ ret = of_cfe_connect_subdevs(cfe);
++ if (ret) {
++ cfe_err("Failed to connect subdevs\n");
++ goto err_media_unregister;
++ }
++
++ pm_runtime_put(&cfe->pdev->dev);
++
++ return 0;
++
++err_media_unregister:
++ media_device_unregister(&cfe->mdev);
++err_pisp_fe_uninit:
++ pisp_fe_uninit(&cfe->fe);
++err_csi2_uninit:
++ csi2_uninit(&cfe->csi2);
++err_runtime_put:
++ pm_runtime_put(&cfe->pdev->dev);
++err_runtime_disable:
++ pm_runtime_disable(&pdev->dev);
++ debugfs_remove(cfe->debugfs);
++ v4l2_device_unregister(&cfe->v4l2_dev);
++err_cfe_put:
++ cfe_put(cfe);
++
++ return ret;
++}
++
++static int cfe_remove(struct platform_device *pdev)
++{
++ struct cfe_device *cfe = platform_get_drvdata(pdev);
++
++ debugfs_remove(cfe->debugfs);
++
++ v4l2_async_nf_unregister(&cfe->notifier);
++ media_device_unregister(&cfe->mdev);
++ cfe_unregister_nodes(cfe);
++
++ pisp_fe_uninit(&cfe->fe);
++ csi2_uninit(&cfe->csi2);
++
++ pm_runtime_disable(&pdev->dev);
++
++ v4l2_device_unregister(&cfe->v4l2_dev);
++
++ return 0;
++}
++
++static int cfe_runtime_suspend(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct cfe_device *cfe = platform_get_drvdata(pdev);
++
++ clk_disable_unprepare(cfe->clk);
++
++ return 0;
++}
++
++static int cfe_runtime_resume(struct device *dev)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct cfe_device *cfe = platform_get_drvdata(pdev);
++ int ret;
++
++ ret = clk_prepare_enable(cfe->clk);
++ if (ret) {
++ dev_err(dev, "Unable to enable clock\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct dev_pm_ops cfe_pm_ops = {
++ SET_RUNTIME_PM_OPS(cfe_runtime_suspend, cfe_runtime_resume, NULL)
++ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
++};
++
++static const struct of_device_id cfe_of_match[] = {
++ { .compatible = "raspberrypi,rp1-cfe" },
++ { /* sentinel */ },
++};
++MODULE_DEVICE_TABLE(of, cfe_of_match);
++
++static struct platform_driver cfe_driver = {
++ .probe = cfe_probe,
++ .remove = cfe_remove,
++ .driver = {
++ .name = CFE_MODULE_NAME,
++ .of_match_table = cfe_of_match,
++ .pm = &cfe_pm_ops,
++ },
++};
++
++module_platform_driver(cfe_driver);
++
++MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
++MODULE_DESCRIPTION("RP1 Camera Front End driver");
++MODULE_LICENSE("GPL");
++MODULE_VERSION(CFE_VERSION);
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -0,0 +1,40 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 CFE driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++#ifndef _RP1_CFE_
++#define _RP1_CFE_
++
++#include <linux/types.h>
++#include <linux/media-bus-format.h>
++#include <linux/videodev2.h>
++
++extern bool cfe_debug_irq;
++
++enum cfe_remap_types {
++ CFE_REMAP_16BIT,
++ CFE_REMAP_COMPRESSED,
++ CFE_NUM_REMAP,
++};
++
++#define CFE_FORMAT_FLAG_META_OUT BIT(0)
++#define CFE_FORMAT_FLAG_META_CAP BIT(1)
++#define CFE_FORMAT_FLAG_FE_OUT BIT(2)
++
++struct cfe_fmt {
++ u32 fourcc;
++ u32 code;
++ u8 depth;
++ u8 csi_dt;
++ u32 remap[CFE_NUM_REMAP];
++ u32 flags;
++};
++
++extern const struct v4l2_mbus_framefmt cfe_default_format;
++extern const struct v4l2_mbus_framefmt cfe_default_meta_format;
++
++const struct cfe_fmt *find_format_by_code(u32 code);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -0,0 +1,294 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 Camera Front End formats definition
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _CFE_FMTS_H_
++#define _CFE_FMTS_H_
++
++#include "cfe.h"
++
++static const struct cfe_fmt formats[] = {
++ /* YUV Formats */
++ {
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .code = MEDIA_BUS_FMT_YUYV8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_UYVY,
++ .code = MEDIA_BUS_FMT_UYVY8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_YVYU,
++ .code = MEDIA_BUS_FMT_YVYU8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_VYUY,
++ .code = MEDIA_BUS_FMT_VYUY8_1X16,
++ .depth = 16,
++ .csi_dt = 0x1e,
++ },
++ {
++ /* RGB Formats */
++ .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
++ .code = MEDIA_BUS_FMT_RGB565_2X8_LE,
++ .depth = 16,
++ .csi_dt = 0x22,
++ },
++ { .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
++ .code = MEDIA_BUS_FMT_RGB565_2X8_BE,
++ .depth = 16,
++ .csi_dt = 0x22
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
++ .depth = 16,
++ .csi_dt = 0x21,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
++ .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
++ .depth = 16,
++ .csi_dt = 0x21,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
++ .code = MEDIA_BUS_FMT_RGB888_1X24,
++ .depth = 24,
++ .csi_dt = 0x24,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
++ .code = MEDIA_BUS_FMT_BGR888_1X24,
++ .depth = 24,
++ .csi_dt = 0x24,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_RGB32, /* argb */
++ .code = MEDIA_BUS_FMT_ARGB8888_1X32,
++ .depth = 32,
++ .csi_dt = 0x0,
++ },
++
++ /* Bayer Formats */
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR8,
++ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG8,
++ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG8,
++ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB8,
++ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR10P,
++ .code = MEDIA_BUS_FMT_SBGGR10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG10P,
++ .code = MEDIA_BUS_FMT_SGBRG10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG10P,
++ .code = MEDIA_BUS_FMT_SGRBG10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB10P,
++ .code = MEDIA_BUS_FMT_SRGGB10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR12P,
++ .code = MEDIA_BUS_FMT_SBGGR12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG12P,
++ .code = MEDIA_BUS_FMT_SGBRG12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG12P,
++ .code = MEDIA_BUS_FMT_SGRBG12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB12P,
++ .code = MEDIA_BUS_FMT_SRGGB12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR14P,
++ .code = MEDIA_BUS_FMT_SBGGR14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG14P,
++ .code = MEDIA_BUS_FMT_SGBRG14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG14P,
++ .code = MEDIA_BUS_FMT_SGRBG14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB14P,
++ .code = MEDIA_BUS_FMT_SRGGB14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SBGGR16,
++ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
++ .depth = 16,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGBRG16,
++ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
++ .depth = 16,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SGRBG16,
++ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
++ .depth = 16,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_SRGGB16,
++ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
++ .depth = 16,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ /* PiSP Compressed Mode 1 */
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
++ .code = MEDIA_BUS_FMT_PISP_COMP1_RGGB,
++ .depth = 8,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
++ .code = MEDIA_BUS_FMT_PISP_COMP1_BGGR,
++ .depth = 8,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
++ .code = MEDIA_BUS_FMT_PISP_COMP1_GBRG,
++ .depth = 8,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
++ .code = MEDIA_BUS_FMT_PISP_COMP1_GRBG,
++ .depth = 8,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++ /* Greyscale format */
++ {
++ .fourcc = V4L2_PIX_FMT_GREY,
++ .code = MEDIA_BUS_FMT_Y8_1X8,
++ .depth = 8,
++ .csi_dt = 0x2a,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_Y10P,
++ .code = MEDIA_BUS_FMT_Y10_1X10,
++ .depth = 10,
++ .csi_dt = 0x2b,
++ .remap = { V4L2_PIX_FMT_Y16 },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_Y12P,
++ .code = MEDIA_BUS_FMT_Y12_1X12,
++ .depth = 12,
++ .csi_dt = 0x2c,
++ .remap = { V4L2_PIX_FMT_Y16 },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_Y14P,
++ .code = MEDIA_BUS_FMT_Y14_1X14,
++ .depth = 14,
++ .csi_dt = 0x2d,
++ .remap = { V4L2_PIX_FMT_Y16 },
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_Y16,
++ .depth = 16,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
++
++ /* Embedded data format */
++ {
++ .fourcc = V4L2_META_FMT_SENSOR_DATA,
++ .code = MEDIA_BUS_FMT_SENSOR_DATA,
++ .depth = 8,
++ .csi_dt = 0x12,
++ .flags = CFE_FORMAT_FLAG_META_CAP,
++ },
++
++ /* Frontend formats */
++ {
++ .fourcc = V4L2_META_FMT_RPI_FE_CFG,
++ .flags = CFE_FORMAT_FLAG_META_OUT,
++ },
++ {
++ .fourcc = V4L2_META_FMT_RPI_FE_STATS,
++ .flags = CFE_FORMAT_FLAG_META_CAP,
++ },
++};
++
++#endif /* _CFE_FMTS_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -0,0 +1,446 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RP1 CSI-2 Driver
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++#include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
++
++#include <media/videobuf2-dma-contig.h>
++
++#include "csi2.h"
++#include "cfe.h"
++
++#define csi2_dbg_irq(fmt, arg...) \
++ do { \
++ if (cfe_debug_irq) \
++ dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg); \
++ } while (0)
++#define csi2_dbg(fmt, arg...) dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg)
++#define csi2_info(fmt, arg...) dev_info(csi2->v4l2_dev->dev, fmt, ##arg)
++#define csi2_err(fmt, arg...) dev_err(csi2->v4l2_dev->dev, fmt, ##arg)
++
++/* CSI2-DMA registers */
++#define CSI2_STATUS 0x000
++#define CSI2_QOS 0x004
++#define CSI2_DISCARDS_OVERFLOW 0x008
++#define CSI2_DISCARDS_INACTIVE 0x00c
++#define CSI2_DISCARDS_UNMATCHED 0x010
++#define CSI2_DISCARDS_LEN_LIMIT 0x014
++#define CSI2_LLEV_PANICS 0x018
++#define CSI2_ULEV_PANICS 0x01c
++#define CSI2_IRQ_MASK 0x020
++#define CSI2_CTRL 0x024
++#define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28)
++#define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c)
++#define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c)
++#define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30)
++#define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34)
++#define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38)
++#define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40)
++#define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44)
++#define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48)
++
++/* CSI2_STATUS */
++#define IRQ_FS(x) (BIT(0) << (x))
++#define IRQ_FE(x) (BIT(4) << (x))
++#define IRQ_FE_ACK(x) (BIT(8) << (x))
++#define IRQ_LE(x) (BIT(12) << (x))
++#define IRQ_LE_ACK(x) (BIT(16) << (x))
++#define IRQ_CH_MASK(x) (IRQ_FS(x) | IRQ_FE(x) | IRQ_FE_ACK(x) | IRQ_LE(x) | IRQ_LE_ACK(x))
++#define IRQ_OVERFLOW BIT(20)
++#define IRQ_DISCARD_OVERFLOW BIT(21)
++#define IRQ_DISCARD_LEN_LIMIT BIT(22)
++#define IRQ_DISCARD_UNMATCHED BIT(23)
++#define IRQ_DISCARD_INACTIVE BIT(24)
++
++/* CSI2_CTRL */
++#define EOP_IS_EOL BIT(0)
++
++/* CSI2_CH_CTRL */
++#define DMA_EN BIT(0)
++#define FORCE BIT(3)
++#define AUTO_ARM BIT(4)
++#define IRQ_EN_FS BIT(13)
++#define IRQ_EN_FE BIT(14)
++#define IRQ_EN_FE_ACK BIT(15)
++#define IRQ_EN_LE BIT(16)
++#define IRQ_EN_LE_ACK BIT(17)
++#define FLUSH_FE BIT(28)
++#define PACK_LINE BIT(29)
++#define PACK_BYTES BIT(30)
++#define CH_MODE_MASK GENMASK(2, 1)
++#define VC_MASK GENMASK(6, 5)
++#define DT_MASK GENMASK(12, 7)
++#define LC_MASK GENMASK(27, 18)
++
++/* CHx_COMPRESSION_CONTROL */
++#define COMP_OFFSET_MASK GENMASK(15, 0)
++#define COMP_SHIFT_MASK GENMASK(19, 16)
++#define COMP_MODE_MASK GENMASK(25, 24)
++
++static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset)
++{
++ return readl(csi2->base + offset);
++}
++
++static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
++{
++ writel(val, csi2->base + offset);
++}
++
++static inline void set_field(u32 *valp, u32 field, u32 mask)
++{
++ u32 val = *valp;
++
++ val &= ~mask;
++ val |= (field << __ffs(mask)) & mask;
++ *valp = val;
++}
++
++static int csi2_regs_show(struct seq_file *s, void *data)
++{
++ struct csi2_device *csi2 = s->private;
++ unsigned int i;
++ int ret;
++
++ ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev);
++ if (ret)
++ return ret;
++
++#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg))
++#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, csi2_reg_read(csi2, reg(idx)))
++
++ DUMP(CSI2_STATUS);
++ DUMP(CSI2_DISCARDS_OVERFLOW);
++ DUMP(CSI2_DISCARDS_INACTIVE);
++ DUMP(CSI2_DISCARDS_UNMATCHED);
++ DUMP(CSI2_DISCARDS_LEN_LIMIT);
++ DUMP(CSI2_LLEV_PANICS);
++ DUMP(CSI2_ULEV_PANICS);
++ DUMP(CSI2_IRQ_MASK);
++ DUMP(CSI2_CTRL);
++
++ for (i = 0; i < CSI2_NUM_CHANNELS; ++i) {
++ DUMP_CH(i, CSI2_CH_CTRL);
++ DUMP_CH(i, CSI2_CH_ADDR0);
++ DUMP_CH(i, CSI2_CH_ADDR1);
++ DUMP_CH(i, CSI2_CH_STRIDE);
++ DUMP_CH(i, CSI2_CH_LENGTH);
++ DUMP_CH(i, CSI2_CH_DEBUG);
++ DUMP_CH(i, CSI2_CH_FRAME_SIZE);
++ DUMP_CH(i, CSI2_CH_COMP_CTRL);
++ DUMP_CH(i, CSI2_CH_FE_FRAME_ID);
++ }
++
++#undef DUMP
++#undef DUMP_CH
++
++ pm_runtime_put(csi2->v4l2_dev->dev);
++
++ return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(csi2_regs);
++
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
++{
++ unsigned int i;
++ u32 status;
++
++ status = csi2_reg_read(csi2, CSI2_STATUS);
++ csi2_dbg_irq("ISR: STA: 0x%x\n", status);
++
++ /* Write value back to clear the interrupts */
++ csi2_reg_write(csi2, CSI2_STATUS, status);
++
++ for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
++ u32 dbg;
++
++ if ((status & IRQ_CH_MASK(i)) == 0)
++ continue;
++
++ dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i));
++
++ csi2_dbg_irq("ISR: [%u], %s%s%s%s%s frame: %u line: %u\n", i,
++ (status & IRQ_FS(i)) ? "FS " : "",
++ (status & IRQ_FE(i)) ? "FE " : "",
++ (status & IRQ_FE_ACK(i)) ? "FE_ACK " : "",
++ (status & IRQ_LE(i)) ? "LE " : "",
++ (status & IRQ_LE_ACK(i)) ? "LE_ACK " : "",
++ dbg >> 16,
++ csi2->num_lines[i] ?
++ ((dbg & 0xffff) % csi2->num_lines[i]) :
++ 0);
++
++ sof[i] = !!(status & IRQ_FS(i));
++ eof[i] = !!(status & IRQ_FE_ACK(i));
++ lci[i] = !!(status & IRQ_LE_ACK(i));
++ }
++}
++
++void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
++ dma_addr_t dmaaddr, unsigned int stride, unsigned int size)
++{
++ u64 addr = dmaaddr;
++ /*
++ * ADDRESS0 must be written last as it triggers the double buffering
++ * mechanism for all buffer registers within the hardware.
++ */
++ addr >>= 4;
++ csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4);
++ csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4);
++ csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32);
++ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff);
++}
++
++void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
++ enum csi2_compression_mode mode, unsigned int shift,
++ unsigned int offset)
++{
++ u32 compression = 0;
++
++ set_field(&compression, COMP_OFFSET_MASK, offset);
++ set_field(&compression, COMP_SHIFT_MASK, shift);
++ set_field(&compression, COMP_MODE_MASK, mode);
++ csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
++}
++
++void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
++ u16 dt, enum csi2_mode mode, bool auto_arm,
++ bool pack_bytes, unsigned int width,
++ unsigned int height)
++{
++ u32 ctrl;
++
++ csi2_dbg("%s [%u]\n", __func__, channel);
++
++ /*
++ * Disable the channel, but ensure N != 0! Otherwise we end up with a
++ * spurious LE + LE_ACK interrupt when re-enabling the channel.
++ */
++ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0x100 << __ffs(LC_MASK));
++ csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0);
++ csi2_reg_write(csi2, CSI2_STATUS, IRQ_CH_MASK(channel));
++
++ /* Enable channel and FS/FE/LE interrupts. */
++ ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | IRQ_EN_LE_ACK | PACK_LINE;
++ /* PACK_BYTES ensures no striding for embedded data. */
++ if (pack_bytes)
++ ctrl |= PACK_BYTES;
++
++ if (auto_arm)
++ ctrl |= AUTO_ARM;
++
++ if (width && height) {
++ int line_int_freq = height >> 2;
++
++ line_int_freq = min(max(0x80, line_int_freq), 0x3ff);
++ set_field(&ctrl, line_int_freq, LC_MASK);
++ set_field(&ctrl, mode, CH_MODE_MASK);
++ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel),
++ (height << 16) | width);
++ } else {
++ /*
++ * Do not disable line interrupts for the embedded data channel,
++ * set it to the maximum value. This avoids spamming the ISR
++ * with spurious line interrupts.
++ */
++ set_field(&ctrl, 0x3ff, LC_MASK);
++ set_field(&ctrl, 0x00, CH_MODE_MASK);
++ }
++
++ set_field(&ctrl, dt, DT_MASK);
++ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
++ csi2->num_lines[channel] = height;
++}
++
++void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel)
++{
++ csi2_dbg("%s [%u]\n", __func__, channel);
++
++ /* Channel disable. Use FORCE to allow stopping mid-frame. */
++ csi2_reg_write(csi2, CSI2_CH_CTRL(channel),
++ (0x100 << __ffs(LC_MASK)) | FORCE);
++ /* Latch the above change by writing to the ADDR0 register. */
++ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
++ /* Write this again, the HW needs it! */
++ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
++}
++
++void csi2_open_rx(struct csi2_device *csi2)
++{
++ dphy_start(&csi2->dphy);
++
++ if (!csi2->multipacket_line)
++ csi2_reg_write(csi2, CSI2_CTRL, EOP_IS_EOL);
++}
++
++void csi2_close_rx(struct csi2_device *csi2)
++{
++ dphy_stop(&csi2->dphy);
++}
++
++static struct csi2_device *to_csi2_device(struct v4l2_subdev *subdev)
++{
++ return container_of(subdev, struct csi2_device, sd);
++}
++
++static int csi2_init_cfg(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state)
++{
++ struct v4l2_mbus_framefmt *fmt;
++
++ for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) {
++ const struct v4l2_mbus_framefmt *def_fmt;
++
++ /* CSI2_CH1_EMBEDDED */
++ if (i == 1)
++ def_fmt = &cfe_default_meta_format;
++ else
++ def_fmt = &cfe_default_format;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, i);
++ *fmt = *def_fmt;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, i + CSI2_NUM_CHANNELS);
++ *fmt = *def_fmt;
++ }
++
++ return 0;
++}
++
++static int csi2_pad_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct v4l2_mbus_framefmt *fmt;
++ const struct cfe_fmt *cfe_fmt;
++
++ /* TODO: format validation */
++
++ cfe_fmt = find_format_by_code(format->format.code);
++ if (!cfe_fmt)
++ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++
++ format->format.code = cfe_fmt->code;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++ *fmt = format->format;
++
++ if (format->pad < CSI2_NUM_CHANNELS) {
++ /* Propagate to the source pad */
++ fmt = v4l2_subdev_get_pad_format(sd, state,
++ format->pad + CSI2_NUM_CHANNELS);
++ *fmt = format->format;
++ }
++
++ return 0;
++}
++
++static int csi2_link_validate(struct v4l2_subdev *sd, struct media_link *link,
++ struct v4l2_subdev_format *source_fmt,
++ struct v4l2_subdev_format *sink_fmt)
++{
++ struct csi2_device *csi2 = to_csi2_device(sd);
++
++ csi2_dbg("%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
++ link->source->entity->name, link->source->index,
++ link->sink->entity->name, link->sink->index);
++
++ if ((link->source->entity == &csi2->sd.entity &&
++ link->source->index == 1) ||
++ (link->sink->entity == &csi2->sd.entity &&
++ link->sink->index == 1)) {
++ csi2_dbg("Ignore metadata pad for now\n");
++ return 0;
++ }
++
++ /* The width, height and code must match. */
++ if (source_fmt->format.width != sink_fmt->format.width ||
++ source_fmt->format.width != sink_fmt->format.width ||
++ source_fmt->format.code != sink_fmt->format.code) {
++ csi2_err("%s: format does not match (source %ux%u 0x%x, sink %ux%u 0x%x)\n",
++ __func__,
++ source_fmt->format.width, source_fmt->format.height,
++ source_fmt->format.code,
++ sink_fmt->format.width, sink_fmt->format.height,
++ sink_fmt->format.code);
++ return -EPIPE;
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {
++ .init_cfg = csi2_init_cfg,
++ .get_fmt = v4l2_subdev_get_fmt,
++ .set_fmt = csi2_pad_set_fmt,
++ .link_validate = csi2_link_validate,
++};
++
++static const struct media_entity_operations csi2_entity_ops = {
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++static const struct v4l2_subdev_ops csi2_subdev_ops = {
++ .pad = &csi2_subdev_pad_ops,
++};
++
++int csi2_init(struct csi2_device *csi2, struct dentry *debugfs)
++{
++ unsigned int i, ret;
++
++ csi2->dphy.dev = csi2->v4l2_dev->dev;
++ dphy_probe(&csi2->dphy);
++
++ debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops);
++
++ for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++)
++ csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ?
++ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad),
++ csi2->pad);
++ if (ret)
++ return ret;
++
++ /* Initialize subdev */
++ v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
++ csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
++ csi2->sd.entity.ops = &csi2_entity_ops;
++ csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
++ csi2->sd.owner = THIS_MODULE;
++ snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2");
++
++ ret = v4l2_subdev_init_finalize(&csi2->sd);
++ if (ret)
++ goto err_entity_cleanup;
++
++ ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd);
++ if (ret) {
++ csi2_err("Failed register csi2 subdev (%d)\n", ret);
++ goto err_subdev_cleanup;
++ }
++
++ return 0;
++
++err_subdev_cleanup:
++ v4l2_subdev_cleanup(&csi2->sd);
++err_entity_cleanup:
++ media_entity_cleanup(&csi2->sd.entity);
++
++ return ret;
++}
++
++void csi2_uninit(struct csi2_device *csi2)
++{
++ v4l2_device_unregister_subdev(&csi2->sd);
++ v4l2_subdev_cleanup(&csi2->sd);
++ media_entity_cleanup(&csi2->sd.entity);
++}
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -0,0 +1,75 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 CSI-2 driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++#ifndef _RP1_CSI2_
++#define _RP1_CSI2_
++
++#include <linux/debugfs.h>
++#include <linux/io.h>
++#include <linux/types.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-subdev.h>
++
++#include "dphy.h"
++
++#define CSI2_NUM_CHANNELS 4
++
++enum csi2_mode {
++ CSI2_MODE_NORMAL,
++ CSI2_MODE_REMAP,
++ CSI2_MODE_COMPRESSED,
++ CSI2_MODE_FE_STREAMING
++};
++
++enum csi2_compression_mode {
++ CSI2_COMPRESSION_DELTA = 1,
++ CSI2_COMPRESSION_SIMPLE = 2,
++ CSI2_COMPRESSION_COMBINED = 3,
++};
++
++struct csi2_cfg {
++ u16 width;
++ u16 height;
++ u32 stride;
++ u32 buffer_size;
++};
++
++struct csi2_device {
++ /* Parent V4l2 device */
++ struct v4l2_device *v4l2_dev;
++
++ void __iomem *base;
++
++ struct dphy_data dphy;
++
++ enum v4l2_mbus_type bus_type;
++ unsigned int bus_flags;
++ u32 active_data_lanes;
++ bool multipacket_line;
++ unsigned int num_lines[CSI2_NUM_CHANNELS];
++
++ struct media_pad pad[CSI2_NUM_CHANNELS * 2];
++ struct v4l2_subdev sd;
++};
++
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);
++void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
++ dma_addr_t dmaaddr, unsigned int stride,
++ unsigned int size);
++void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
++ enum csi2_compression_mode mode, unsigned int shift,
++ unsigned int offset);
++void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
++ u16 dt, enum csi2_mode mode, bool auto_arm,
++ bool pack_bytes, unsigned int width,
++ unsigned int height);
++void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel);
++void csi2_open_rx(struct csi2_device *csi2);
++void csi2_close_rx(struct csi2_device *csi2);
++int csi2_init(struct csi2_device *csi2, struct dentry *debugfs);
++void csi2_uninit(struct csi2_device *csi2);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * RP1 CSI-2 Driver
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/delay.h>
++#include <linux/dev_printk.h>
++#include <linux/pm_runtime.h>
++
++#include "dphy.h"
++
++#define dphy_dbg(fmt, arg...) dev_dbg(dphy->dev, fmt, ##arg)
++#define dphy_info(fmt, arg...) dev_info(dphy->dev, fmt, ##arg)
++#define dphy_err(fmt, arg...) dev_err(dphy->dev, fmt, ##arg)
++
++/* DW dphy Host registers */
++#define VERSION 0x000
++#define N_LANES 0x004
++#define RESETN 0x008
++#define PHY_SHUTDOWNZ 0x040
++#define PHY_RSTZ 0x044
++#define PHY_RX 0x048
++#define PHY_STOPSTATE 0x04c
++#define PHY_TST_CTRL0 0x050
++#define PHY_TST_CTRL1 0x054
++#define PHY2_TST_CTRL0 0x058
++#define PHY2_TST_CTRL1 0x05c
++
++/* DW dphy Host Transactions */
++#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44
++#define DPHY_PLL_INPUT_DIV_OFFSET 0x17
++#define DPHY_PLL_LOOP_DIV_OFFSET 0x18
++#define DPHY_PLL_DIV_CTRL_OFFSET 0x19
++
++static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset)
++{
++ return readl(dphy->base + offset);
++}
++
++static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data)
++{
++ writel(data, dphy->base + offset);
++}
++
++static void set_tstclr(struct dphy_data *dphy, u32 val)
++{
++ u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0);
++
++ dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~1) | val);
++}
++
++static void set_tstclk(struct dphy_data *dphy, u32 val)
++{
++ u32 ctrl0 = dw_csi2_host_read(dphy, PHY_TST_CTRL0);
++
++ dw_csi2_host_write(dphy, PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1));
++}
++
++static uint8_t get_tstdout(struct dphy_data *dphy)
++{
++ u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1);
++
++ return ((ctrl1 >> 8) & 0xff);
++}
++
++static void set_testen(struct dphy_data *dphy, u32 val)
++{
++ u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1);
++
++ dw_csi2_host_write(dphy, PHY_TST_CTRL1,
++ (ctrl1 & ~(1 << 16)) | (val << 16));
++}
++
++static void set_testdin(struct dphy_data *dphy, u32 val)
++{
++ u32 ctrl1 = dw_csi2_host_read(dphy, PHY_TST_CTRL1);
++
++ dw_csi2_host_write(dphy, PHY_TST_CTRL1, (ctrl1 & ~0xff) | val);
++}
++
++static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code,
++ uint8_t test_data)
++{
++ /* See page 101 of the MIPI DPHY databook. */
++ set_tstclk(dphy, 1);
++ set_testen(dphy, 0);
++ set_testdin(dphy, test_code);
++ set_testen(dphy, 1);
++ set_tstclk(dphy, 0);
++ set_testen(dphy, 0);
++ set_testdin(dphy, test_data);
++ set_tstclk(dphy, 1);
++ return get_tstdout(dphy);
++}
++
++static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t freq_mhz)
++{
++ /* See Table 5-1 on page 65 of dphy databook */
++ static const u16 hsfreqrange_table[][2] = {
++ { 89, 0b000000 }, { 99, 0b010000 }, { 109, 0b100000 },
++ { 129, 0b000001 }, { 139, 0b010001 }, { 149, 0b100001 },
++ { 169, 0b000010 }, { 179, 0b010010 }, { 199, 0b100010 },
++ { 219, 0b000011 }, { 239, 0b010011 }, { 249, 0b100011 },
++ { 269, 0b000100 }, { 299, 0b010100 }, { 329, 0b000101 },
++ { 359, 0b010101 }, { 399, 0b100101 }, { 449, 0b000110 },
++ { 499, 0b010110 }, { 549, 0b000111 }, { 599, 0b010111 },
++ { 649, 0b001000 }, { 699, 0b011000 }, { 749, 0b001001 },
++ { 799, 0b011001 }, { 849, 0b101001 }, { 899, 0b111001 },
++ { 949, 0b001010 }, { 999, 0b011010 }, { 1049, 0b101010 },
++ { 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 },
++ { 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 },
++ { 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 },
++ };
++ unsigned int i;
++
++ if (freq_mhz < 80 || freq_mhz > 1500)
++ dphy_err("DPHY: Frequency %u MHz out of range\n", freq_mhz);
++
++ for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
++ if (freq_mhz <= hsfreqrange_table[i][0])
++ break;
++ }
++
++ dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET,
++ hsfreqrange_table[i][1] << 1);
++}
++
++static void dphy_init(struct dphy_data *dphy)
++{
++ dw_csi2_host_write(dphy, PHY_RSTZ, 0);
++ dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 0);
++ set_tstclk(dphy, 1);
++ set_testen(dphy, 0);
++ set_tstclr(dphy, 1);
++ usleep_range(15, 20);
++ set_tstclr(dphy, 0);
++ usleep_range(15, 20);
++
++ dphy_set_hsfreqrange(dphy, dphy->dphy_freq);
++
++ usleep_range(5, 10);
++ dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1);
++ usleep_range(5, 10);
++ dw_csi2_host_write(dphy, PHY_RSTZ, 1);
++}
++
++void dphy_start(struct dphy_data *dphy)
++{
++ dw_csi2_host_write(dphy, N_LANES, (dphy->num_lanes - 1));
++ dphy_init(dphy);
++ dw_csi2_host_write(dphy, RESETN, 0xffffffff);
++ usleep_range(10, 50);
++}
++
++void dphy_stop(struct dphy_data *dphy)
++{
++ /* Set only one lane (lane 0) as active (ON) */
++ dw_csi2_host_write(dphy, N_LANES, 0);
++ dw_csi2_host_write(dphy, RESETN, 0);
++}
++
++void dphy_probe(struct dphy_data *dphy)
++{
++ u32 host_ver;
++ u8 host_ver_major, host_ver_minor;
++
++ host_ver = dw_csi2_host_read(dphy, VERSION);
++ host_ver_major = (u8)((host_ver >> 24) - '0');
++ host_ver_minor = (u8)((host_ver >> 16) - '0');
++ host_ver_minor = host_ver_minor * 10;
++ host_ver_minor += (u8)((host_ver >> 8) - '0');
++
++ dphy_info("DW dphy Host HW v%u.%u\n", host_ver_major, host_ver_minor);
++}
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
+@@ -0,0 +1,26 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++
++#ifndef _RP1_DPHY_
++#define _RP1_DPHY_
++
++#include <linux/io.h>
++#include <linux/types.h>
++
++struct dphy_data {
++ struct device *dev;
++
++ void __iomem *base;
++
++ u32 dphy_freq;
++ u32 num_lanes;
++};
++
++void dphy_probe(struct dphy_data *dphy);
++void dphy_start(struct dphy_data *dphy);
++void dphy_stop(struct dphy_data *dphy);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
+@@ -0,0 +1,69 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP common definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_COMMON_H_
++#define _PISP_COMMON_H_
++
++#include "pisp_types.h"
++
++struct pisp_bla_config {
++ u16 black_level_r;
++ u16 black_level_gr;
++ u16 black_level_gb;
++ u16 black_level_b;
++ u16 output_black_level;
++ u8 pad[2];
++};
++
++struct pisp_wbg_config {
++ u16 gain_r;
++ u16 gain_g;
++ u16 gain_b;
++ u8 pad[2];
++};
++
++struct pisp_compress_config {
++ /* value subtracted from incoming data */
++ u16 offset;
++ u8 pad;
++ /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++ u8 mode;
++};
++
++struct pisp_decompress_config {
++ /* value added to reconstructed data */
++ u16 offset;
++ u8 pad;
++ /* 1 => Companding; 2 => Delta (recommended); 3 => Combined (for HDR) */
++ u8 mode;
++};
++
++enum pisp_axi_flags {
++ /*
++ * round down bursts to end at a 32-byte boundary, to align following
++ * bursts
++ */
++ PISP_AXI_FLAG_ALIGN = 128,
++ /* for FE writer: force WSTRB high, to pad output to 16-byte boundary */
++ PISP_AXI_FLAG_PAD = 64,
++ /* for FE writer: Use Output FIFO level to trigger "panic" */
++ PISP_AXI_FLAG_PANIC = 32,
++};
++
++struct pisp_axi_config {
++ /*
++ * burst length minus one, which must be in the range 0:15; OR'd with
++ * flags
++ */
++ u8 maxlen_flags;
++ /* { prot[2:0], cache[3:0] } fields, echoed on AXI bus */
++ u8 cache_prot;
++ /* QoS field(s) (4x4 bits for FE writer; 4 bits for other masters) */
++ u16 qos;
++};
++
++#endif /* _PISP_COMMON_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -0,0 +1,563 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * PiSP Front End driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++
++#include <linux/bitops.h>
++#include <linux/delay.h>
++#include <linux/moduleparam.h>
++#include <linux/pm_runtime.h>
++#include <linux/seq_file.h>
++
++#include <media/videobuf2-dma-contig.h>
++
++#include "pisp_fe.h"
++#include "cfe.h"
++
++#define FE_VERSION 0x000
++#define FE_CONTROL 0x004
++#define FE_STATUS 0x008
++#define FE_FRAME_STATUS 0x00c
++#define FE_ERROR_STATUS 0x010
++#define FE_OUTPUT_STATUS 0x014
++#define FE_INT_EN 0x018
++#define FE_INT_STATUS 0x01c
++
++/* CONTROL */
++#define FE_CONTROL_QUEUE BIT(0)
++#define FE_CONTROL_ABORT BIT(1)
++#define FE_CONTROL_RESET BIT(2)
++#define FE_CONTROL_LATCH_REGS BIT(3)
++
++/* INT_EN / INT_STATUS */
++#define FE_INT_EOF BIT(0)
++#define FE_INT_SOF BIT(1)
++#define FE_INT_LINES0 BIT(8)
++#define FE_INT_LINES1 BIT(9)
++#define FE_INT_STATS BIT(16)
++#define FE_INT_QREADY BIT(24)
++
++/* STATUS */
++#define FE_STATUS_QUEUED BIT(0)
++#define FE_STATUS_WAITING BIT(1)
++#define FE_STATUS_ACTIVE BIT(2)
++
++#define PISP_FE_CONFIG_BASE_OFFSET 0x0040
++
++#define PISP_FE_ENABLE_STATS_CLUSTER \
++ (PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \
++ PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \
++ PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \
++ PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS)
++
++#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \
++ ((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \
++ PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i)))
++
++struct pisp_fe_config_param {
++ u32 dirty_flags;
++ u32 dirty_flags_extra;
++ size_t offset;
++ size_t size;
++};
++
++static const struct pisp_fe_config_param pisp_fe_config_map[] = {
++ /* *_dirty_flag_extra types */
++ { 0, PISP_FE_DIRTY_GLOBAL, offsetof(struct pisp_fe_config, global),
++ sizeof(struct pisp_fe_global_config) },
++ { 0, PISP_FE_DIRTY_FLOATING, offsetof(struct pisp_fe_config, floating_stats),
++ sizeof(struct pisp_fe_floating_stats_config) },
++ { 0, PISP_FE_DIRTY_OUTPUT_AXI, offsetof(struct pisp_fe_config, output_axi),
++ sizeof(struct pisp_fe_output_axi_config) },
++ /* *_dirty_flag types */
++ { PISP_FE_ENABLE_INPUT, 0, offsetof(struct pisp_fe_config, input),
++ sizeof(struct pisp_fe_input_config) },
++ { PISP_FE_ENABLE_DECOMPRESS, 0, offsetof(struct pisp_fe_config, decompress),
++ sizeof(struct pisp_decompress_config) },
++ { PISP_FE_ENABLE_DECOMPAND, 0, offsetof(struct pisp_fe_config, decompand),
++ sizeof(struct pisp_fe_decompand_config) },
++ { PISP_FE_ENABLE_BLA, 0, offsetof(struct pisp_fe_config, bla),
++ sizeof(struct pisp_bla_config) },
++ { PISP_FE_ENABLE_DPC, 0, offsetof(struct pisp_fe_config, dpc),
++ sizeof(struct pisp_fe_dpc_config) },
++ { PISP_FE_ENABLE_STATS_CROP, 0, offsetof(struct pisp_fe_config, stats_crop),
++ sizeof(struct pisp_fe_crop_config) },
++ { PISP_FE_ENABLE_BLC, 0, offsetof(struct pisp_fe_config, blc),
++ sizeof(struct pisp_bla_config) },
++ { PISP_FE_ENABLE_CDAF_STATS, 0, offsetof(struct pisp_fe_config, cdaf_stats),
++ sizeof(struct pisp_fe_cdaf_stats_config) },
++ { PISP_FE_ENABLE_AWB_STATS, 0, offsetof(struct pisp_fe_config, awb_stats),
++ sizeof(struct pisp_fe_awb_stats_config) },
++ { PISP_FE_ENABLE_RGBY, 0, offsetof(struct pisp_fe_config, rgby),
++ sizeof(struct pisp_fe_rgby_config) },
++ { PISP_FE_ENABLE_LSC, 0, offsetof(struct pisp_fe_config, lsc),
++ sizeof(struct pisp_fe_lsc_config) },
++ { PISP_FE_ENABLE_AGC_STATS, 0, offsetof(struct pisp_fe_config, agc_stats),
++ sizeof(struct pisp_agc_statistics) },
++ { PISP_FE_ENABLE_CROP0, 0, offsetof(struct pisp_fe_config, ch[0].crop),
++ sizeof(struct pisp_fe_crop_config) },
++ { PISP_FE_ENABLE_DOWNSCALE0, 0, offsetof(struct pisp_fe_config, ch[0].downscale),
++ sizeof(struct pisp_fe_downscale_config) },
++ { PISP_FE_ENABLE_COMPRESS0, 0, offsetof(struct pisp_fe_config, ch[0].compress),
++ sizeof(struct pisp_compress_config) },
++ { PISP_FE_ENABLE_OUTPUT0, 0, offsetof(struct pisp_fe_config, ch[0].output),
++ sizeof(struct pisp_fe_output_config) },
++ { PISP_FE_ENABLE_CROP1, 0, offsetof(struct pisp_fe_config, ch[1].crop),
++ sizeof(struct pisp_fe_crop_config) },
++ { PISP_FE_ENABLE_DOWNSCALE1, 0, offsetof(struct pisp_fe_config, ch[1].downscale),
++ sizeof(struct pisp_fe_downscale_config) },
++ { PISP_FE_ENABLE_COMPRESS1, 0, offsetof(struct pisp_fe_config, ch[1].compress),
++ sizeof(struct pisp_compress_config) },
++ { PISP_FE_ENABLE_OUTPUT1, 0, offsetof(struct pisp_fe_config, ch[1].output),
++ sizeof(struct pisp_fe_output_config) },
++};
++
++#define pisp_fe_dbg_irq(fmt, arg...) \
++ do { \
++ if (cfe_debug_irq) \
++ dev_dbg(fe->v4l2_dev->dev, fmt, ##arg); \
++ } while (0)
++#define pisp_fe_dbg(fmt, arg...) dev_dbg(fe->v4l2_dev->dev, fmt, ##arg)
++#define pisp_fe_info(fmt, arg...) dev_info(fe->v4l2_dev->dev, fmt, ##arg)
++#define pisp_fe_err(fmt, arg...) dev_err(fe->v4l2_dev->dev, fmt, ##arg)
++
++static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset)
++{
++ return readl(fe->base + offset);
++}
++
++static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset,
++ u32 val)
++{
++ writel(val, fe->base + offset);
++}
++
++static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, u32 offset,
++ u32 val)
++{
++ writel_relaxed(val, fe->base + offset);
++}
++
++static int pisp_regs_show(struct seq_file *s, void *data)
++{
++ struct pisp_fe_device *fe = s->private;
++ int ret;
++
++ ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev);
++ if (ret)
++ return ret;
++
++ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
++
++#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg))
++ DUMP(FE_VERSION);
++ DUMP(FE_CONTROL);
++ DUMP(FE_STATUS);
++ DUMP(FE_FRAME_STATUS);
++ DUMP(FE_ERROR_STATUS);
++ DUMP(FE_OUTPUT_STATUS);
++ DUMP(FE_INT_EN);
++ DUMP(FE_INT_STATUS);
++#undef DUMP
++
++ pm_runtime_put(fe->v4l2_dev->dev);
++
++ return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(pisp_regs);
++
++static void pisp_config_write(struct pisp_fe_device *fe,
++ struct pisp_fe_config *config,
++ unsigned int start_offset,
++ unsigned int size)
++{
++ const unsigned int max_offset =
++ offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]);
++ unsigned int i, end_offset;
++ u32 *cfg = (u32 *)config;
++
++ start_offset = min(start_offset, max_offset);
++ end_offset = min(start_offset + size, max_offset);
++
++ cfg += start_offset >> 2;
++ for (i = start_offset; i < end_offset; i += 4, cfg++)
++ pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i,
++ *cfg);
++}
++
++void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof)
++{
++ u32 status, int_status, out_status, frame_status, error_status;
++ unsigned int i;
++
++ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
++ status = pisp_fe_reg_read(fe, FE_STATUS);
++ out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS);
++ frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS);
++ error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS);
++
++ int_status = pisp_fe_reg_read(fe, FE_INT_STATUS);
++ pisp_fe_reg_write(fe, FE_INT_STATUS, int_status);
++
++ pisp_fe_dbg_irq("%s: status 0x%x out 0x%x frame 0x%x error 0x%x int 0x%x\n",
++ __func__, status, out_status, frame_status, error_status,
++ int_status);
++
++ /* We do not report interrupts for the input/stream pad. */
++ for (i = 0; i < FE_NUM_PADS - 1; i++) {
++ sof[i] = !!(int_status & FE_INT_SOF);
++ eof[i] = !!(int_status & FE_INT_EOF);
++ }
++}
++
++static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg,
++ unsigned int c, struct v4l2_format const *f)
++{
++ unsigned int wbytes;
++
++ wbytes = cfg->ch[c].output.format.width;
++ if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK)
++ wbytes *= 2;
++
++ /* Check output image dimensions are nonzero and not too big */
++ if (cfg->ch[c].output.format.width < 2 ||
++ cfg->ch[c].output.format.height < 2 ||
++ cfg->ch[c].output.format.height > f->fmt.pix.height ||
++ cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline ||
++ wbytes > f->fmt.pix.bytesperline)
++ return false;
++
++ /* Check for zero-sized crops, which could cause lockup */
++ if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) &&
++ ((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) ||
++ cfg->ch[c].crop.offset_y >= cfg->input.format.height ||
++ cfg->ch[c].crop.width < 2 ||
++ cfg->ch[c].crop.height < 2)))
++ return false;
++
++ if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) &&
++ (cfg->ch[c].downscale.output_width < 2 ||
++ cfg->ch[c].downscale.output_height < 2))
++ return false;
++
++ return true;
++}
++
++static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg)
++{
++ /* Check for zero-sized crop, which could cause lockup */
++ return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) ||
++ (cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) &&
++ cfg->stats_crop.offset_y < cfg->input.format.height &&
++ cfg->stats_crop.width >= 2 &&
++ cfg->stats_crop.height >= 2));
++}
++
++int pisp_fe_validate_config(struct pisp_fe_device *fe,
++ struct pisp_fe_config *cfg,
++ struct v4l2_format const *f0,
++ struct v4l2_format const *f1)
++{
++ unsigned int i;
++
++ /*
++ * Check the input is enabled, streaming and has nonzero size;
++ * to avoid cases where the hardware might lock up or try to
++ * read inputs from memory (which this driver doesn't support).
++ */
++ if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) ||
++ cfg->input.streaming != 1 || cfg->input.format.width < 2 ||
++ cfg->input.format.height < 2) {
++ pisp_fe_err("%s: Input config not valid", __func__);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
++ if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) {
++ if (cfg->global.enables &
++ PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) {
++ pisp_fe_err("%s: Output %u not valid",
++ __func__, i);
++ return -EINVAL;
++ }
++ continue;
++ }
++
++ if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0))
++ return -EINVAL;
++ }
++
++ if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) &&
++ !pisp_fe_validate_stats(cfg)) {
++ pisp_fe_err("%s: Stats config not valid", __func__);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
++ struct pisp_fe_config *cfg)
++{
++ unsigned int i;
++ u64 addr;
++ u32 status;
++
++ /*
++ * Check output buffers exist and outputs are correctly configured.
++ * If valid, set the buffer's DMA address; otherwise disable.
++ */
++ for (i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
++ struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i];
++
++ if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i)))
++ continue;
++
++ addr = vb2_dma_contig_plane_dma_addr(buf, 0);
++ cfg->output_buffer[i].addr_lo = addr & 0xffffffff;
++ cfg->output_buffer[i].addr_hi = addr >> 32;
++ }
++
++ if (vb2_bufs[FE_STATS_PAD]) {
++ addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0);
++ cfg->stats_buffer.addr_lo = addr & 0xffffffff;
++ cfg->stats_buffer.addr_hi = addr >> 32;
++ }
++
++ /* Set up ILINES interrupts 3/4 of the way down each output */
++ cfg->ch[0].output.ilines =
++ max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2);
++ cfg->ch[1].output.ilines =
++ max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2);
++
++ /*
++ * The hardware must have consumed the previous config by now.
++ * This read of status also serves as a memory barrier before the
++ * sequence of relaxed writes which follow.
++ */
++ status = pisp_fe_reg_read(fe, FE_STATUS);
++ pisp_fe_dbg_irq("%s: status = 0x%x\n", __func__, status);
++ if (WARN_ON(status & FE_STATUS_QUEUED))
++ return;
++
++ /*
++ * Unconditionally write buffers, global and input parameters.
++ * Write cropping and output parameters whenever they are enabled.
++ * Selectively write other parameters that have been marked as
++ * changed through the dirty flags.
++ */
++ pisp_config_write(fe, cfg, 0,
++ offsetof(struct pisp_fe_config, decompress));
++ cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL;
++ cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT;
++ cfg->dirty_flags |= (cfg->global.enables &
++ (PISP_FE_ENABLE_STATS_CROP |
++ PISP_FE_ENABLE_OUTPUT_CLUSTER(0) |
++ PISP_FE_ENABLE_OUTPUT_CLUSTER(1)));
++ for (i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) {
++ const struct pisp_fe_config_param *p = &pisp_fe_config_map[i];
++
++ if (cfg->dirty_flags & p->dirty_flags ||
++ cfg->dirty_flags_extra & p->dirty_flags_extra)
++ pisp_config_write(fe, cfg, p->offset, p->size);
++ }
++
++ /* This final non-relaxed write serves as a memory barrier */
++ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE);
++}
++
++void pisp_fe_start(struct pisp_fe_device *fe)
++{
++ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);
++ pisp_fe_reg_write(fe, FE_INT_STATUS, -1);
++ pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1);
++ fe->inframe_count = 0;
++}
++
++void pisp_fe_stop(struct pisp_fe_device *fe)
++{
++ pisp_fe_reg_write(fe, FE_INT_EN, 0);
++ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);
++ usleep_range(1000, 2000);
++ WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
++ pisp_fe_reg_write(fe, FE_INT_STATUS, -1);
++}
++
++static struct pisp_fe_device *to_pisp_fe_device(struct v4l2_subdev *subdev)
++{
++ return container_of(subdev, struct pisp_fe_device, sd);
++}
++
++static int pisp_fe_init_cfg(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state)
++{
++ struct v4l2_mbus_framefmt *fmt;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD);
++ *fmt = cfe_default_format;
++ fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_CONFIG_PAD);
++ *fmt = cfe_default_meta_format;
++ fmt->code = MEDIA_BUS_FMT_PISP_FE_CONFIG;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD);
++ *fmt = cfe_default_format;
++ fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD);
++ *fmt = cfe_default_format;
++ fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_STATS_PAD);
++ *fmt = cfe_default_meta_format;
++ fmt->code = MEDIA_BUS_FMT_PISP_FE_STATS;
++
++ return 0;
++}
++
++static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state,
++ struct v4l2_subdev_format *format)
++{
++ struct v4l2_mbus_framefmt *fmt;
++ const struct cfe_fmt *cfe_fmt;
++
++ /* TODO: format propagation to source pads */
++ /* TODO: format validation */
++
++ switch (format->pad) {
++ case FE_STREAM_PAD:
++ case FE_OUTPUT0_PAD:
++ case FE_OUTPUT1_PAD:
++ cfe_fmt = find_format_by_code(format->format.code);
++ if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
++ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++
++ format->format.code = cfe_fmt->code;
++
++ break;
++
++ case FE_CONFIG_PAD:
++ format->format.code = MEDIA_BUS_FMT_PISP_FE_CONFIG;
++ break;
++
++ case FE_STATS_PAD:
++ format->format.code = MEDIA_BUS_FMT_PISP_FE_STATS;
++ break;
++ }
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++ *fmt = format->format;
++
++ return 0;
++}
++
++static int pisp_fe_link_validate(struct v4l2_subdev *sd,
++ struct media_link *link,
++ struct v4l2_subdev_format *source_fmt,
++ struct v4l2_subdev_format *sink_fmt)
++{
++ struct pisp_fe_device *fe = to_pisp_fe_device(sd);
++
++ pisp_fe_dbg("%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
++ link->source->entity->name, link->source->index,
++ link->sink->entity->name, link->sink->index);
++
++ /* The width, height and code must match. */
++ if (source_fmt->format.width != sink_fmt->format.width ||
++ source_fmt->format.width != sink_fmt->format.width ||
++ source_fmt->format.code != sink_fmt->format.code) {
++ pisp_fe_err("%s: format does not match (source %ux%u 0x%x, sink %ux%u 0x%x)\n",
++ __func__,
++ source_fmt->format.width,
++ source_fmt->format.height,
++ source_fmt->format.code,
++ sink_fmt->format.width,
++ sink_fmt->format.height,
++ sink_fmt->format.code);
++ return -EPIPE;
++ }
++
++ return 0;
++}
++
++static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = {
++ .init_cfg = pisp_fe_init_cfg,
++ .get_fmt = v4l2_subdev_get_fmt,
++ .set_fmt = pisp_fe_pad_set_fmt,
++ .link_validate = pisp_fe_link_validate,
++};
++
++static const struct media_entity_operations pisp_fe_entity_ops = {
++ .link_validate = v4l2_subdev_link_validate,
++};
++
++static const struct v4l2_subdev_ops pisp_fe_subdev_ops = {
++ .pad = &pisp_fe_subdev_pad_ops,
++};
++
++int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs)
++{
++ int ret;
++
++ debugfs_create_file("pisp_regs", 0444, debugfs, fe, &pisp_regs_fops);
++
++ fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION);
++ pisp_fe_info("PiSP FE HW v%u.%u\n",
++ (fe->hw_revision >> 24) & 0xff,
++ (fe->hw_revision >> 20) & 0x0f);
++
++ fe->pad[FE_STREAM_PAD].flags =
++ MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
++ fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK;
++ fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE;
++ fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE;
++ fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE;
++
++ ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad),
++ fe->pad);
++ if (ret)
++ return ret;
++
++ /* Initialize subdev */
++ v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops);
++ fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
++ fe->sd.entity.ops = &pisp_fe_entity_ops;
++ fe->sd.entity.name = "pisp-fe";
++ fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
++ fe->sd.owner = THIS_MODULE;
++ snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe");
++
++ ret = v4l2_subdev_init_finalize(&fe->sd);
++ if (ret)
++ goto err_entity_cleanup;
++
++ ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd);
++ if (ret) {
++ pisp_fe_err("Failed register pisp fe subdev (%d)\n", ret);
++ goto err_subdev_cleanup;
++ }
++
++ /* Must be in IDLE state (STATUS == 0) here. */
++ WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
++
++ return 0;
++
++err_subdev_cleanup:
++ v4l2_subdev_cleanup(&fe->sd);
++err_entity_cleanup:
++ media_entity_cleanup(&fe->sd.entity);
++
++ return ret;
++}
++
++void pisp_fe_uninit(struct pisp_fe_device *fe)
++{
++ v4l2_device_unregister_subdev(&fe->sd);
++ v4l2_subdev_cleanup(&fe->sd);
++ media_entity_cleanup(&fe->sd.entity);
++}
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.h
+@@ -0,0 +1,53 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * PiSP Front End driver.
++ * Copyright (c) 2021 Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_H_
++#define _PISP_FE_H_
++
++#include <linux/debugfs.h>
++#include <linux/io.h>
++#include <linux/types.h>
++#include <linux/videodev2.h>
++
++#include <media/media-device.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-subdev.h>
++
++#include "pisp_fe_config.h"
++
++enum pisp_fe_pads {
++ FE_STREAM_PAD,
++ FE_CONFIG_PAD,
++ FE_OUTPUT0_PAD,
++ FE_OUTPUT1_PAD,
++ FE_STATS_PAD,
++ FE_NUM_PADS
++};
++
++struct pisp_fe_device {
++ /* Parent V4l2 device */
++ struct v4l2_device *v4l2_dev;
++ void __iomem *base;
++ u32 hw_revision;
++
++ u16 inframe_count;
++ struct media_pad pad[FE_NUM_PADS];
++ struct v4l2_subdev sd;
++};
++
++void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof);
++int pisp_fe_validate_config(struct pisp_fe_device *fe,
++ struct pisp_fe_config *cfg,
++ struct v4l2_format const *f0,
++ struct v4l2_format const *f1);
++void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
++ struct pisp_fe_config *cfg);
++void pisp_fe_start(struct pisp_fe_device *fe);
++void pisp_fe_stop(struct pisp_fe_device *fe);
++int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs);
++void pisp_fe_uninit(struct pisp_fe_device *fe);
++
++#endif
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
+@@ -0,0 +1,272 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP Front End Driver Configuration structures
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_CONFIG_
++#define _PISP_FE_CONFIG_
++
++#include <media/raspberrypi/pisp_common.h>
++
++#include "pisp_statistics.h"
++
++#define PISP_FE_NUM_OUTPUTS 2
++
++enum pisp_fe_enable {
++ PISP_FE_ENABLE_INPUT = 0x000001,
++ PISP_FE_ENABLE_DECOMPRESS = 0x000002,
++ PISP_FE_ENABLE_DECOMPAND = 0x000004,
++ PISP_FE_ENABLE_BLA = 0x000008,
++ PISP_FE_ENABLE_DPC = 0x000010,
++ PISP_FE_ENABLE_STATS_CROP = 0x000020,
++ PISP_FE_ENABLE_DECIMATE = 0x000040,
++ PISP_FE_ENABLE_BLC = 0x000080,
++ PISP_FE_ENABLE_CDAF_STATS = 0x000100,
++ PISP_FE_ENABLE_AWB_STATS = 0x000200,
++ PISP_FE_ENABLE_RGBY = 0x000400,
++ PISP_FE_ENABLE_LSC = 0x000800,
++ PISP_FE_ENABLE_AGC_STATS = 0x001000,
++ PISP_FE_ENABLE_CROP0 = 0x010000,
++ PISP_FE_ENABLE_DOWNSCALE0 = 0x020000,
++ PISP_FE_ENABLE_COMPRESS0 = 0x040000,
++ PISP_FE_ENABLE_OUTPUT0 = 0x080000,
++ PISP_FE_ENABLE_CROP1 = 0x100000,
++ PISP_FE_ENABLE_DOWNSCALE1 = 0x200000,
++ PISP_FE_ENABLE_COMPRESS1 = 0x400000,
++ PISP_FE_ENABLE_OUTPUT1 = 0x800000
++};
++
++#define PISP_FE_ENABLE_CROP(i) (PISP_FE_ENABLE_CROP0 << (4 * (i)))
++#define PISP_FE_ENABLE_DOWNSCALE(i) (PISP_FE_ENABLE_DOWNSCALE0 << (4 * (i)))
++#define PISP_FE_ENABLE_COMPRESS(i) (PISP_FE_ENABLE_COMPRESS0 << (4 * (i)))
++#define PISP_FE_ENABLE_OUTPUT(i) (PISP_FE_ENABLE_OUTPUT0 << (4 * (i)))
++
++/*
++ * We use the enable flags to show when blocks are "dirty", but we need some
++ * extra ones too.
++ */
++enum pisp_fe_dirty {
++ PISP_FE_DIRTY_GLOBAL = 0x0001,
++ PISP_FE_DIRTY_FLOATING = 0x0002,
++ PISP_FE_DIRTY_OUTPUT_AXI = 0x0004
++};
++
++struct pisp_fe_global_config {
++ u32 enables;
++ u8 bayer_order;
++ u8 pad[3];
++};
++
++struct pisp_fe_input_axi_config {
++ /* burst length minus one, in the range 0..15; OR'd with flags */
++ u8 maxlen_flags;
++ /* { prot[2:0], cache[3:0] } fields */
++ u8 cache_prot;
++ /* QoS (only 4 LS bits are used) */
++ u16 qos;
++};
++
++struct pisp_fe_output_axi_config {
++ /* burst length minus one, in the range 0..15; OR'd with flags */
++ u8 maxlen_flags;
++ /* { prot[2:0], cache[3:0] } fields */
++ u8 cache_prot;
++ /* QoS (4 bitfields of 4 bits each for different panic levels) */
++ u16 qos;
++ /* For Panic mode: Output FIFO panic threshold */
++ u16 thresh;
++ /* For Panic mode: Output FIFO statistics throttle threshold */
++ u16 throttle;
++};
++
++struct pisp_fe_input_config {
++ u8 streaming;
++ u8 pad[3];
++ struct pisp_image_format_config format;
++ struct pisp_fe_input_axi_config axi;
++ /* Extra cycles delay before issuing each burst request */
++ u8 holdoff;
++ u8 pad2[3];
++};
++
++struct pisp_fe_output_config {
++ struct pisp_image_format_config format;
++ u16 ilines;
++ u8 pad[2];
++};
++
++struct pisp_fe_input_buffer_config {
++ u32 addr_lo;
++ u32 addr_hi;
++ u16 frame_id;
++ u16 pad;
++};
++
++#define PISP_FE_DECOMPAND_LUT_SIZE 65
++
++struct pisp_fe_decompand_config {
++ u16 lut[PISP_FE_DECOMPAND_LUT_SIZE];
++ u16 pad;
++};
++
++struct pisp_fe_dpc_config {
++ u8 coeff_level;
++ u8 coeff_range;
++ u8 coeff_range2;
++#define PISP_FE_DPC_FLAG_FOLDBACK 1
++#define PISP_FE_DPC_FLAG_VFLAG 2
++ u8 flags;
++};
++
++#define PISP_FE_LSC_LUT_SIZE 16
++
++struct pisp_fe_lsc_config {
++ u8 shift;
++ u8 pad0;
++ u16 scale;
++ u16 centre_x;
++ u16 centre_y;
++ u16 lut[PISP_FE_LSC_LUT_SIZE];
++};
++
++struct pisp_fe_rgby_config {
++ u16 gain_r;
++ u16 gain_g;
++ u16 gain_b;
++ u8 maxflag;
++ u8 pad;
++};
++
++struct pisp_fe_agc_stats_config {
++ u16 offset_x;
++ u16 offset_y;
++ u16 size_x;
++ u16 size_y;
++ /* each weight only 4 bits */
++ u8 weights[PISP_AGC_STATS_NUM_ZONES / 2];
++ u16 row_offset_x;
++ u16 row_offset_y;
++ u16 row_size_x;
++ u16 row_size_y;
++ u8 row_shift;
++ u8 float_shift;
++ u8 pad1[2];
++};
++
++struct pisp_fe_awb_stats_config {
++ u16 offset_x;
++ u16 offset_y;
++ u16 size_x;
++ u16 size_y;
++ u8 shift;
++ u8 pad[3];
++ u16 r_lo;
++ u16 r_hi;
++ u16 g_lo;
++ u16 g_hi;
++ u16 b_lo;
++ u16 b_hi;
++};
++
++struct pisp_fe_floating_stats_region {
++ u16 offset_x;
++ u16 offset_y;
++ u16 size_x;
++ u16 size_y;
++};
++
++struct pisp_fe_floating_stats_config {
++ struct pisp_fe_floating_stats_region
++ regions[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++#define PISP_FE_CDAF_NUM_WEIGHTS 8
++
++struct pisp_fe_cdaf_stats_config {
++ u16 noise_constant;
++ u16 noise_slope;
++ u16 offset_x;
++ u16 offset_y;
++ u16 size_x;
++ u16 size_y;
++ u16 skip_x;
++ u16 skip_y;
++ u32 mode;
++};
++
++struct pisp_fe_stats_buffer_config {
++ u32 addr_lo;
++ u32 addr_hi;
++};
++
++struct pisp_fe_crop_config {
++ u16 offset_x;
++ u16 offset_y;
++ u16 width;
++ u16 height;
++};
++
++enum pisp_fe_downscale_flags {
++ DOWNSCALE_BAYER =
++ 1, /* downscale the four Bayer components independently... */
++ DOWNSCALE_BIN =
++ 2 /* ...without trying to preserve their spatial relationship */
++};
++
++struct pisp_fe_downscale_config {
++ u8 xin;
++ u8 xout;
++ u8 yin;
++ u8 yout;
++ u8 flags; /* enum pisp_fe_downscale_flags */
++ u8 pad[3];
++ u16 output_width;
++ u16 output_height;
++};
++
++struct pisp_fe_output_buffer_config {
++ u32 addr_lo;
++ u32 addr_hi;
++};
++
++/* Each of the two output channels/branches: */
++struct pisp_fe_output_branch_config {
++ struct pisp_fe_crop_config crop;
++ struct pisp_fe_downscale_config downscale;
++ struct pisp_compress_config compress;
++ struct pisp_fe_output_config output;
++ u32 pad;
++};
++
++/* And finally one to rule them all: */
++struct pisp_fe_config {
++ /* I/O configuration: */
++ struct pisp_fe_stats_buffer_config stats_buffer;
++ struct pisp_fe_output_buffer_config output_buffer[PISP_FE_NUM_OUTPUTS];
++ struct pisp_fe_input_buffer_config input_buffer;
++ /* processing configuration: */
++ struct pisp_fe_global_config global;
++ struct pisp_fe_input_config input;
++ struct pisp_decompress_config decompress;
++ struct pisp_fe_decompand_config decompand;
++ struct pisp_bla_config bla;
++ struct pisp_fe_dpc_config dpc;
++ struct pisp_fe_crop_config stats_crop;
++ u32 spare1; /* placeholder for future decimate configuration */
++ struct pisp_bla_config blc;
++ struct pisp_fe_rgby_config rgby;
++ struct pisp_fe_lsc_config lsc;
++ struct pisp_fe_agc_stats_config agc_stats;
++ struct pisp_fe_awb_stats_config awb_stats;
++ struct pisp_fe_cdaf_stats_config cdaf_stats;
++ struct pisp_fe_floating_stats_config floating_stats;
++ struct pisp_fe_output_axi_config output_axi;
++ struct pisp_fe_output_branch_config ch[PISP_FE_NUM_OUTPUTS];
++ /* non-register fields: */
++ u32 dirty_flags; /* these use pisp_fe_enable */
++ u32 dirty_flags_extra; /* these use pisp_fe_dirty */
++};
++
++#endif /* _PISP_FE_CONFIG_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
+@@ -0,0 +1,62 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP Front End statistics definitions
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_STATISTICS_H_
++#define _PISP_FE_STATISTICS_H_
++
++#define PISP_FLOATING_STATS_NUM_ZONES 4
++#define PISP_AGC_STATS_NUM_BINS 1024
++#define PISP_AGC_STATS_SIZE 16
++#define PISP_AGC_STATS_NUM_ZONES (PISP_AGC_STATS_SIZE * PISP_AGC_STATS_SIZE)
++#define PISP_AGC_STATS_NUM_ROW_SUMS 512
++
++struct pisp_agc_statistics_zone {
++ u64 Y_sum;
++ u32 counted;
++ u32 pad;
++};
++
++struct pisp_agc_statistics {
++ u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS];
++ /*
++ * 32-bits per bin means an image (just less than) 16384x16384 pixels
++ * in size can weight every pixel from 0 to 15.
++ */
++ u32 histogram[PISP_AGC_STATS_NUM_BINS];
++ struct pisp_agc_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++#define PISP_AWB_STATS_SIZE 32
++#define PISP_AWB_STATS_NUM_ZONES (PISP_AWB_STATS_SIZE * PISP_AWB_STATS_SIZE)
++
++struct pisp_awb_statistics_zone {
++ u32 R_sum;
++ u32 G_sum;
++ u32 B_sum;
++ u32 counted;
++};
++
++struct pisp_awb_statistics {
++ struct pisp_awb_statistics_zone zones[PISP_AWB_STATS_NUM_ZONES];
++ struct pisp_awb_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++#define PISP_CDAF_STATS_SIZE 8
++#define PISP_CDAF_STATS_NUM_FOMS (PISP_CDAF_STATS_SIZE * PISP_CDAF_STATS_SIZE)
++
++struct pisp_cdaf_statistics {
++ u64 foms[PISP_CDAF_STATS_NUM_FOMS];
++ u64 floating[PISP_FLOATING_STATS_NUM_ZONES];
++};
++
++struct pisp_statistics {
++ struct pisp_awb_statistics awb;
++ struct pisp_agc_statistics agc;
++ struct pisp_cdaf_statistics cdaf;
++};
++
++#endif /* _PISP_FE_STATISTICS_H_ */
+--- /dev/null
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
+@@ -0,0 +1,144 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * RP1 PiSP Front End image definitions.
++ *
++ * Copyright (C) 2021 - Raspberry Pi Ltd.
++ *
++ */
++#ifndef _PISP_FE_TYPES_H_
++#define _PISP_FE_TYPES_H_
++
++/* This definition must match the format description in the hardware exactly! */
++struct pisp_image_format_config {
++ /* size in pixels */
++ u16 width, height;
++ /* must match struct pisp_image_format below */
++ u32 format;
++ s32 stride;
++ /* some planar image formats will need a second stride */
++ s32 stride2;
++};
++
++static_assert(sizeof(struct pisp_image_format_config) == 16);
++
++enum pisp_bayer_order {
++ /*
++ * Note how bayer_order&1 tells you if G is on the even pixels of the
++ * checkerboard or not, and bayer_order&2 tells you if R is on the even
++ * rows or is swapped with B. Note that if the top (of the 8) bits is
++ * set, this denotes a monochrome or greyscale image, and the lower bits
++ * should all be ignored.
++ */
++ PISP_BAYER_ORDER_RGGB = 0,
++ PISP_BAYER_ORDER_GBRG = 1,
++ PISP_BAYER_ORDER_BGGR = 2,
++ PISP_BAYER_ORDER_GRBG = 3,
++ PISP_BAYER_ORDER_GREYSCALE = 128
++};
++
++enum pisp_image_format {
++ /*
++ * Precise values are mostly tbd. Generally these will be portmanteau
++ * values comprising bit fields and flags. This format must be shared
++ * throughout the PiSP.
++ */
++ PISP_IMAGE_FORMAT_BPS_8 = 0x00000000,
++ PISP_IMAGE_FORMAT_BPS_10 = 0x00000001,
++ PISP_IMAGE_FORMAT_BPS_12 = 0x00000002,
++ PISP_IMAGE_FORMAT_BPS_16 = 0x00000003,
++ PISP_IMAGE_FORMAT_BPS_MASK = 0x00000003,
++
++ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED = 0x00000000,
++ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR = 0x00000010,
++ PISP_IMAGE_FORMAT_PLANARITY_PLANAR = 0x00000020,
++ PISP_IMAGE_FORMAT_PLANARITY_MASK = 0x00000030,
++
++ PISP_IMAGE_FORMAT_SAMPLING_444 = 0x00000000,
++ PISP_IMAGE_FORMAT_SAMPLING_422 = 0x00000100,
++ PISP_IMAGE_FORMAT_SAMPLING_420 = 0x00000200,
++ PISP_IMAGE_FORMAT_SAMPLING_MASK = 0x00000300,
++
++ PISP_IMAGE_FORMAT_ORDER_NORMAL = 0x00000000,
++ PISP_IMAGE_FORMAT_ORDER_SWAPPED = 0x00001000,
++
++ PISP_IMAGE_FORMAT_SHIFT_0 = 0x00000000,
++ PISP_IMAGE_FORMAT_SHIFT_1 = 0x00010000,
++ PISP_IMAGE_FORMAT_SHIFT_2 = 0x00020000,
++ PISP_IMAGE_FORMAT_SHIFT_3 = 0x00030000,
++ PISP_IMAGE_FORMAT_SHIFT_4 = 0x00040000,
++ PISP_IMAGE_FORMAT_SHIFT_5 = 0x00050000,
++ PISP_IMAGE_FORMAT_SHIFT_6 = 0x00060000,
++ PISP_IMAGE_FORMAT_SHIFT_7 = 0x00070000,
++ PISP_IMAGE_FORMAT_SHIFT_8 = 0x00080000,
++ PISP_IMAGE_FORMAT_SHIFT_MASK = 0x000f0000,
++
++ PISP_IMAGE_FORMAT_UNCOMPRESSED = 0x00000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MODE_1 = 0x01000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MODE_2 = 0x02000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MODE_3 = 0x03000000,
++ PISP_IMAGE_FORMAT_COMPRESSION_MASK = 0x03000000,
++
++ PISP_IMAGE_FORMAT_HOG_SIGNED = 0x04000000,
++ PISP_IMAGE_FORMAT_HOG_UNSIGNED = 0x08000000,
++ PISP_IMAGE_FORMAT_INTEGRAL_IMAGE = 0x10000000,
++ PISP_IMAGE_FORMAT_WALLPAPER_ROLL = 0x20000000,
++ PISP_IMAGE_FORMAT_THREE_CHANNEL = 0x40000000,
++
++ /* Lastly a few specific instantiations of the above. */
++ PISP_IMAGE_FORMAT_SINGLE_16 = PISP_IMAGE_FORMAT_BPS_16,
++ PISP_IMAGE_FORMAT_THREE_16 =
++ PISP_IMAGE_FORMAT_BPS_16 | PISP_IMAGE_FORMAT_THREE_CHANNEL
++};
++
++#define PISP_IMAGE_FORMAT_bps_8(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_8)
++#define PISP_IMAGE_FORMAT_bps_10(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_10)
++#define PISP_IMAGE_FORMAT_bps_12(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_12)
++#define PISP_IMAGE_FORMAT_bps_16(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) == PISP_IMAGE_FORMAT_BPS_16)
++#define PISP_IMAGE_FORMAT_bps(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) ? \
++ 8 + (2 << (((fmt) & PISP_IMAGE_FORMAT_BPS_MASK) - 1)) : \
++ 8)
++#define PISP_IMAGE_FORMAT_shift(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_SHIFT_MASK) / PISP_IMAGE_FORMAT_SHIFT_1)
++#define PISP_IMAGE_FORMAT_three_channel(fmt) \
++ ((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL)
++#define PISP_IMAGE_FORMAT_single_channel(fmt) \
++ (!((fmt) & PISP_IMAGE_FORMAT_THREE_CHANNEL))
++#define PISP_IMAGE_FORMAT_compressed(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_COMPRESSION_MASK) != \
++ PISP_IMAGE_FORMAT_UNCOMPRESSED)
++#define PISP_IMAGE_FORMAT_sampling_444(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \
++ PISP_IMAGE_FORMAT_SAMPLING_444)
++#define PISP_IMAGE_FORMAT_sampling_422(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \
++ PISP_IMAGE_FORMAT_SAMPLING_422)
++#define PISP_IMAGE_FORMAT_sampling_420(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_SAMPLING_MASK) == \
++ PISP_IMAGE_FORMAT_SAMPLING_420)
++#define PISP_IMAGE_FORMAT_order_normal(fmt) \
++ (!((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED))
++#define PISP_IMAGE_FORMAT_order_swapped(fmt) \
++ ((fmt) & PISP_IMAGE_FORMAT_ORDER_SWAPPED)
++#define PISP_IMAGE_FORMAT_interleaved(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \
++ PISP_IMAGE_FORMAT_PLANARITY_INTERLEAVED)
++#define PISP_IMAGE_FORMAT_semiplanar(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \
++ PISP_IMAGE_FORMAT_PLANARITY_SEMI_PLANAR)
++#define PISP_IMAGE_FORMAT_planar(fmt) \
++ (((fmt) & PISP_IMAGE_FORMAT_PLANARITY_MASK) == \
++ PISP_IMAGE_FORMAT_PLANARITY_PLANAR)
++#define PISP_IMAGE_FORMAT_wallpaper(fmt) \
++ ((fmt) & PISP_IMAGE_FORMAT_WALLPAPER_ROLL)
++#define PISP_IMAGE_FORMAT_HOG(fmt) \
++ ((fmt) & \
++ (PISP_IMAGE_FORMAT_HOG_SIGNED | PISP_IMAGE_FORMAT_HOG_UNSIGNED))
++
++#define PISP_WALLPAPER_WIDTH 128 // in bytes
++
++#endif /* _PISP_FE_TYPES_H_ */
--- /dev/null
+From 2be65d1fd1f7d3cf6f59b58b53e285400f04a160 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 15 Feb 2023 09:46:35 +0000
+Subject: [PATCH] dt-bindings: net: cdns,macb: AXI tuning properties
+
+Add optional properties to tune the AXI interface -
+cdns,aw2w-max-pipe, cdns,ar2r-max-pipe and cdns,use-aw2b-fill.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../devicetree/bindings/net/cdns,macb.yaml | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/Documentation/devicetree/bindings/net/cdns,macb.yaml
++++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml
+@@ -121,6 +121,22 @@ properties:
+ Node containing PHY children. If this node is not present, then PHYs will
+ be direct children.
+
++ cdns,aw2w-max-pipe:
++ $ref: /schemas/types.yaml#/definitions/uint32
++ description:
++ Maximum number of outstanding AXI write requests
++
++ cdns,ar2r-max-pipe:
++ $ref: /schemas/types.yaml#/definitions/uint32
++ description:
++ Maximum number of outstanding AXI read requests
++
++ cdns,use-aw2b-fill:
++ type: boolean
++ description:
++ If set, the maximum number of outstanding write transactions operates
++ between the AW to B AXI channel, instead of the AW to W AXI channel.
++
+ patternProperties:
+ "^ethernet-phy@[0-9a-f]$":
+ type: object
--- /dev/null
+From 9ef0615a5c5f93cb72af8df3a2dae6d23b106eb5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 21 Feb 2023 21:26:16 +0000
+Subject: [PATCH] ASoC: dwc: list all supported sample sizes
+
+The hardware configuration determines the maximum-supported sample size
+for each channel, but TCRx allows smaller sizes to be specified at run
+time. Include the smaller supported sizes in the formats array.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -448,9 +448,9 @@ static const u32 bus_widths[COMP_MAX_DAT
+ static const u32 formats[COMP_MAX_WORDSIZE] = {
+ SNDRV_PCM_FMTBIT_S16_LE,
+ SNDRV_PCM_FMTBIT_S16_LE,
+- SNDRV_PCM_FMTBIT_S24_LE,
+- SNDRV_PCM_FMTBIT_S24_LE,
+- SNDRV_PCM_FMTBIT_S32_LE,
++ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
++ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
++ SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ 0,
+ 0,
+ 0
--- /dev/null
+From 06f794e8cb227249e03893e4b4923ff58556eb60 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 4 Mar 2021 14:49:23 +0000
+Subject: [PATCH] ASoC: dwc: Support set_bclk_ratio
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -351,11 +351,46 @@ static int dw_i2s_set_fmt(struct snd_soc
+ return ret;
+ }
+
++static int dw_i2s_set_bclk_ratio(struct snd_soc_dai *cpu_dai,
++ unsigned int ratio)
++{
++ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
++ struct i2s_clk_config_data *config = &dev->config;
++
++ dev_err(dev->dev, "%s(%d)\n", __func__, ratio);
++ switch (ratio) {
++ case 32:
++ config->data_width = 16;
++ dev->ccr = 0x00;
++ dev->xfer_resolution = 0x02;
++ break;
++
++ case 48:
++ config->data_width = 24;
++ dev->ccr = 0x08;
++ dev->xfer_resolution = 0x04;
++ break;
++
++ case 64:
++ config->data_width = 32;
++ dev->ccr = 0x10;
++ dev->xfer_resolution = 0x05;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
++
++ return 0;
++}
++
+ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+ .hw_params = dw_i2s_hw_params,
+ .prepare = dw_i2s_prepare,
+ .trigger = dw_i2s_trigger,
+ .set_fmt = dw_i2s_set_fmt,
++ .set_bclk_ratio = dw_i2s_set_bclk_ratio,
+ };
+
+ #ifdef CONFIG_PM
--- /dev/null
+From b3b1177092d4d2ba6df74042d39aa42c5055f687 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 3 Jul 2023 09:08:16 +0100
+Subject: [PATCH] ASoC: dwc: Add DMACR handling
+
+Add control of the DMACR register, which is required for paced DMA
+(i.e. DREQ) support.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 13 ++++++++++---
+ sound/soc/dwc/local.h | 13 +++++++++++++
+ 2 files changed, 23 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -185,9 +185,9 @@ static void i2s_stop(struct dw_i2s_dev *
+
+ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+ {
+- u32 ch_reg;
+ struct i2s_clk_config_data *config = &dev->config;
+-
++ u32 ch_reg;
++ u32 dmacr = 0;
+
+ i2s_disable_channels(dev, stream);
+
+@@ -198,15 +198,22 @@ static void dw_i2s_config(struct dw_i2s_
+ i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
+ dev->fifo_th - 1);
+ i2s_write_reg(dev->i2s_base, TER(ch_reg), 1);
++ dmacr |= (DMACR_DMAEN_TXCH0 << ch_reg);
+ } else {
+ i2s_write_reg(dev->i2s_base, RCR(ch_reg),
+ dev->xfer_resolution);
+ i2s_write_reg(dev->i2s_base, RFCR(ch_reg),
+ dev->fifo_th - 1);
+ i2s_write_reg(dev->i2s_base, RER(ch_reg), 1);
++ dmacr |= (DMACR_DMAEN_RXCH0 << ch_reg);
+ }
+-
+ }
++ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
++ dmacr |= DMACR_DMAEN_TX;
++ else if (stream == SNDRV_PCM_STREAM_CAPTURE)
++ dmacr |= DMACR_DMAEN_RX;
++
++ i2s_write_reg(dev->i2s_base, DMACR, dmacr);
+ }
+
+ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
+--- a/sound/soc/dwc/local.h
++++ b/sound/soc/dwc/local.h
+@@ -25,6 +25,8 @@
+ #define RXFFR 0x014
+ #define TXFFR 0x018
+
++#define DMACR 0x200
++
+ /* Interrupt status register fields */
+ #define ISR_TXFO BIT(5)
+ #define ISR_TXFE BIT(4)
+@@ -47,6 +49,17 @@
+ #define RFF(x) (0x40 * x + 0x050)
+ #define TFF(x) (0x40 * x + 0x054)
+
++#define DMACR_DMAEN_TX BIT(17)
++#define DMACR_DMAEN_RX BIT(16)
++#define DMACR_DMAEN_TXCH3 BIT(11)
++#define DMACR_DMAEN_TXCH2 BIT(10)
++#define DMACR_DMAEN_TXCH1 BIT(9)
++#define DMACR_DMAEN_TXCH0 BIT(8)
++#define DMACR_DMAEN_RXCH3 BIT(3)
++#define DMACR_DMAEN_RXCH2 BIT(2)
++#define DMACR_DMAEN_RXCH1 BIT(1)
++#define DMACR_DMAEN_RXCH0 BIT(0)
++
+ /* I2SCOMPRegisters */
+ #define I2S_COMP_PARAM_2 0x01F0
+ #define I2S_COMP_PARAM_1 0x01F4
--- /dev/null
+From e6baee4502c0228c79408b047096a1259a84353f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 3 Jul 2023 10:14:43 +0100
+Subject: [PATCH] ASOC: dwc: Improve DMA shutdown
+
+Disabling the I2S interface with outstanding transfers prevents the
+DMAC from shutting down, so keep it partially active after a stop.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 72 ++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 64 insertions(+), 8 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -165,24 +165,26 @@ static void i2s_start(struct dw_i2s_dev
+ i2s_write_reg(dev->i2s_base, CER, 1);
+ }
+
+-static void i2s_stop(struct dw_i2s_dev *dev,
+- struct snd_pcm_substream *substream)
++static void i2s_pause(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream)
+ {
+
+ i2s_clear_irqs(dev, substream->stream);
+- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+- i2s_write_reg(dev->i2s_base, ITER, 0);
+- else
+- i2s_write_reg(dev->i2s_base, IRER, 0);
+
+ i2s_disable_irqs(dev, substream->stream, 8);
+
+ if (!dev->active) {
+ i2s_write_reg(dev->i2s_base, CER, 0);
+- i2s_write_reg(dev->i2s_base, IER, 0);
++ /* Keep the device enabled until the shutdown - do not clear IER */
+ }
+ }
+
++static void i2s_stop(struct dw_i2s_dev *dev, struct snd_pcm_substream *substream)
++{
++ i2s_clear_irqs(dev, substream->stream);
++
++ i2s_disable_irqs(dev, substream->stream, 8);
++}
++
+ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
+ {
+ struct i2s_clk_config_data *config = &dev->config;
+@@ -288,6 +290,55 @@ static int dw_i2s_hw_params(struct snd_p
+ return 0;
+ }
+
++static int dw_i2s_startup(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *cpu_dai)
++{
++ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
++ union dw_i2s_snd_dma_data *dma_data = NULL;
++ u32 dmacr;
++
++ dev_dbg(dev->dev, "%s(%s)\n", __func__, substream->name);
++ if (!(dev->capability & DWC_I2S_RECORD) &&
++ substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++ return -EINVAL;
++
++ if (!(dev->capability & DWC_I2S_PLAY) &&
++ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ return -EINVAL;
++
++ dw_i2s_config(dev, substream->stream);
++ dmacr = i2s_read_reg(dev->i2s_base, DMACR);
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ dma_data = &dev->play_dma_data;
++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++ dma_data = &dev->capture_dma_data;
++
++ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
++ i2s_write_reg(dev->i2s_base, DMACR, dmacr);
++
++ return 0;
++}
++
++static void dw_i2s_shutdown(struct snd_pcm_substream *substream,
++ struct snd_soc_dai *dai)
++{
++ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
++
++ dev_dbg(dev->dev, "%s(%s)\n", __func__, substream->name);
++ i2s_disable_channels(dev, substream->stream);
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ i2s_write_reg(dev->i2s_base, ITER, 0);
++ else
++ i2s_write_reg(dev->i2s_base, IRER, 0);
++
++ i2s_disable_irqs(dev, substream->stream, 8);
++
++ if (!dev->active) {
++ i2s_write_reg(dev->i2s_base, CER, 0);
++ i2s_write_reg(dev->i2s_base, IER, 0);
++ }
++}
++
+ static int dw_i2s_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+ {
+@@ -315,9 +366,12 @@ static int dw_i2s_trigger(struct snd_pcm
+ i2s_start(dev, substream);
+ break;
+
++ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
++ dev->active--;
++ i2s_pause(dev, substream);
++ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dev->active--;
+ i2s_stop(dev, substream);
+ break;
+@@ -394,6 +448,8 @@ static int dw_i2s_set_bclk_ratio(struct
+
+ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
+ .hw_params = dw_i2s_hw_params,
++ .startup = dw_i2s_startup,
++ .shutdown = dw_i2s_shutdown,
+ .prepare = dw_i2s_prepare,
+ .trigger = dw_i2s_trigger,
+ .set_fmt = dw_i2s_set_fmt,
--- /dev/null
+From 9c6694c24f26ea435165431d41c72451fadbd753 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 21 Jul 2023 12:07:16 +0100
+Subject: [PATCH] ASOC: dwc: Fix 16-bit audio handling
+
+IMO the Synopsys datasheet could be clearer in this area, but it seems
+that the DMA data ports (DMATX and DMARX) expect left and right samples
+in alternate writes; if a stereo pair is pushed in a single 32-bit
+write, the upper half is ignored, leading to double speed audio with a
+confused stereo image. Make sure the necessary changes happen by
+updating the DMA configuration data in the hw_params method.
+
+The set_bclk_ratio change was made at a time when it looked like it
+could be causing an error, but I think the division of responsibilities
+is clearer this way (and the kernel log clearer without the info-level
+message).
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -223,23 +223,34 @@ static int dw_i2s_hw_params(struct snd_p
+ {
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
+ struct i2s_clk_config_data *config = &dev->config;
++ union dw_i2s_snd_dma_data *dma_data = NULL;
+ int ret;
+
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ dma_data = &dev->play_dma_data;
++ else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++ dma_data = &dev->capture_dma_data;
++ else
++ return -1;
++
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ config->data_width = 16;
++ dma_data->dt.addr_width = 2;
+ dev->ccr = 0x00;
+ dev->xfer_resolution = 0x02;
+ break;
+
+ case SNDRV_PCM_FORMAT_S24_LE:
+ config->data_width = 24;
++ dma_data->dt.addr_width = 4;
+ dev->ccr = 0x08;
+ dev->xfer_resolution = 0x04;
+ break;
+
+ case SNDRV_PCM_FORMAT_S32_LE:
+ config->data_width = 32;
++ dma_data->dt.addr_width = 4;
+ dev->ccr = 0x10;
+ dev->xfer_resolution = 0x05;
+ break;
+@@ -418,24 +429,21 @@ static int dw_i2s_set_bclk_ratio(struct
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+ struct i2s_clk_config_data *config = &dev->config;
+
+- dev_err(dev->dev, "%s(%d)\n", __func__, ratio);
++ dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio);
++ if (ratio < config->data_width * 2)
++ return -EINVAL;
++
+ switch (ratio) {
+ case 32:
+- config->data_width = 16;
+ dev->ccr = 0x00;
+- dev->xfer_resolution = 0x02;
+ break;
+
+ case 48:
+- config->data_width = 24;
+ dev->ccr = 0x08;
+- dev->xfer_resolution = 0x04;
+ break;
+
+ case 64:
+- config->data_width = 32;
+ dev->ccr = 0x10;
+- dev->xfer_resolution = 0x05;
+ break;
+ default:
+ return -EINVAL;
--- /dev/null
+From f476db1b71e8b82e5299168f963a2fefb7a395e2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 1 Sep 2023 14:07:48 +0100
+Subject: [PATCH] ASoC: bcm: Remove dependency on BCM2835 I2S
+
+These soundcard drivers don't rely on a specific I2S interface, so
+remove the dependency declarations.
+
+See: https://github.com/raspberrypi/linux-2712/issues/111
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/Kconfig | 40 +---------------------------------------
+ 1 file changed, 1 insertion(+), 39 deletions(-)
+
+--- a/sound/soc/bcm/Kconfig
++++ b/sound/soc/bcm/Kconfig
+@@ -29,13 +29,11 @@ config SND_BCM63XX_I2S_WHISTLER
+
+ config SND_BCM2708_SOC_CHIPDIP_DAC
+ tristate "Support for the ChipDip DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ help
+ Say Y or M if you want to add support for the ChipDip DAC soundcard
+
+ config SND_BCM2708_SOC_GOOGLEVOICEHAT_SOUNDCARD
+ tristate "Support for Google voiceHAT soundcard"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_VOICEHAT
+ select SND_RPI_SIMPLE_SOUNDCARD
+ help
+@@ -43,7 +41,6 @@ config SND_BCM2708_SOC_GOOGLEVOICEHAT_SO
+
+ config SND_BCM2708_SOC_HIFIBERRY_DAC
+ tristate "Support for HifiBerry DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM5102A
+ select SND_RPI_SIMPLE_SOUNDCARD
+ help
+@@ -51,7 +48,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DAC
+
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+ tristate "Support for HifiBerry DAC+"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x
+ select SND_SOC_TPA6130A2
+ select COMMON_CLK_HIFIBERRY_DACPRO
+@@ -60,7 +56,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSHD
+ tristate "Support for HifiBerry DAC+ HD"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM179X_I2C
+ select COMMON_CLK_HIFIBERRY_DACPLUSHD
+ help
+@@ -68,7 +63,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADC
+ tristate "Support for HifiBerry DAC+ADC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
+ select SND_SOC_DMIC
+ select COMMON_CLK_HIFIBERRY_DACPRO
+@@ -77,7 +71,6 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSADCPRO
+ tristate "Support for HifiBerry DAC+ADC PRO"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
+ select SND_SOC_PCM186X_I2C
+ select SND_SOC_TPA6130A2
+@@ -87,29 +80,25 @@ config SND_BCM2708_SOC_HIFIBERRY_DACPLUS
+
+ config SND_BCM2708_SOC_HIFIBERRY_DACPLUSDSP
+ tristate "Support for HifiBerry DAC+DSP"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_RPI_SIMPLE_SOUNDCARD
+ help
+ Say Y or M if you want to add support for HifiBerry DSP-DAC.
+
+ config SND_BCM2708_SOC_HIFIBERRY_DIGI
+ tristate "Support for HifiBerry Digi"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8804
+ help
+ Say Y or M if you want to add support for HifiBerry Digi S/PDIF output board.
+
+ config SND_BCM2708_SOC_HIFIBERRY_AMP
+ tristate "Support for the HifiBerry Amp"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_TAS5713
+ select SND_RPI_SIMPLE_SOUNDCARD
+ help
+ Say Y or M if you want to add support for the HifiBerry Amp amplifier board.
+
+- config SND_BCM2708_SOC_PIFI_40
++config SND_BCM2708_SOC_PIFI_40
+ tristate "Support for the PiFi-40 amp"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_TAS571X
+ select SND_PIFI_40
+ help
+@@ -117,7 +106,6 @@ config SND_BCM2708_SOC_HIFIBERRY_AMP
+
+ config SND_BCM2708_SOC_RPI_CIRRUS
+ tristate "Support for Cirrus Logic Audio Card"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM5102
+ select SND_SOC_WM8804
+ help
+@@ -126,7 +114,6 @@ config SND_BCM2708_SOC_RPI_CIRRUS
+
+ config SND_BCM2708_SOC_RPI_DAC
+ tristate "Support for RPi-DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM1794A
+ select SND_RPI_SIMPLE_SOUNDCARD
+ help
+@@ -134,14 +121,12 @@ config SND_BCM2708_SOC_RPI_DAC
+
+ config SND_BCM2708_SOC_RPI_PROTO
+ tristate "Support for Rpi-PROTO"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8731_I2C
+ help
+ Say Y or M if you want to add support for Audio Codec Board PROTO (WM8731).
+
+ config SND_BCM2708_SOC_JUSTBOOM_BOTH
+ tristate "Support for simultaneous JustBoom Digi and JustBoom DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8804
+ select SND_SOC_PCM512x
+ help
+@@ -153,14 +138,12 @@ config SND_BCM2708_SOC_JUSTBOOM_BOTH
+
+ config SND_BCM2708_SOC_JUSTBOOM_DAC
+ tristate "Support for JustBoom DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x
+ help
+ Say Y or M if you want to add support for JustBoom DAC.
+
+ config SND_BCM2708_SOC_JUSTBOOM_DIGI
+ tristate "Support for JustBoom Digi"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8804
+ select SND_RPI_WM8804_SOUNDCARD
+ help
+@@ -168,21 +151,18 @@ config SND_BCM2708_SOC_JUSTBOOM_DIGI
+
+ config SND_BCM2708_SOC_IQAUDIO_CODEC
+ tristate "Support for IQaudIO-CODEC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_DA7213
+ help
+ Say Y or M if you want to add support for IQaudIO-CODEC.
+
+ config SND_BCM2708_SOC_IQAUDIO_DAC
+ tristate "Support for IQaudIO-DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
+ help
+ Say Y or M if you want to add support for IQaudIO-DAC.
+
+ config SND_BCM2708_SOC_IQAUDIO_DIGI
+ tristate "Support for IQAudIO Digi"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8804
+ select SND_RPI_WM8804_SOUNDCARD
+ help
+@@ -190,14 +170,12 @@ config SND_BCM2708_SOC_IQAUDIO_DIGI
+
+ config SND_BCM2708_SOC_I_SABRE_Q2M
+ tristate "Support for Audiophonics I-Sabre Q2M DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_I_SABRE_CODEC
+ help
+ Say Y or M if you want to add support for Audiophonics I-SABRE Q2M DAC
+
+ config SND_BCM2708_SOC_ADAU1977_ADC
+ tristate "Support for ADAU1977 ADC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_ADAU1977_I2C
+ select SND_RPI_SIMPLE_SOUNDCARD
+ help
+@@ -205,35 +183,30 @@ config SND_BCM2708_SOC_ADAU1977_ADC
+
+ config SND_AUDIOINJECTOR_PI_SOUNDCARD
+ tristate "Support for audioinjector.net Pi add on soundcard"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8731_I2C
+ help
+ Say Y or M if you want to add support for audioinjector.net Pi Hat
+
+ config SND_AUDIOINJECTOR_OCTO_SOUNDCARD
+ tristate "Support for audioinjector.net Octo channel (Hat) soundcard"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_CS42XX8_I2C
+ help
+ Say Y or M if you want to add support for audioinjector.net octo add on
+
+ config SND_AUDIOINJECTOR_ISOLATED_SOUNDCARD
+ tristate "Support for audioinjector.net isolated DAC and ADC soundcard"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_CS4271_I2C
+ help
+ Say Y or M if you want to add support for audioinjector.net isolated soundcard
+
+ config SND_AUDIOSENSE_PI
+ tristate "Support for AudioSense Add-On Soundcard"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_TLV320AIC32X4_I2C
+ help
+ Say Y or M if you want to add support for tlv320aic32x4 add-on
+
+ config SND_DIGIDAC1_SOUNDCARD
+ tristate "Support for Red Rocks Audio DigiDAC1"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8804
+ select SND_SOC_WM8741
+ help
+@@ -241,35 +214,30 @@ config SND_DIGIDAC1_SOUNDCARD
+
+ config SND_BCM2708_SOC_DIONAUDIO_LOCO
+ tristate "Support for Dion Audio LOCO DAC-AMP"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM5102a
+ help
+ Say Y or M if you want to add support for Dion Audio LOCO.
+
+ config SND_BCM2708_SOC_DIONAUDIO_LOCO_V2
+ tristate "Support for Dion Audio LOCO-V2 DAC-AMP"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM5122
+ help
+ Say Y or M if you want to add support for Dion Audio LOCO-V2.
+
+ config SND_BCM2708_SOC_ALLO_PIANO_DAC
+ tristate "Support for Allo Piano DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
+ help
+ Say Y or M if you want to add support for Allo Piano DAC.
+
+ config SND_BCM2708_SOC_ALLO_PIANO_DAC_PLUS
+ tristate "Support for Allo Piano DAC Plus"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
+ help
+ Say Y or M if you want to add support for Allo Piano DAC Plus.
+
+ config SND_BCM2708_SOC_ALLO_BOSS_DAC
+ tristate "Support for Allo Boss DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_PCM512x_I2C
+ select COMMON_CLK_HIFIBERRY_DACPRO
+ help
+@@ -277,7 +245,6 @@ config SND_BCM2708_SOC_ALLO_BOSS_DAC
+
+ config SND_BCM2708_SOC_ALLO_BOSS2_DAC
+ tristate "Support for Allo Boss2 DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ depends on I2C
+ select REGMAP_I2C
+ select SND_AUDIO_GRAPH_CARD
+@@ -286,7 +253,6 @@ config SND_BCM2708_SOC_ALLO_BOSS2_DAC
+
+ config SND_BCM2708_SOC_ALLO_DIGIONE
+ tristate "Support for Allo DigiOne"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_WM8804
+ select SND_RPI_WM8804_SOUNDCARD
+ help
+@@ -294,7 +260,6 @@ config SND_BCM2708_SOC_ALLO_DIGIONE
+
+ config SND_BCM2708_SOC_ALLO_KATANA_DAC
+ tristate "Support for Allo Katana DAC"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ depends on I2C
+ select REGMAP_I2C
+ select SND_AUDIO_GRAPH_CARD
+@@ -303,14 +268,12 @@ config SND_BCM2708_SOC_ALLO_KATANA_DAC
+
+ config SND_BCM2708_SOC_FE_PI_AUDIO
+ tristate "Support for Fe-Pi-Audio"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_SGTL5000
+ help
+ Say Y or M if you want to add support for Fe-Pi-Audio.
+
+ config SND_PISOUND
+ tristate "Support for Blokas Labs pisound"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_RAWMIDI
+ help
+ Say Y or M if you want to add support for Blokas Labs pisound.
+@@ -328,7 +291,6 @@ config SND_RPI_WM8804_SOUNDCARD
+
+ config SND_DACBERRY400
+ tristate "Support for DACBERRY400 Soundcard"
+- depends on SND_BCM2708_SOC_I2S || SND_BCM2835_SOC_I2S
+ select SND_SOC_TLV320AIC3X_I2C
+ help
+ Say Y or M if you want to add support for tlv320aic3x add-on
--- /dev/null
+From cad3c92ff0c1a5fa539d08b695b0f6b326924890 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Mar 2023 18:04:42 +0000
+Subject: [PATCH] hwmon: Add RP1 ADC and temperature driver
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/hwmon/Kconfig | 7 +
+ drivers/hwmon/Makefile | 1 +
+ drivers/hwmon/rp1-adc.c | 301 ++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 309 insertions(+)
+ create mode 100644 drivers/hwmon/rp1-adc.c
+
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -2331,6 +2331,13 @@ config SENSORS_INTEL_M10_BMC_HWMON
+ sensors monitor various telemetry data of different components on the
+ card, e.g. board temperature, FPGA core temperature/voltage/current.
+
++config SENSORS_RP1_ADC
++ tristate "RP1 ADC and temperature sensor driver"
++ depends on MFD_RP1
++ help
++ Say yes here to enable support for the voltage and temperature
++ sensors of the Raspberry Pi RP1 peripheral chip.
++
+ if ACPI
+
+ comment "ACPI drivers"
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -173,6 +173,7 @@ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591
+ obj-$(CONFIG_SENSORS_POWR1220) += powr1220.o
+ obj-$(CONFIG_SENSORS_PWM_FAN) += pwm-fan.o
+ obj-$(CONFIG_SENSORS_RASPBERRYPI_HWMON) += raspberrypi-hwmon.o
++obj-$(CONFIG_SENSORS_RP1_ADC) += rp1-adc.o
+ obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o
+ obj-$(CONFIG_SENSORS_SBTSI) += sbtsi_temp.o
+ obj-$(CONFIG_SENSORS_SBRMI) += sbrmi.o
+--- /dev/null
++++ b/drivers/hwmon/rp1-adc.c
+@@ -0,0 +1,301 @@
++// SPDX-License-Identifier: GPL-2.0+
++/*
++ * Driver for the RP1 ADC and temperature sensor
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ */
++
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/io.h>
++#include <linux/module.h>
++#include <linux/mod_devicetable.h>
++#include <linux/platform_device.h>
++#include <linux/regulator/consumer.h>
++
++#define MODULE_NAME "rp1-adc"
++
++#define RP1_ADC_CS 0x00
++#define RP1_ADC_RESULT 0x04
++#define RP1_ADC_FCS 0x08
++#define RP1_ADC_FIFO 0x0c
++#define RP1_ADC_DIV 0x10
++
++#define RP1_ADC_INTR 0x14
++#define RP1_ADC_INTE 0x18
++#define RP1_ADC_INTF 0x1c
++#define RP1_ADC_INTS 0x20
++
++#define RP1_ADC_RWTYPE_SET 0x2000
++#define RP1_ADC_RWTYPE_CLR 0x3000
++
++#define RP1_ADC_CS_RROBIN_MASK 0x1f
++#define RP1_ADC_CS_RROBIN_SHIFT 16
++#define RP1_ADC_CS_AINSEL_MASK 0x7
++#define RP1_ADC_CS_AINSEL_SHIFT 12
++#define RP1_ADC_CS_ERR_STICKY 0x400
++#define RP1_ADC_CS_ERR 0x200
++#define RP1_ADC_CS_READY 0x100
++#define RP1_ADC_CS_START_MANY 0x8
++#define RP1_ADC_CS_START_ONCE 0x4
++#define RP1_ADC_CS_TS_EN 0x2
++#define RP1_ADC_CS_EN 0x1
++
++#define RP1_ADC_FCS_THRESH_MASK 0xf
++#define RP1_ADC_FCS_THRESH_SHIFT 24
++#define RP1_ADC_FCS_LEVEL_MASK 0xf
++#define RP1_ADC_FCS_LEVEL_SHIFT 16
++#define RP1_ADC_FCS_OVER 0x800
++#define RP1_ADC_FCS_UNDER 0x400
++#define RP1_ADC_FCS_FULL 0x200
++#define RP1_ADC_FCS_EMPTY 0x100
++#define RP1_ADC_FCS_DREQ_EN 0x8
++#define RP1_ADC_FCS_ERR 0x4
++#define RP1_ADC_FCS_SHIFR 0x2
++#define RP1_ADC_FCS_EN 0x1
++
++#define RP1_ADC_FIFO_ERR 0x8000
++#define RP1_ADC_FIFO_VAL_MASK 0xfff
++
++#define RP1_ADC_DIV_INT_MASK 0xffff
++#define RP1_ADC_DIV_INT_SHIFT 8
++#define RP1_ADC_DIV_FRAC_MASK 0xff
++#define RP1_ADC_DIV_FRAC_SHIFT 0
++
++struct rp1_adc_data {
++ void __iomem *base;
++ spinlock_t lock;
++ struct device *hwmon_dev;
++ int vref_mv;
++};
++
++static int rp1_adc_ready_wait(struct rp1_adc_data *data)
++{
++ int retries = 10;
++
++ while (retries && !(readl(data->base + RP1_ADC_CS) & RP1_ADC_CS_READY))
++ retries--;
++
++ return retries ? 0 : -EIO;
++}
++
++static int rp1_adc_read(struct rp1_adc_data *data,
++ struct device_attribute *devattr, unsigned int *val)
++{
++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++ int channel = attr->index;
++ int ret;
++
++ spin_lock(&data->lock);
++
++ writel(RP1_ADC_CS_AINSEL_MASK << RP1_ADC_CS_AINSEL_SHIFT,
++ data->base + RP1_ADC_RWTYPE_CLR + RP1_ADC_CS);
++ writel(channel << RP1_ADC_CS_AINSEL_SHIFT,
++ data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
++ writel(RP1_ADC_CS_START_ONCE,
++ data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
++
++ ret = rp1_adc_ready_wait(data);
++ if (!ret)
++ *val = readl(data->base + RP1_ADC_RESULT);
++
++ spin_unlock(&data->lock);
++
++ return ret;
++}
++
++static int rp1_adc_to_mv(struct rp1_adc_data *data, unsigned int val)
++{
++ return ((u64)data->vref_mv * val) / 0xfff;
++}
++
++static ssize_t rp1_adc_show(struct device *dev,
++ struct device_attribute *devattr,
++ char *buf)
++{
++ struct rp1_adc_data *data = dev_get_drvdata(dev);
++ unsigned int val;
++ int ret;
++
++ ret = rp1_adc_read(data, devattr, &val);
++ if (ret)
++ return ret;
++
++ return sprintf(buf, "%d\n", rp1_adc_to_mv(data, val));
++}
++
++static ssize_t rp1_adc_temp_show(struct device *dev,
++ struct device_attribute *devattr,
++ char *buf)
++{
++ struct rp1_adc_data *data = dev_get_drvdata(dev);
++ unsigned int val;
++ int ret, mv, mc;
++
++ writel(RP1_ADC_CS_TS_EN,
++ data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
++ ret = rp1_adc_read(data, devattr, &val);
++ if (ret)
++ return ret;
++
++ mv = rp1_adc_to_mv(data, val);
++
++ /* T = 27 - (ADC_voltage - 0.706)/0.001721 */
++
++ mc = 27000 - DIV_ROUND_CLOSEST((mv - 706) * (s64)1000000, 1721);
++
++ return sprintf(buf, "%d\n", mc);
++}
++
++static ssize_t rp1_adc_raw_show(struct device *dev,
++ struct device_attribute *devattr,
++ char *buf)
++{
++ struct rp1_adc_data *data = dev_get_drvdata(dev);
++ unsigned int val;
++ int ret = rp1_adc_read(data, devattr, &val);
++
++ if (ret)
++ return ret;
++
++ return sprintf(buf, "%u\n", val);
++}
++
++static ssize_t rp1_adc_temp_raw_show(struct device *dev,
++ struct device_attribute *devattr,
++ char *buf)
++{
++ struct rp1_adc_data *data = dev_get_drvdata(dev);
++ unsigned int val;
++ int ret = rp1_adc_read(data, devattr, &val);
++
++ if (ret)
++ return ret;
++
++ return sprintf(buf, "%u\n", val);
++}
++
++static SENSOR_DEVICE_ATTR_RO(in1_input, rp1_adc, 0);
++static SENSOR_DEVICE_ATTR_RO(in2_input, rp1_adc, 1);
++static SENSOR_DEVICE_ATTR_RO(in3_input, rp1_adc, 2);
++static SENSOR_DEVICE_ATTR_RO(in4_input, rp1_adc, 3);
++static SENSOR_DEVICE_ATTR_RO(temp1_input, rp1_adc_temp, 4);
++static SENSOR_DEVICE_ATTR_RO(in1_raw, rp1_adc_raw, 0);
++static SENSOR_DEVICE_ATTR_RO(in2_raw, rp1_adc_raw, 1);
++static SENSOR_DEVICE_ATTR_RO(in3_raw, rp1_adc_raw, 2);
++static SENSOR_DEVICE_ATTR_RO(in4_raw, rp1_adc_raw, 3);
++static SENSOR_DEVICE_ATTR_RO(temp1_raw, rp1_adc_temp_raw, 4);
++
++static struct attribute *rp1_adc_attrs[] = {
++ &sensor_dev_attr_in1_input.dev_attr.attr,
++ &sensor_dev_attr_in2_input.dev_attr.attr,
++ &sensor_dev_attr_in3_input.dev_attr.attr,
++ &sensor_dev_attr_in4_input.dev_attr.attr,
++ &sensor_dev_attr_temp1_input.dev_attr.attr,
++ &sensor_dev_attr_in1_raw.dev_attr.attr,
++ &sensor_dev_attr_in2_raw.dev_attr.attr,
++ &sensor_dev_attr_in3_raw.dev_attr.attr,
++ &sensor_dev_attr_in4_raw.dev_attr.attr,
++ &sensor_dev_attr_temp1_raw.dev_attr.attr,
++ NULL
++};
++
++static umode_t rp1_adc_is_visible(struct kobject *kobj,
++ struct attribute *attr, int index)
++{
++ return 0444;
++}
++
++static const struct attribute_group rp1_adc_group = {
++ .attrs = rp1_adc_attrs,
++ .is_visible = rp1_adc_is_visible,
++};
++__ATTRIBUTE_GROUPS(rp1_adc);
++
++static int __init rp1_adc_probe(struct platform_device *pdev)
++{
++ struct rp1_adc_data *data;
++ struct regulator *reg;
++ struct clk *clk;
++ int vref_uv, ret;
++
++ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++
++ spin_lock_init(&data->lock);
++
++ data->base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(data->base))
++ return PTR_ERR(data->base);
++
++ platform_set_drvdata(pdev, data);
++
++ clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(clk))
++ return -ENODEV;
++
++ clk_set_rate(clk, 50000000);
++ clk_prepare_enable(clk);
++
++ reg = devm_regulator_get(&pdev->dev, "vref");
++ if (IS_ERR(reg))
++ return PTR_ERR(reg);
++
++ vref_uv = regulator_get_voltage(reg);
++ data->vref_mv = DIV_ROUND_CLOSEST(vref_uv, 1000);
++
++ data->hwmon_dev =
++ devm_hwmon_device_register_with_groups(&pdev->dev,
++ "rp1_adc",
++ data,
++ rp1_adc_groups);
++ if (IS_ERR(data->hwmon_dev)) {
++ ret = PTR_ERR(data->hwmon_dev);
++ dev_err(&pdev->dev, "hwmon_device_register failed with %d.\n", ret);
++ goto err_register;
++ }
++
++ /* Disable interrupts */
++ writel(0, data->base + RP1_ADC_INTE);
++
++ /* Enable the block, clearing any sticky error */
++ writel(RP1_ADC_CS_EN | RP1_ADC_CS_ERR_STICKY, data->base + RP1_ADC_CS);
++
++ return 0;
++
++err_register:
++ sysfs_remove_group(&pdev->dev.kobj, &rp1_adc_group);
++
++ return ret;
++}
++
++static int rp1_adc_remove(struct platform_device *pdev)
++{
++ struct rp1_adc_data *data = platform_get_drvdata(pdev);
++
++ hwmon_device_unregister(data->hwmon_dev);
++
++ return 0;
++}
++
++static const struct of_device_id rp1_adc_dt_ids[] = {
++ { .compatible = "raspberrypi,rp1-adc", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, rp1_adc_dt_ids);
++
++static struct platform_driver rp1_adc_driver = {
++ .remove = rp1_adc_remove,
++ .driver = {
++ .name = MODULE_NAME,
++ .of_match_table = rp1_adc_dt_ids,
++ },
++};
++
++module_platform_driver_probe(rp1_adc_driver, rp1_adc_probe);
++
++MODULE_DESCRIPTION("RP1 ADC driver");
++MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 0c7aeb96fd3ab68011ba6c24239c501190890308 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Mar 2023 14:27:58 +0000
+Subject: [PATCH] mfd: bcm2835-pm: Add support for BCM2712
+
+BCM2712 lacks the "asb" and "rpivid_asb" register ranges, but still
+requires the use of the bcm2835-power driver to reset the V3D block.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/mfd/bcm2835-pm.c | 28 +++++++++++++++++++---------
+ 1 file changed, 19 insertions(+), 9 deletions(-)
+
+--- a/drivers/mfd/bcm2835-pm.c
++++ b/drivers/mfd/bcm2835-pm.c
+@@ -69,12 +69,30 @@ static int bcm2835_pm_get_pdata(struct p
+ return 0;
+ }
+
++static const struct of_device_id bcm2835_pm_of_match[] = {
++ { .compatible = "brcm,bcm2835-pm-wdt", },
++ { .compatible = "brcm,bcm2835-pm", },
++ { .compatible = "brcm,bcm2711-pm", },
++ { .compatible = "brcm,bcm2712-pm", .data = (const void *)1},
++ {},
++};
++MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
++
+ static int bcm2835_pm_probe(struct platform_device *pdev)
+ {
++ const struct of_device_id *of_id;
+ struct device *dev = &pdev->dev;
+ struct bcm2835_pm *pm;
++ bool is_2712;
+ int ret;
+
++ of_id = of_match_node(bcm2835_pm_of_match, pdev->dev.of_node);
++ if (!of_id) {
++ dev_err(&pdev->dev, "Failed to match compatible string\n");
++ return -EINVAL;
++ }
++ is_2712 = !!of_id->data;
++
+ pm = devm_kzalloc(dev, sizeof(*pm), GFP_KERNEL);
+ if (!pm)
+ return -ENOMEM;
+@@ -97,21 +115,13 @@ static int bcm2835_pm_probe(struct platf
+ * bcm2835-pm binding as the key for whether we can reference
+ * the full PM register range and support power domains.
+ */
+- if (pm->asb)
++ if (pm->asb || is_2712)
+ return devm_mfd_add_devices(dev, -1, bcm2835_power_devs,
+ ARRAY_SIZE(bcm2835_power_devs),
+ NULL, 0, NULL);
+ return 0;
+ }
+
+-static const struct of_device_id bcm2835_pm_of_match[] = {
+- { .compatible = "brcm,bcm2835-pm-wdt", },
+- { .compatible = "brcm,bcm2835-pm", },
+- { .compatible = "brcm,bcm2711-pm", },
+- {},
+-};
+-MODULE_DEVICE_TABLE(of, bcm2835_pm_of_match);
+-
+ static struct platform_driver bcm2835_pm_driver = {
+ .probe = bcm2835_pm_probe,
+ .driver = {
--- /dev/null
+From 9cf85a95eeb239a079a3485bd1d0447431bdc7f1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Mar 2023 14:42:48 +0000
+Subject: [PATCH] soc: bcm: bcm2835-power: Add support for BCM2712
+
+BCM2712 has a PM block but neither ASB nor RPIVID_ASB. Use the absence
+of the "asb" register range to indicate BCM2712 and its different PM
+register range.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/soc/bcm/bcm2835-power.c | 29 +++++++++++++++++++----------
+ 1 file changed, 19 insertions(+), 10 deletions(-)
+
+--- a/drivers/soc/bcm/bcm2835-power.c
++++ b/drivers/soc/bcm/bcm2835-power.c
+@@ -79,6 +79,7 @@
+ #define PM_IMAGE 0x108
+ #define PM_GRAFX 0x10c
+ #define PM_PROC 0x110
++#define PM_GRAFX_2712 0x304
+ #define PM_ENAB BIT(12)
+ #define PM_ISPRSTN BIT(8)
+ #define PM_H264RSTN BIT(7)
+@@ -381,6 +382,9 @@ static int bcm2835_power_pd_power_on(str
+ return bcm2835_power_power_on(pd, PM_GRAFX);
+
+ case BCM2835_POWER_DOMAIN_GRAFX_V3D:
++ if (!power->asb)
++ return bcm2835_asb_power_on(pd, PM_GRAFX_2712,
++ 0, 0, PM_V3DRSTN);
+ return bcm2835_asb_power_on(pd, PM_GRAFX,
+ ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
+ PM_V3DRSTN);
+@@ -447,6 +451,9 @@ static int bcm2835_power_pd_power_off(st
+ return bcm2835_power_power_off(pd, PM_GRAFX);
+
+ case BCM2835_POWER_DOMAIN_GRAFX_V3D:
++ if (!power->asb)
++ return bcm2835_asb_power_off(pd, PM_GRAFX_2712,
++ 0, 0, PM_V3DRSTN);
+ return bcm2835_asb_power_off(pd, PM_GRAFX,
+ ASB_V3D_M_CTRL, ASB_V3D_S_CTRL,
+ PM_V3DRSTN);
+@@ -642,19 +649,21 @@ static int bcm2835_power_probe(struct pl
+ power->asb = pm->asb;
+ power->rpivid_asb = pm->rpivid_asb;
+
+- id = readl(power->asb + ASB_AXI_BRDG_ID);
+- if (id != BCM2835_BRDG_ID /* "BRDG" */) {
+- dev_err(dev, "ASB register ID returned 0x%08x\n", id);
+- return -ENODEV;
+- }
+-
+- if (power->rpivid_asb) {
+- id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
++ if (power->asb) {
++ id = readl(power->asb + ASB_AXI_BRDG_ID);
+ if (id != BCM2835_BRDG_ID /* "BRDG" */) {
+- dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
+- id);
++ dev_err(dev, "ASB register ID returned 0x%08x\n", id);
+ return -ENODEV;
+ }
++
++ if (power->rpivid_asb) {
++ id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID);
++ if (id != BCM2835_BRDG_ID /* "BRDG" */) {
++ dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n",
++ id);
++ return -ENODEV;
++ }
++ }
+ }
+
+ power->pd_xlate.domains = devm_kcalloc(dev,
--- /dev/null
+From 380c336af070edf85826abbb0057bf92a03ec466 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 1 Mar 2023 17:57:11 +0000
+Subject: [PATCH] drivers: spi: Fix spi-gpio to correctly implement
+ sck-idle-input
+
+Formerly, if configured using DT, CS GPIOs were driven from spi.c
+and it was possible for CS to be asserted (low) *before* starting
+to drive SCK. CS GPIOs have been brought under control of this
+driver in both ACPI and DT cases, with a fixup for GPIO polarity.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/spi/spi-gpio.c | 74 +++++++++++++++++++++++++++++-------------
+ 1 file changed, 51 insertions(+), 23 deletions(-)
+
+--- a/drivers/spi/spi-gpio.c
++++ b/drivers/spi/spi-gpio.c
+@@ -37,6 +37,7 @@ struct spi_gpio {
+ struct gpio_desc *mosi;
+ bool sck_idle_input;
+ struct gpio_desc **cs_gpios;
++ bool cs_dont_invert;
+ };
+
+ /*----------------------------------------------------------------------*/
+@@ -233,12 +234,18 @@ static void spi_gpio_chipselect(struct s
+ gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
+ }
+
+- /* Drive chip select line, if we have one */
++ /*
++ * Drive chip select line, if we have one.
++ * SPI chip selects are normally active-low, but when
++ * cs_dont_invert is set, we assume their polarity is
++ * controlled by the GPIO, and write '1' to assert.
++ */
+ if (spi_gpio->cs_gpios) {
+ struct gpio_desc *cs = spi_gpio->cs_gpios[spi->chip_select];
++ int val = ((spi->mode & SPI_CS_HIGH) || spi_gpio->cs_dont_invert) ?
++ is_active : !is_active;
+
+- /* SPI chip selects are normally active-low */
+- gpiod_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
++ gpiod_set_value_cansleep(cs, val);
+ }
+
+ if (spi_gpio->sck_idle_input && !is_active)
+@@ -254,12 +261,14 @@ static int spi_gpio_setup(struct spi_dev
+ /*
+ * The CS GPIOs have already been
+ * initialized from the descriptor lookup.
++ * Here we set them to the non-asserted state.
+ */
+ if (spi_gpio->cs_gpios) {
+ cs = spi_gpio->cs_gpios[spi->chip_select];
+ if (!spi->controller_state && cs)
+ status = gpiod_direction_output(cs,
+- !(spi->mode & SPI_CS_HIGH));
++ !((spi->mode & SPI_CS_HIGH) ||
++ spi_gpio->cs_dont_invert));
+ }
+
+ if (!status)
+@@ -336,6 +345,38 @@ static int spi_gpio_request(struct devic
+ return PTR_ERR_OR_ZERO(spi_gpio->sck);
+ }
+
++/*
++ * In order to implement "sck-idle-input" (which requires SCK
++ * direction and CS level to be switched in a particular order),
++ * we need to control GPIO chip selects from within this driver.
++ */
++
++static int spi_gpio_probe_get_cs_gpios(struct device *dev,
++ struct spi_master *master,
++ bool gpio_defines_polarity)
++{
++ int i;
++ struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
++
++ spi_gpio->cs_dont_invert = gpio_defines_polarity;
++ spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
++ sizeof(*spi_gpio->cs_gpios),
++ GFP_KERNEL);
++ if (!spi_gpio->cs_gpios)
++ return -ENOMEM;
++
++ for (i = 0; i < master->num_chipselect; i++) {
++ spi_gpio->cs_gpios[i] =
++ devm_gpiod_get_index(dev, "cs", i,
++ gpio_defines_polarity ?
++ GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
++ if (IS_ERR(spi_gpio->cs_gpios[i]))
++ return PTR_ERR(spi_gpio->cs_gpios[i]);
++ }
++
++ return 0;
++}
++
+ #ifdef CONFIG_OF
+ static const struct of_device_id spi_gpio_dt_ids[] = {
+ { .compatible = "spi-gpio" },
+@@ -346,10 +387,12 @@ MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids)
+ static int spi_gpio_probe_dt(struct platform_device *pdev,
+ struct spi_master *master)
+ {
+- master->dev.of_node = pdev->dev.of_node;
+- master->use_gpio_descriptors = true;
++ struct device *dev = &pdev->dev;
+
+- return 0;
++ master->dev.of_node = dev->of_node;
++ master->num_chipselect = gpiod_count(dev, "cs");
++
++ return spi_gpio_probe_get_cs_gpios(dev, master, true);
+ }
+ #else
+ static inline int spi_gpio_probe_dt(struct platform_device *pdev,
+@@ -364,8 +407,6 @@ static int spi_gpio_probe_pdata(struct p
+ {
+ struct device *dev = &pdev->dev;
+ struct spi_gpio_platform_data *pdata = dev_get_platdata(dev);
+- struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
+- int i;
+
+ #ifdef GENERIC_BITBANG
+ if (!pdata || !pdata->num_chipselect)
+@@ -377,20 +418,7 @@ static int spi_gpio_probe_pdata(struct p
+ */
+ master->num_chipselect = pdata->num_chipselect ?: 1;
+
+- spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
+- sizeof(*spi_gpio->cs_gpios),
+- GFP_KERNEL);
+- if (!spi_gpio->cs_gpios)
+- return -ENOMEM;
+-
+- for (i = 0; i < master->num_chipselect; i++) {
+- spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
+- GPIOD_OUT_HIGH);
+- if (IS_ERR(spi_gpio->cs_gpios[i]))
+- return PTR_ERR(spi_gpio->cs_gpios[i]);
+- }
+-
+- return 0;
++ return spi_gpio_probe_get_cs_gpios(dev, master, false);
+ }
+
+ static int spi_gpio_probe(struct platform_device *pdev)
--- /dev/null
+From 586f87307e75552292cfc6c76b81cd38d5ec31e2 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Mon, 4 Sep 2023 10:57:47 +0100
+Subject: [PATCH] spi: spi-gpio: Implement spidelay when requested bit rate <=
+ 1 Mbps
+
+Formerly the delay was omitted as bit-banged SPI seldom achieved
+even one Mbit/s; but some modern platforms can run faster, and
+some SPI devices may need to be clocked slower.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/spi/spi-gpio.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+--- a/drivers/spi/spi-gpio.c
++++ b/drivers/spi/spi-gpio.c
+@@ -11,12 +11,12 @@
+ #include <linux/gpio/consumer.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
++#include <linux/delay.h>
+
+ #include <linux/spi/spi.h>
+ #include <linux/spi/spi_bitbang.h>
+ #include <linux/spi/spi_gpio.h>
+
+-
+ /*
+ * This bitbanging SPI master driver should help make systems usable
+ * when a native hardware SPI engine is not available, perhaps because
+@@ -111,12 +111,18 @@ static inline int getmiso(const struct s
+ }
+
+ /*
+- * NOTE: this clocks "as fast as we can". It "should" be a function of the
+- * requested device clock. Software overhead means we usually have trouble
+- * reaching even one Mbit/sec (except when we can inline bitops), so for now
+- * we'll just assume we never need additional per-bit slowdowns.
++ * Generic bit-banged GPIO SPI might free-run at something in the range
++ * 1Mbps ~ 10Mbps (depending on the platform), and some SPI devices may
++ * need to be clocked at a lower rate. ndelay() is often implemented by
++ * udelay() with rounding up, so do the delay only for nsecs >= 500
++ * (<= 1Mbps). The conditional test adds a small overhead.
+ */
+-#define spidelay(nsecs) do {} while (0)
++
++static inline void spidelay(unsigned long nsecs)
++{
++ if (nsecs >= 500)
++ ndelay(nsecs);
++}
+
+ #include "spi-bitbang-txrx.h"
+
--- /dev/null
+From 3f949caeef21269afc67dd62ae9826204f215934 Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+Date: Thu, 2 Mar 2023 11:49:46 +0100
+Subject: [PATCH] drm/v3d: fix up register addresses for V3D 7.x
+
+v2: fix kernel panic with debug-fs interface to list registers
+---
+ drivers/gpu/drm/v3d/v3d_debugfs.c | 177 +++++++++++++++++-------------
+ drivers/gpu/drm/v3d/v3d_gem.c | 3 +
+ drivers/gpu/drm/v3d/v3d_irq.c | 47 ++++----
+ drivers/gpu/drm/v3d/v3d_regs.h | 51 ++++++++-
+ drivers/gpu/drm/v3d/v3d_sched.c | 41 ++++---
+ 5 files changed, 204 insertions(+), 115 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_debugfs.c
++++ b/drivers/gpu/drm/v3d/v3d_debugfs.c
+@@ -13,69 +13,83 @@
+ #include "v3d_drv.h"
+ #include "v3d_regs.h"
+
+-#define REGDEF(reg) { reg, #reg }
++#define REGDEF(min_ver, max_ver, reg) { min_ver, max_ver, reg, #reg }
+ struct v3d_reg_def {
++ u32 min_ver;
++ u32 max_ver;
+ u32 reg;
+ const char *name;
+ };
+
+ static const struct v3d_reg_def v3d_hub_reg_defs[] = {
+- REGDEF(V3D_HUB_AXICFG),
+- REGDEF(V3D_HUB_UIFCFG),
+- REGDEF(V3D_HUB_IDENT0),
+- REGDEF(V3D_HUB_IDENT1),
+- REGDEF(V3D_HUB_IDENT2),
+- REGDEF(V3D_HUB_IDENT3),
+- REGDEF(V3D_HUB_INT_STS),
+- REGDEF(V3D_HUB_INT_MSK_STS),
+-
+- REGDEF(V3D_MMU_CTL),
+- REGDEF(V3D_MMU_VIO_ADDR),
+- REGDEF(V3D_MMU_VIO_ID),
+- REGDEF(V3D_MMU_DEBUG_INFO),
++ REGDEF(33, 42, V3D_HUB_AXICFG),
++ REGDEF(33, 71, V3D_HUB_UIFCFG),
++ REGDEF(33, 71, V3D_HUB_IDENT0),
++ REGDEF(33, 71, V3D_HUB_IDENT1),
++ REGDEF(33, 71, V3D_HUB_IDENT2),
++ REGDEF(33, 71, V3D_HUB_IDENT3),
++ REGDEF(33, 71, V3D_HUB_INT_STS),
++ REGDEF(33, 71, V3D_HUB_INT_MSK_STS),
++
++ REGDEF(33, 71, V3D_MMU_CTL),
++ REGDEF(33, 71, V3D_MMU_VIO_ADDR),
++ REGDEF(33, 71, V3D_MMU_VIO_ID),
++ REGDEF(33, 71, V3D_MMU_DEBUG_INFO),
++
++ REGDEF(71, 71, V3D_V7_GMP_STATUS),
++ REGDEF(71, 71, V3D_V7_GMP_CFG),
++ REGDEF(71, 71, V3D_V7_GMP_VIO_ADDR),
+ };
+
+ static const struct v3d_reg_def v3d_gca_reg_defs[] = {
+- REGDEF(V3D_GCA_SAFE_SHUTDOWN),
+- REGDEF(V3D_GCA_SAFE_SHUTDOWN_ACK),
++ REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN),
++ REGDEF(33, 33, V3D_GCA_SAFE_SHUTDOWN_ACK),
+ };
+
+ static const struct v3d_reg_def v3d_core_reg_defs[] = {
+- REGDEF(V3D_CTL_IDENT0),
+- REGDEF(V3D_CTL_IDENT1),
+- REGDEF(V3D_CTL_IDENT2),
+- REGDEF(V3D_CTL_MISCCFG),
+- REGDEF(V3D_CTL_INT_STS),
+- REGDEF(V3D_CTL_INT_MSK_STS),
+- REGDEF(V3D_CLE_CT0CS),
+- REGDEF(V3D_CLE_CT0CA),
+- REGDEF(V3D_CLE_CT0EA),
+- REGDEF(V3D_CLE_CT1CS),
+- REGDEF(V3D_CLE_CT1CA),
+- REGDEF(V3D_CLE_CT1EA),
+-
+- REGDEF(V3D_PTB_BPCA),
+- REGDEF(V3D_PTB_BPCS),
+-
+- REGDEF(V3D_GMP_STATUS),
+- REGDEF(V3D_GMP_CFG),
+- REGDEF(V3D_GMP_VIO_ADDR),
+-
+- REGDEF(V3D_ERR_FDBGO),
+- REGDEF(V3D_ERR_FDBGB),
+- REGDEF(V3D_ERR_FDBGS),
+- REGDEF(V3D_ERR_STAT),
++ REGDEF(33, 71, V3D_CTL_IDENT0),
++ REGDEF(33, 71, V3D_CTL_IDENT1),
++ REGDEF(33, 71, V3D_CTL_IDENT2),
++ REGDEF(33, 71, V3D_CTL_MISCCFG),
++ REGDEF(33, 71, V3D_CTL_INT_STS),
++ REGDEF(33, 71, V3D_CTL_INT_MSK_STS),
++ REGDEF(33, 71, V3D_CLE_CT0CS),
++ REGDEF(33, 71, V3D_CLE_CT0CA),
++ REGDEF(33, 71, V3D_CLE_CT0EA),
++ REGDEF(33, 71, V3D_CLE_CT1CS),
++ REGDEF(33, 71, V3D_CLE_CT1CA),
++ REGDEF(33, 71, V3D_CLE_CT1EA),
++
++ REGDEF(33, 71, V3D_PTB_BPCA),
++ REGDEF(33, 71, V3D_PTB_BPCS),
++
++ REGDEF(33, 41, V3D_GMP_STATUS),
++ REGDEF(33, 41, V3D_GMP_CFG),
++ REGDEF(33, 41, V3D_GMP_VIO_ADDR),
++
++ REGDEF(33, 71, V3D_ERR_FDBGO),
++ REGDEF(33, 71, V3D_ERR_FDBGB),
++ REGDEF(33, 71, V3D_ERR_FDBGS),
++ REGDEF(33, 71, V3D_ERR_STAT),
+ };
+
+ static const struct v3d_reg_def v3d_csd_reg_defs[] = {
+- REGDEF(V3D_CSD_STATUS),
+- REGDEF(V3D_CSD_CURRENT_CFG0),
+- REGDEF(V3D_CSD_CURRENT_CFG1),
+- REGDEF(V3D_CSD_CURRENT_CFG2),
+- REGDEF(V3D_CSD_CURRENT_CFG3),
+- REGDEF(V3D_CSD_CURRENT_CFG4),
+- REGDEF(V3D_CSD_CURRENT_CFG5),
+- REGDEF(V3D_CSD_CURRENT_CFG6),
++ REGDEF(41, 71, V3D_CSD_STATUS),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG0),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG1),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG2),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG3),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG4),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG5),
++ REGDEF(41, 41, V3D_CSD_CURRENT_CFG6),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG0),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG1),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG2),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG3),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG4),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG5),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG6),
++ REGDEF(71, 71, V3D_V7_CSD_CURRENT_CFG7),
+ };
+
+ static int v3d_v3d_debugfs_regs(struct seq_file *m, void *unused)
+@@ -86,38 +100,41 @@ static int v3d_v3d_debugfs_regs(struct s
+ int i, core;
+
+ for (i = 0; i < ARRAY_SIZE(v3d_hub_reg_defs); i++) {
+- seq_printf(m, "%s (0x%04x): 0x%08x\n",
+- v3d_hub_reg_defs[i].name, v3d_hub_reg_defs[i].reg,
+- V3D_READ(v3d_hub_reg_defs[i].reg));
++ const struct v3d_reg_def *def = &v3d_hub_reg_defs[i];
++
++ if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
++ seq_printf(m, "%s (0x%04x): 0x%08x\n",
++ def->name, def->reg, V3D_READ(def->reg));
++ }
+ }
+
+- if (v3d->ver < 41) {
+- for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) {
++ for (i = 0; i < ARRAY_SIZE(v3d_gca_reg_defs); i++) {
++ const struct v3d_reg_def *def = &v3d_gca_reg_defs[i];
++
++ if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
+ seq_printf(m, "%s (0x%04x): 0x%08x\n",
+- v3d_gca_reg_defs[i].name,
+- v3d_gca_reg_defs[i].reg,
+- V3D_GCA_READ(v3d_gca_reg_defs[i].reg));
++ def->name, def->reg, V3D_GCA_READ(def->reg));
+ }
+ }
+
+ for (core = 0; core < v3d->cores; core++) {
+ for (i = 0; i < ARRAY_SIZE(v3d_core_reg_defs); i++) {
+- seq_printf(m, "core %d %s (0x%04x): 0x%08x\n",
+- core,
+- v3d_core_reg_defs[i].name,
+- v3d_core_reg_defs[i].reg,
+- V3D_CORE_READ(core,
+- v3d_core_reg_defs[i].reg));
++ const struct v3d_reg_def *def = &v3d_core_reg_defs[i];
++
++ if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
++ seq_printf(m, "core %d %s (0x%04x): 0x%08x\n",
++ core, def->name, def->reg,
++ V3D_CORE_READ(core, def->reg));
++ }
+ }
+
+- if (v3d_has_csd(v3d)) {
+- for (i = 0; i < ARRAY_SIZE(v3d_csd_reg_defs); i++) {
++ for (i = 0; i < ARRAY_SIZE(v3d_csd_reg_defs); i++) {
++ const struct v3d_reg_def *def = &v3d_csd_reg_defs[i];
++
++ if (v3d->ver >= def->min_ver && v3d->ver <= def->max_ver) {
+ seq_printf(m, "core %d %s (0x%04x): 0x%08x\n",
+- core,
+- v3d_csd_reg_defs[i].name,
+- v3d_csd_reg_defs[i].reg,
+- V3D_CORE_READ(core,
+- v3d_csd_reg_defs[i].reg));
++ core, def->name, def->reg,
++ V3D_CORE_READ(core, def->reg));
+ }
+ }
+ }
+@@ -148,8 +165,10 @@ static int v3d_v3d_debugfs_ident(struct
+ str_yes_no(ident2 & V3D_HUB_IDENT2_WITH_MMU));
+ seq_printf(m, "TFU: %s\n",
+ str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TFU));
+- seq_printf(m, "TSY: %s\n",
+- str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY));
++ if (v3d->ver <= 42) {
++ seq_printf(m, "TSY: %s\n",
++ str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_TSY));
++ }
+ seq_printf(m, "MSO: %s\n",
+ str_yes_no(ident1 & V3D_HUB_IDENT1_WITH_MSO));
+ seq_printf(m, "L3C: %s (%dkb)\n",
+@@ -178,10 +197,14 @@ static int v3d_v3d_debugfs_ident(struct
+ seq_printf(m, " QPUs: %d\n", nslc * qups);
+ seq_printf(m, " Semaphores: %d\n",
+ V3D_GET_FIELD(ident1, V3D_IDENT1_NSEM));
+- seq_printf(m, " BCG int: %d\n",
+- (ident2 & V3D_IDENT2_BCG_INT) != 0);
+- seq_printf(m, " Override TMU: %d\n",
+- (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0);
++ if (v3d->ver <= 42) {
++ seq_printf(m, " BCG int: %d\n",
++ (ident2 & V3D_IDENT2_BCG_INT) != 0);
++ }
++ if (v3d->ver < 40) {
++ seq_printf(m, " Override TMU: %d\n",
++ (misccfg & V3D_MISCCFG_OVRTMUOUT) != 0);
++ }
+ }
+
+ return 0;
+@@ -289,8 +312,10 @@ static int v3d_measure_clock(struct seq_
+ int measure_ms = 1000;
+
+ if (v3d->ver >= 40) {
++ int cycle_count_reg = v3d->ver < 71 ?
++ V3D_PCTR_CYCLE_COUNT : V3D_V7_PCTR_CYCLE_COUNT;
+ V3D_CORE_WRITE(core, V3D_V4_PCTR_0_SRC_0_3,
+- V3D_SET_FIELD(V3D_PCTR_CYCLE_COUNT,
++ V3D_SET_FIELD(cycle_count_reg,
+ V3D_PCTR_S0));
+ V3D_CORE_WRITE(core, V3D_V4_PCTR_0_CLR, 1);
+ V3D_CORE_WRITE(core, V3D_V4_PCTR_0_EN, 1);
+--- a/drivers/gpu/drm/v3d/v3d_gem.c
++++ b/drivers/gpu/drm/v3d/v3d_gem.c
+@@ -88,6 +88,9 @@ v3d_init_hw_state(struct v3d_dev *v3d)
+ static void
+ v3d_idle_axi(struct v3d_dev *v3d, int core)
+ {
++ if (v3d->ver >= 71)
++ return;
++
+ V3D_CORE_WRITE(core, V3D_GMP_CFG, V3D_GMP_CFG_STOP_REQ);
+
+ if (wait_for((V3D_CORE_READ(core, V3D_GMP_STATUS) &
+--- a/drivers/gpu/drm/v3d/v3d_irq.c
++++ b/drivers/gpu/drm/v3d/v3d_irq.c
+@@ -20,16 +20,17 @@
+ #include "v3d_regs.h"
+ #include "v3d_trace.h"
+
+-#define V3D_CORE_IRQS ((u32)(V3D_INT_OUTOMEM | \
+- V3D_INT_FLDONE | \
+- V3D_INT_FRDONE | \
+- V3D_INT_CSDDONE | \
+- V3D_INT_GMPV))
+-
+-#define V3D_HUB_IRQS ((u32)(V3D_HUB_INT_MMU_WRV | \
+- V3D_HUB_INT_MMU_PTI | \
+- V3D_HUB_INT_MMU_CAP | \
+- V3D_HUB_INT_TFUC))
++#define V3D_CORE_IRQS(ver) ((u32)(V3D_INT_OUTOMEM | \
++ V3D_INT_FLDONE | \
++ V3D_INT_FRDONE | \
++ (ver < 71 ? V3D_INT_CSDDONE : V3D_V7_INT_CSDDONE) | \
++ (ver < 71 ? V3D_INT_GMPV : 0)))
++
++#define V3D_HUB_IRQS(ver) ((u32)(V3D_HUB_INT_MMU_WRV | \
++ V3D_HUB_INT_MMU_PTI | \
++ V3D_HUB_INT_MMU_CAP | \
++ V3D_HUB_INT_TFUC | \
++ (ver >= 71 ? V3D_V7_HUB_INT_GMPV : 0)))
+
+ static irqreturn_t
+ v3d_hub_irq(int irq, void *arg);
+@@ -118,7 +119,8 @@ v3d_irq(int irq, void *arg)
+ status = IRQ_HANDLED;
+ }
+
+- if (intsts & V3D_INT_CSDDONE) {
++ if ((v3d->ver < 71 && (intsts & V3D_INT_CSDDONE)) ||
++ (v3d->ver >= 71 && (intsts & V3D_V7_INT_CSDDONE))) {
+ struct v3d_fence *fence =
+ to_v3d_fence(v3d->csd_job->base.irq_fence);
+ v3d->gpu_queue_stats[V3D_CSD].last_exec_end = local_clock();
+@@ -131,7 +133,7 @@ v3d_irq(int irq, void *arg)
+ /* We shouldn't be triggering these if we have GMP in
+ * always-allowed mode.
+ */
+- if (intsts & V3D_INT_GMPV)
++ if (v3d->ver < 71 && (intsts & V3D_INT_GMPV))
+ dev_err(v3d->drm.dev, "GMP violation\n");
+
+ /* V3D 4.2 wires the hub and core IRQs together, so if we &
+@@ -205,6 +207,11 @@ v3d_hub_irq(int irq, void *arg)
+ status = IRQ_HANDLED;
+ }
+
++ if (v3d->ver >= 71 && intsts & V3D_V7_HUB_INT_GMPV) {
++ dev_err(v3d->drm.dev, "GMP Violation\n");
++ status = IRQ_HANDLED;
++ }
++
+ return status;
+ }
+
+@@ -219,8 +226,8 @@ v3d_irq_init(struct v3d_dev *v3d)
+ * for us.
+ */
+ for (core = 0; core < v3d->cores; core++)
+- V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS);
+- V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS);
++ V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
++ V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
+
+ irq1 = platform_get_irq_optional(v3d_to_pdev(v3d), 1);
+ if (irq1 == -EPROBE_DEFER)
+@@ -264,12 +271,12 @@ v3d_irq_enable(struct v3d_dev *v3d)
+
+ /* Enable our set of interrupts, masking out any others. */
+ for (core = 0; core < v3d->cores; core++) {
+- V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS);
+- V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_CLR, V3D_CORE_IRQS);
++ V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_SET, ~V3D_CORE_IRQS(v3d->ver));
++ V3D_CORE_WRITE(core, V3D_CTL_INT_MSK_CLR, V3D_CORE_IRQS(v3d->ver));
+ }
+
+- V3D_WRITE(V3D_HUB_INT_MSK_SET, ~V3D_HUB_IRQS);
+- V3D_WRITE(V3D_HUB_INT_MSK_CLR, V3D_HUB_IRQS);
++ V3D_WRITE(V3D_HUB_INT_MSK_SET, ~V3D_HUB_IRQS(v3d->ver));
++ V3D_WRITE(V3D_HUB_INT_MSK_CLR, V3D_HUB_IRQS(v3d->ver));
+ }
+
+ void
+@@ -284,8 +291,8 @@ v3d_irq_disable(struct v3d_dev *v3d)
+
+ /* Clear any pending interrupts we might have left. */
+ for (core = 0; core < v3d->cores; core++)
+- V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS);
+- V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS);
++ V3D_CORE_WRITE(core, V3D_CTL_INT_CLR, V3D_CORE_IRQS(v3d->ver));
++ V3D_WRITE(V3D_HUB_INT_CLR, V3D_HUB_IRQS(v3d->ver));
+
+ cancel_work_sync(&v3d->overflow_mem_work);
+ }
+--- a/drivers/gpu/drm/v3d/v3d_regs.h
++++ b/drivers/gpu/drm/v3d/v3d_regs.h
+@@ -57,6 +57,7 @@
+ #define V3D_HUB_INT_MSK_STS 0x0005c
+ #define V3D_HUB_INT_MSK_SET 0x00060
+ #define V3D_HUB_INT_MSK_CLR 0x00064
++# define V3D_V7_HUB_INT_GMPV BIT(6)
+ # define V3D_HUB_INT_MMU_WRV BIT(5)
+ # define V3D_HUB_INT_MMU_PTI BIT(4)
+ # define V3D_HUB_INT_MMU_CAP BIT(3)
+@@ -64,6 +65,7 @@
+ # define V3D_HUB_INT_TFUC BIT(1)
+ # define V3D_HUB_INT_TFUF BIT(0)
+
++/* GCA registers only exist in V3D < 41 */
+ #define V3D_GCA_CACHE_CTRL 0x0000c
+ # define V3D_GCA_CACHE_CTRL_FLUSH BIT(0)
+
+@@ -87,6 +89,7 @@
+ # define V3D_TOP_GR_BRIDGE_SW_INIT_1_V3D_CLK_108_SW_INIT BIT(0)
+
+ #define V3D_TFU_CS 0x00400
++#define V3D_V7_TFU_CS 0x00700
+ /* Stops current job, empties input fifo. */
+ # define V3D_TFU_CS_TFURST BIT(31)
+ # define V3D_TFU_CS_CVTCT_MASK V3D_MASK(23, 16)
+@@ -96,6 +99,7 @@
+ # define V3D_TFU_CS_BUSY BIT(0)
+
+ #define V3D_TFU_SU 0x00404
++#define V3D_V7_TFU_SU 0x00704
+ /* Interrupt when FINTTHR input slots are free (0 = disabled) */
+ # define V3D_TFU_SU_FINTTHR_MASK V3D_MASK(13, 8)
+ # define V3D_TFU_SU_FINTTHR_SHIFT 8
+@@ -107,38 +111,53 @@
+ # define V3D_TFU_SU_THROTTLE_SHIFT 0
+
+ #define V3D_TFU_ICFG 0x00408
++#define V3D_V7_TFU_ICFG 0x00708
+ /* Interrupt when the conversion is complete. */
+ # define V3D_TFU_ICFG_IOC BIT(0)
+
+ /* Input Image Address */
+ #define V3D_TFU_IIA 0x0040c
++#define V3D_V7_TFU_IIA 0x0070c
+ /* Input Chroma Address */
+ #define V3D_TFU_ICA 0x00410
++#define V3D_V7_TFU_ICA 0x00710
+ /* Input Image Stride */
+ #define V3D_TFU_IIS 0x00414
++#define V3D_V7_TFU_IIS 0x00714
+ /* Input Image U-Plane Address */
+ #define V3D_TFU_IUA 0x00418
++#define V3D_V7_TFU_IUA 0x00718
++/* Image output config (VD 7.x only) */
++#define V3D_V7_TFU_IOC 0x0071c
+ /* Output Image Address */
+ #define V3D_TFU_IOA 0x0041c
++#define V3D_V7_TFU_IOA 0x00720
+ /* Image Output Size */
+ #define V3D_TFU_IOS 0x00420
++#define V3D_V7_TFU_IOS 0x00724
+ /* TFU YUV Coefficient 0 */
+ #define V3D_TFU_COEF0 0x00424
+-/* Use these regs instead of the defaults. */
++#define V3D_V7_TFU_COEF0 0x00728
++/* Use these regs instead of the defaults (V3D 4.x only) */
+ # define V3D_TFU_COEF0_USECOEF BIT(31)
+ /* TFU YUV Coefficient 1 */
+ #define V3D_TFU_COEF1 0x00428
++#define V3D_V7_TFU_COEF1 0x0072c
+ /* TFU YUV Coefficient 2 */
+ #define V3D_TFU_COEF2 0x0042c
++#define V3D_V7_TFU_COEF2 0x00730
+ /* TFU YUV Coefficient 3 */
+ #define V3D_TFU_COEF3 0x00430
++#define V3D_V7_TFU_COEF3 0x00734
+
++/* V3D 4.x only */
+ #define V3D_TFU_CRC 0x00434
+
+ /* Per-MMU registers. */
+
+ #define V3D_MMUC_CONTROL 0x01000
+ # define V3D_MMUC_CONTROL_CLEAR BIT(3)
++# define V3D_V7_MMUC_CONTROL_CLEAR BIT(11)
+ # define V3D_MMUC_CONTROL_FLUSHING BIT(2)
+ # define V3D_MMUC_CONTROL_FLUSH BIT(1)
+ # define V3D_MMUC_CONTROL_ENABLE BIT(0)
+@@ -246,7 +265,6 @@
+
+ #define V3D_CTL_L2TCACTL 0x00030
+ # define V3D_L2TCACTL_TMUWCF BIT(8)
+-# define V3D_L2TCACTL_L2T_NO_WM BIT(4)
+ /* Invalidates cache lines. */
+ # define V3D_L2TCACTL_FLM_FLUSH 0
+ /* Removes cachelines without writing dirty lines back. */
+@@ -268,7 +286,9 @@
+ # define V3D_INT_QPU_MASK V3D_MASK(27, 16)
+ # define V3D_INT_QPU_SHIFT 16
+ # define V3D_INT_CSDDONE BIT(7)
++# define V3D_V7_INT_CSDDONE BIT(6)
+ # define V3D_INT_PCTR BIT(6)
++# define V3D_V7_INT_PCTR BIT(5)
+ # define V3D_INT_GMPV BIT(5)
+ # define V3D_INT_TRFB BIT(4)
+ # define V3D_INT_SPILLUSE BIT(3)
+@@ -350,14 +370,19 @@
+ #define V3D_V4_PCTR_0_SRC_X(x) (V3D_V4_PCTR_0_SRC_0_3 + \
+ 4 * (x))
+ # define V3D_PCTR_S0_MASK V3D_MASK(6, 0)
++# define V3D_V7_PCTR_S0_MASK V3D_MASK(7, 0)
+ # define V3D_PCTR_S0_SHIFT 0
+ # define V3D_PCTR_S1_MASK V3D_MASK(14, 8)
++# define V3D_V7_PCTR_S1_MASK V3D_MASK(15, 8)
+ # define V3D_PCTR_S1_SHIFT 8
+ # define V3D_PCTR_S2_MASK V3D_MASK(22, 16)
++# define V3D_V7_PCTR_S2_MASK V3D_MASK(23, 16)
+ # define V3D_PCTR_S2_SHIFT 16
+ # define V3D_PCTR_S3_MASK V3D_MASK(30, 24)
++# define V3D_V7_PCTR_S3_MASK V3D_MASK(31, 24)
+ # define V3D_PCTR_S3_SHIFT 24
+ # define V3D_PCTR_CYCLE_COUNT 32
++# define V3D_V7_PCTR_CYCLE_COUNT 0
+
+ /* Output values of the counters. */
+ #define V3D_PCTR_0_PCTR0 0x00680
+@@ -365,6 +390,7 @@
+ #define V3D_PCTR_0_PCTRX(x) (V3D_PCTR_0_PCTR0 + \
+ 4 * (x))
+ #define V3D_GMP_STATUS 0x00800
++#define V3D_V7_GMP_STATUS 0x00600
+ # define V3D_GMP_STATUS_GMPRST BIT(31)
+ # define V3D_GMP_STATUS_WR_COUNT_MASK V3D_MASK(30, 24)
+ # define V3D_GMP_STATUS_WR_COUNT_SHIFT 24
+@@ -378,12 +404,14 @@
+ # define V3D_GMP_STATUS_VIO BIT(0)
+
+ #define V3D_GMP_CFG 0x00804
++#define V3D_V7_GMP_CFG 0x00604
+ # define V3D_GMP_CFG_LBURSTEN BIT(3)
+ # define V3D_GMP_CFG_PGCRSEN BIT()
+ # define V3D_GMP_CFG_STOP_REQ BIT(1)
+ # define V3D_GMP_CFG_PROT_ENABLE BIT(0)
+
+ #define V3D_GMP_VIO_ADDR 0x00808
++#define V3D_V7_GMP_VIO_ADDR 0x00608
+ #define V3D_GMP_VIO_TYPE 0x0080c
+ #define V3D_GMP_TABLE_ADDR 0x00810
+ #define V3D_GMP_CLEAR_LOAD 0x00814
+@@ -399,24 +427,28 @@
+ # define V3D_CSD_STATUS_HAVE_QUEUED_DISPATCH BIT(0)
+
+ #define V3D_CSD_QUEUED_CFG0 0x00904
++#define V3D_V7_CSD_QUEUED_CFG0 0x00930
+ # define V3D_CSD_QUEUED_CFG0_NUM_WGS_X_MASK V3D_MASK(31, 16)
+ # define V3D_CSD_QUEUED_CFG0_NUM_WGS_X_SHIFT 16
+ # define V3D_CSD_QUEUED_CFG0_WG_X_OFFSET_MASK V3D_MASK(15, 0)
+ # define V3D_CSD_QUEUED_CFG0_WG_X_OFFSET_SHIFT 0
+
+ #define V3D_CSD_QUEUED_CFG1 0x00908
++#define V3D_V7_CSD_QUEUED_CFG1 0x00934
+ # define V3D_CSD_QUEUED_CFG1_NUM_WGS_Y_MASK V3D_MASK(31, 16)
+ # define V3D_CSD_QUEUED_CFG1_NUM_WGS_Y_SHIFT 16
+ # define V3D_CSD_QUEUED_CFG1_WG_Y_OFFSET_MASK V3D_MASK(15, 0)
+ # define V3D_CSD_QUEUED_CFG1_WG_Y_OFFSET_SHIFT 0
+
+ #define V3D_CSD_QUEUED_CFG2 0x0090c
++#define V3D_V7_CSD_QUEUED_CFG2 0x00938
+ # define V3D_CSD_QUEUED_CFG2_NUM_WGS_Z_MASK V3D_MASK(31, 16)
+ # define V3D_CSD_QUEUED_CFG2_NUM_WGS_Z_SHIFT 16
+ # define V3D_CSD_QUEUED_CFG2_WG_Z_OFFSET_MASK V3D_MASK(15, 0)
+ # define V3D_CSD_QUEUED_CFG2_WG_Z_OFFSET_SHIFT 0
+
+ #define V3D_CSD_QUEUED_CFG3 0x00910
++#define V3D_V7_CSD_QUEUED_CFG3 0x0093c
+ # define V3D_CSD_QUEUED_CFG3_OVERLAP_WITH_PREV BIT(26)
+ # define V3D_CSD_QUEUED_CFG3_MAX_SG_ID_MASK V3D_MASK(25, 20)
+ # define V3D_CSD_QUEUED_CFG3_MAX_SG_ID_SHIFT 20
+@@ -429,22 +461,36 @@
+
+ /* Number of batches, minus 1 */
+ #define V3D_CSD_QUEUED_CFG4 0x00914
++#define V3D_V7_CSD_QUEUED_CFG4 0x00940
+
+ /* Shader address, pnan, singleseg, threading, like a shader record. */
+ #define V3D_CSD_QUEUED_CFG5 0x00918
++#define V3D_V7_CSD_QUEUED_CFG5 0x00944
+
+ /* Uniforms address (4 byte aligned) */
+ #define V3D_CSD_QUEUED_CFG6 0x0091c
++#define V3D_V7_CSD_QUEUED_CFG6 0x00948
++
++#define V3D_V7_CSD_QUEUED_CFG7 0x0094c
+
+ #define V3D_CSD_CURRENT_CFG0 0x00920
++#define V3D_V7_CSD_CURRENT_CFG0 0x00958
+ #define V3D_CSD_CURRENT_CFG1 0x00924
++#define V3D_V7_CSD_CURRENT_CFG1 0x0095c
+ #define V3D_CSD_CURRENT_CFG2 0x00928
++#define V3D_V7_CSD_CURRENT_CFG2 0x00960
+ #define V3D_CSD_CURRENT_CFG3 0x0092c
++#define V3D_V7_CSD_CURRENT_CFG3 0x00964
+ #define V3D_CSD_CURRENT_CFG4 0x00930
++#define V3D_V7_CSD_CURRENT_CFG4 0x00968
+ #define V3D_CSD_CURRENT_CFG5 0x00934
++#define V3D_V7_CSD_CURRENT_CFG5 0x0096c
+ #define V3D_CSD_CURRENT_CFG6 0x00938
++#define V3D_V7_CSD_CURRENT_CFG6 0x00970
++#define V3D_V7_CSD_CURRENT_CFG7 0x00974
+
+ #define V3D_CSD_CURRENT_ID0 0x0093c
++#define V3D_V7_CSD_CURRENT_ID0 0x00978
+ # define V3D_CSD_CURRENT_ID0_WG_X_MASK V3D_MASK(31, 16)
+ # define V3D_CSD_CURRENT_ID0_WG_X_SHIFT 16
+ # define V3D_CSD_CURRENT_ID0_WG_IN_SG_MASK V3D_MASK(11, 8)
+@@ -453,6 +499,7 @@
+ # define V3D_CSD_CURRENT_ID0_L_IDX_SHIFT 0
+
+ #define V3D_CSD_CURRENT_ID1 0x00940
++#define V3D_V7_CSD_CURRENT_ID1 0x0097c
+ # define V3D_CSD_CURRENT_ID0_WG_Z_MASK V3D_MASK(31, 16)
+ # define V3D_CSD_CURRENT_ID0_WG_Z_SHIFT 16
+ # define V3D_CSD_CURRENT_ID0_WG_Y_MASK V3D_MASK(15, 0)
+--- a/drivers/gpu/drm/v3d/v3d_sched.c
++++ b/drivers/gpu/drm/v3d/v3d_sched.c
+@@ -282,6 +282,8 @@ static struct dma_fence *v3d_render_job_
+ return fence;
+ }
+
++#define V3D_TFU_REG(name) ((v3d->ver < 71) ? V3D_TFU_ ## name : V3D_V7_TFU_ ## name)
++
+ static struct dma_fence *
+ v3d_tfu_job_run(struct drm_sched_job *sched_job)
+ {
+@@ -302,20 +304,22 @@ v3d_tfu_job_run(struct drm_sched_job *sc
+ trace_v3d_submit_tfu(dev, to_v3d_fence(fence)->seqno);
+
+ v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_TFU], sched_job);
+- V3D_WRITE(V3D_TFU_IIA, job->args.iia);
+- V3D_WRITE(V3D_TFU_IIS, job->args.iis);
+- V3D_WRITE(V3D_TFU_ICA, job->args.ica);
+- V3D_WRITE(V3D_TFU_IUA, job->args.iua);
+- V3D_WRITE(V3D_TFU_IOA, job->args.ioa);
+- V3D_WRITE(V3D_TFU_IOS, job->args.ios);
+- V3D_WRITE(V3D_TFU_COEF0, job->args.coef[0]);
+- if (job->args.coef[0] & V3D_TFU_COEF0_USECOEF) {
+- V3D_WRITE(V3D_TFU_COEF1, job->args.coef[1]);
+- V3D_WRITE(V3D_TFU_COEF2, job->args.coef[2]);
+- V3D_WRITE(V3D_TFU_COEF3, job->args.coef[3]);
++ V3D_WRITE(V3D_TFU_REG(IIA), job->args.iia);
++ V3D_WRITE(V3D_TFU_REG(IIS), job->args.iis);
++ V3D_WRITE(V3D_TFU_REG(ICA), job->args.ica);
++ V3D_WRITE(V3D_TFU_REG(IUA), job->args.iua);
++ V3D_WRITE(V3D_TFU_REG(IOA), job->args.ioa);
++ if (v3d->ver >= 71)
++ V3D_WRITE(V3D_V7_TFU_IOC, job->args.v71.ioc);
++ V3D_WRITE(V3D_TFU_REG(IOS), job->args.ios);
++ V3D_WRITE(V3D_TFU_REG(COEF0), job->args.coef[0]);
++ if (v3d->ver >= 71 || (job->args.coef[0] & V3D_TFU_COEF0_USECOEF)) {
++ V3D_WRITE(V3D_TFU_REG(COEF1), job->args.coef[1]);
++ V3D_WRITE(V3D_TFU_REG(COEF2), job->args.coef[2]);
++ V3D_WRITE(V3D_TFU_REG(COEF3), job->args.coef[3]);
+ }
+ /* ICFG kicks off the job. */
+- V3D_WRITE(V3D_TFU_ICFG, job->args.icfg | V3D_TFU_ICFG_IOC);
++ V3D_WRITE(V3D_TFU_REG(ICFG), job->args.icfg | V3D_TFU_ICFG_IOC);
+
+ return fence;
+ }
+@@ -327,7 +331,7 @@ v3d_csd_job_run(struct drm_sched_job *sc
+ struct v3d_dev *v3d = job->base.v3d;
+ struct drm_device *dev = &v3d->drm;
+ struct dma_fence *fence;
+- int i;
++ int i, csd_cfg0_reg, csd_cfg_reg_count;
+
+ v3d->csd_job = job;
+
+@@ -346,10 +350,12 @@ v3d_csd_job_run(struct drm_sched_job *sc
+ v3d_sched_stats_add_job(&v3d->gpu_queue_stats[V3D_CSD], sched_job);
+ v3d_switch_perfmon(v3d, &job->base);
+
+- for (i = 1; i <= 6; i++)
+- V3D_CORE_WRITE(0, V3D_CSD_QUEUED_CFG0 + 4 * i, job->args.cfg[i]);
++ csd_cfg0_reg = v3d->ver < 71 ? V3D_CSD_QUEUED_CFG0 : V3D_V7_CSD_QUEUED_CFG0;
++ csd_cfg_reg_count = v3d->ver < 71 ? 6 : 7;
++ for (i = 1; i <= csd_cfg_reg_count; i++)
++ V3D_CORE_WRITE(0, csd_cfg0_reg + 4 * i, job->args.cfg[i]);
+ /* CFG0 write kicks off the job. */
+- V3D_CORE_WRITE(0, V3D_CSD_QUEUED_CFG0, job->args.cfg[0]);
++ V3D_CORE_WRITE(0, csd_cfg0_reg, job->args.cfg[0]);
+
+ return fence;
+ }
+@@ -452,7 +458,8 @@ v3d_csd_job_timedout(struct drm_sched_jo
+ {
+ struct v3d_csd_job *job = to_csd_job(sched_job);
+ struct v3d_dev *v3d = job->base.v3d;
+- u32 batches = V3D_CORE_READ(0, V3D_CSD_CURRENT_CFG4);
++ u32 batches = V3D_CORE_READ(0, (v3d->ver < 71 ? V3D_CSD_CURRENT_CFG4 :
++ V3D_V7_CSD_CURRENT_CFG4));
+
+ /* If we've made progress, skip reset and let the timer get
+ * rearmed.
--- /dev/null
+From 22fb30936524ae96151789741885edbc45efb53d Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+Date: Thu, 2 Mar 2023 11:52:08 +0100
+Subject: [PATCH] drm/v3d: update UAPI to match user-space for V3D 7.x
+
+V3D t.x takes a new parameter to configure TFU jobs that needs
+to be provided by user space.
+---
+ include/uapi/drm/v3d_drm.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/include/uapi/drm/v3d_drm.h
++++ b/include/uapi/drm/v3d_drm.h
+@@ -319,6 +319,10 @@ struct drm_v3d_submit_tfu {
+
+ /* Pointer to an array of ioctl extensions*/
+ __u64 extensions;
++
++ struct {
++ __u32 ioc;
++ } v71;
+ };
+
+ /* Submits a compute shader for dispatch. This job will block on any
--- /dev/null
+From 18bc419d38eda06ded78c7b702c0e21e5af8f24c Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+Date: Thu, 2 Mar 2023 11:54:45 +0100
+Subject: [PATCH] drm/v3d: add brcm,2712-v3d as a compatible V3D device
+
+---
+ drivers/gpu/drm/v3d/v3d_drv.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/v3d/v3d_drv.c
++++ b/drivers/gpu/drm/v3d/v3d_drv.c
+@@ -193,6 +193,7 @@ static const struct drm_driver v3d_drm_d
+ };
+
+ static const struct of_device_id v3d_of_match[] = {
++ { .compatible = "brcm,2712-v3d" },
+ { .compatible = "brcm,2711-v3d" },
+ { .compatible = "brcm,7268-v3d" },
+ { .compatible = "brcm,7278-v3d" },
--- /dev/null
+From 12c7ea43b930976f35ce75d11fd3f55438868e13 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 4 Aug 2023 11:26:10 +0100
+Subject: [PATCH] drm/v3d: Improve MMU support for larger pages
+
+The built-in MMU driver went most of the way towards supporting larger
+kernel pages, but dropped the ball when it comes to calculating indexes
+into the page table. Fix it.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/gpu/drm/v3d/v3d_mmu.c | 15 ++++++++-------
+ 1 file changed, 8 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/v3d/v3d_mmu.c
++++ b/drivers/gpu/drm/v3d/v3d_mmu.c
+@@ -22,6 +22,7 @@
+ #include "v3d_regs.h"
+
+ #define V3D_MMU_PAGE_SHIFT 12
++#define V3D_PAGE_FACTOR (PAGE_SIZE >> V3D_MMU_PAGE_SHIFT)
+
+ /* Note: All PTEs for the 1MB superpage must be filled with the
+ * superpage bit set.
+@@ -88,7 +89,7 @@ void v3d_mmu_insert_ptes(struct v3d_bo *
+ {
+ struct drm_gem_shmem_object *shmem_obj = &bo->base;
+ struct v3d_dev *v3d = to_v3d_dev(shmem_obj->base.dev);
+- u32 page = bo->node.start;
++ u32 page = bo->node.start * V3D_PAGE_FACTOR;
+ u32 page_prot = V3D_PTE_WRITEABLE | V3D_PTE_VALID;
+ struct sg_dma_page_iter dma_iter;
+
+@@ -98,13 +99,13 @@ void v3d_mmu_insert_ptes(struct v3d_bo *
+ u32 pte = page_prot | page_address;
+ u32 i;
+
+- BUG_ON(page_address + (PAGE_SIZE >> V3D_MMU_PAGE_SHIFT) >=
++ BUG_ON(page_address + V3D_PAGE_FACTOR >=
+ BIT(24));
+- for (i = 0; i < PAGE_SIZE >> V3D_MMU_PAGE_SHIFT; i++)
++ for (i = 0; i < V3D_PAGE_FACTOR; i++)
+ v3d->pt[page++] = pte + i;
+ }
+
+- WARN_ON_ONCE(page - bo->node.start !=
++ WARN_ON_ONCE(page - (bo->node.start * V3D_PAGE_FACTOR) !=
+ shmem_obj->base.size >> V3D_MMU_PAGE_SHIFT);
+
+ if (v3d_mmu_flush_all(v3d))
+@@ -115,10 +116,10 @@ void v3d_mmu_remove_ptes(struct v3d_bo *
+ {
+ struct v3d_dev *v3d = to_v3d_dev(bo->base.base.dev);
+ u32 npages = bo->base.base.size >> V3D_MMU_PAGE_SHIFT;
+- u32 page;
++ u32 page = bo->node.start * V3D_PAGE_FACTOR;
+
+- for (page = bo->node.start; page < bo->node.start + npages; page++)
+- v3d->pt[page] = 0;
++ while (npages--)
++ v3d->pt[page++] = 0;
+
+ if (v3d_mmu_flush_all(v3d))
+ dev_err(v3d->drm.dev, "MMU flush timeout\n");
--- /dev/null
+From 5970fa51663511d7f773db7109ff6fa2504f186a Mon Sep 17 00:00:00 2001
+From: Iago Toral Quiroga <itoral@igalia.com>
+Date: Thu, 2 Mar 2023 11:56:52 +0100
+Subject: [PATCH] dt-bindings: gpu: v3d: Add BCM2712 to compatibility list
+
+---
+ Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml
++++ b/Documentation/devicetree/bindings/gpu/brcm,bcm-v3d.yaml
+@@ -16,6 +16,7 @@ properties:
+
+ compatible:
+ enum:
++ - brcm,2712-v3d
+ - brcm,2711-v3d
+ - brcm,7268-v3d
+ - brcm,7278-v3d
--- /dev/null
+From fdf9cab5eaa849e90b12e17718bc47130a91433c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Tue, 25 Apr 2023 15:52:13 +0100
+Subject: [PATCH] drivers: char: add generic gpiomem driver
+
+Based on bcm2835-gpiomem.
+
+We allow export of the "GPIO registers" to userspace via a chardev as
+this allows for finer access control (e.g. users must be group gpio, root
+not required).
+
+This driver allows access to either rp1-gpiomem or gpiomem, depending on
+which nodes are populated in devicetree.
+
+RP1 has a different look-and-feel to BCM283x SoCs as it has split ranges
+for IO controls and the parallel registered OE/IN/OUT access. To handle
+this, the driver concatenates the ranges for an IO bank and the
+corresponding RIO instance into a contiguous buffer.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/char/Kconfig | 8 +
+ drivers/char/Makefile | 1 +
+ drivers/char/raspberrypi-gpiomem.c | 276 +++++++++++++++++++++++++++++
+ 3 files changed, 285 insertions(+)
+ create mode 100644 drivers/char/raspberrypi-gpiomem.c
+
+--- a/drivers/char/Kconfig
++++ b/drivers/char/Kconfig
+@@ -461,4 +461,12 @@ config RANDOM_TRUST_BOOTLOADER
+ believe its RNG facilities may be faulty. This may also be configured
+ at boot time with "random.trust_bootloader=on/off".
+
++config RASPBERRYPI_GPIOMEM
++ tristate "Rootless GPIO access via mmap() on Raspberry Pi boards"
++ default n
++ help
++ Provides users with root-free access to the GPIO registers
++ on the board. Calling mmap(/dev/gpiomem) will map the GPIO
++ register page to the user's pointer.
++
+ endmenu
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -46,3 +46,4 @@ obj-$(CONFIG_XILLYBUS_CLASS) += xillybus
+ obj-$(CONFIG_POWERNV_OP_PANEL) += powernv-op-panel.o
+ obj-$(CONFIG_ADI) += adi.o
+ obj-$(CONFIG_BRCM_CHAR_DRIVERS) += broadcom/
++obj-$(CONFIG_RASPBERRYPI_GPIOMEM) += raspberrypi-gpiomem.o
+--- /dev/null
++++ b/drivers/char/raspberrypi-gpiomem.c
+@@ -0,0 +1,276 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/**
++ * raspberrypi-gpiomem.c
++ *
++ * Provides MMIO access to discontiguous section of Device memory as a linear
++ * user mapping. Successor to bcm2835-gpiomem.c.
++ *
++ * Copyright (c) 2023, Raspberry Pi Ltd.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/mm.h>
++#include <linux/slab.h>
++#include <linux/cdev.h>
++#include <linux/pagemap.h>
++#include <linux/io.h>
++
++#define DRIVER_NAME "rpi-gpiomem"
++#define DEVICE_MINOR 0
++
++/*
++ * Sensible max for a hypothetical "gpio" controller that splits pads,
++ * IO controls, GPIO in/out/enable, and function selection into different
++ * ranges. Most use only one or two.
++ */
++#define MAX_RANGES 4
++
++struct io_windows {
++ unsigned long phys_base;
++ unsigned long len;
++};
++
++struct rpi_gpiomem_priv {
++ dev_t devid;
++ struct class *class;
++ struct cdev rpi_gpiomem_cdev;
++ struct device *dev;
++ const char *name;
++ unsigned int nr_wins;
++ struct io_windows iowins[4];
++};
++
++static int rpi_gpiomem_open(struct inode *inode, struct file *file)
++{
++ int dev = iminor(inode);
++ int ret = 0;
++ struct rpi_gpiomem_priv *priv;
++
++ if (dev != DEVICE_MINOR)
++ ret = -ENXIO;
++
++ priv = container_of(inode->i_cdev, struct rpi_gpiomem_priv,
++ rpi_gpiomem_cdev);
++ if (!priv)
++ return -EINVAL;
++ file->private_data = priv;
++ return ret;
++}
++
++static int rpi_gpiomem_release(struct inode *inode, struct file *file)
++{
++ int dev = iminor(inode);
++ int ret = 0;
++
++ if (dev != DEVICE_MINOR)
++ ret = -ENXIO;
++
++ return ret;
++}
++
++static const struct vm_operations_struct rpi_gpiomem_vm_ops = {
++#ifdef CONFIG_HAVE_IOREMAP_PROT
++ .access = generic_access_phys
++#endif
++};
++
++static int rpi_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
++{
++ int i;
++ struct rpi_gpiomem_priv *priv;
++ unsigned long base;
++ unsigned long len = 0;
++ unsigned long offset;
++
++ priv = file->private_data;
++ /*
++ * Userspace must provide a virtual address space at least
++ * the size of the concatenated ranges.
++ */
++ for (i = 0; i < priv->nr_wins; i++)
++ len += priv->iowins[i].len;
++ if (len > vma->vm_end - vma->vm_start + 1)
++ return -EINVAL;
++
++ vma->vm_ops = &rpi_gpiomem_vm_ops;
++ offset = vma->vm_start;
++ for (i = 0; i < priv->nr_wins; i++) {
++ base = priv->iowins[i].phys_base >> PAGE_SHIFT;
++ len = priv->iowins[i].len;
++ vma->vm_page_prot = phys_mem_access_prot(file, base, len,
++ vma->vm_page_prot);
++ if (remap_pfn_range(vma, offset,
++ base, len,
++ vma->vm_page_prot))
++ break;
++ offset += len;
++ }
++
++ if (i < priv->nr_wins)
++ return -EAGAIN;
++
++ return 0;
++}
++
++static const struct file_operations rpi_gpiomem_fops = {
++ .owner = THIS_MODULE,
++ .open = rpi_gpiomem_open,
++ .release = rpi_gpiomem_release,
++ .mmap = rpi_gpiomem_mmap,
++};
++
++static const struct of_device_id rpi_gpiomem_of_match[];
++
++static int rpi_gpiomem_probe(struct platform_device *pdev)
++{
++ int err, i;
++ const struct of_device_id *id;
++ struct device *dev = &pdev->dev;
++ struct device_node *node = dev->of_node;
++ struct resource *ioresource;
++ struct rpi_gpiomem_priv *priv;
++
++ /* Allocate buffers and instance data */
++
++ priv = kzalloc(sizeof(struct rpi_gpiomem_priv), GFP_KERNEL);
++
++ if (!priv) {
++ err = -ENOMEM;
++ goto failed_inst_alloc;
++ }
++ platform_set_drvdata(pdev, priv);
++
++ priv->dev = dev;
++ id = of_match_device(rpi_gpiomem_of_match, dev);
++ if (!id)
++ return -EINVAL;
++
++ /*
++ * Device node naming - for legacy (bcm2835) DT bindings, the driver
++ * created the node based on a hardcoded name - for new bindings,
++ * take the node name from DT.
++ */
++ if (id == &rpi_gpiomem_of_match[0]) {
++ priv->name = "gpiomem";
++ } else {
++ err = of_property_read_string(node, "chardev-name", &priv->name);
++ if (err)
++ return -EINVAL;
++ }
++
++ /*
++ * Go find the register ranges associated with this instance
++ */
++ for (i = 0; i < MAX_RANGES; i++) {
++ ioresource = platform_get_resource(pdev, IORESOURCE_MEM, i);
++ if (!ioresource && i == 0) {
++ dev_err(priv->dev, "failed to get IO resource - no ranges available\n");
++ err = -ENOENT;
++ goto failed_get_resource;
++ }
++ if (!ioresource)
++ break;
++
++ priv->iowins[i].phys_base = ioresource->start;
++ priv->iowins[i].len = (ioresource->end + 1) - ioresource->start;
++ dev_info(&pdev->dev, "window base 0x%08lx size 0x%08lx\n",
++ priv->iowins[i].phys_base, priv->iowins[i].len);
++ priv->nr_wins++;
++ }
++
++ /* Create character device entries */
++
++ err = alloc_chrdev_region(&priv->devid,
++ DEVICE_MINOR, 1, priv->name);
++ if (err != 0) {
++ dev_err(priv->dev, "unable to allocate device number");
++ goto failed_alloc_chrdev;
++ }
++ cdev_init(&priv->rpi_gpiomem_cdev, &rpi_gpiomem_fops);
++ priv->rpi_gpiomem_cdev.owner = THIS_MODULE;
++ err = cdev_add(&priv->rpi_gpiomem_cdev, priv->devid, 1);
++ if (err != 0) {
++ dev_err(priv->dev, "unable to register device");
++ goto failed_cdev_add;
++ }
++
++ /* Create sysfs entries */
++
++ priv->class = class_create(THIS_MODULE, priv->name);
++ if (IS_ERR(priv->class)) {
++ err = PTR_ERR(priv->class);
++ goto failed_class_create;
++ }
++
++ dev = device_create(priv->class, NULL, priv->devid, NULL, priv->name);
++ if (IS_ERR(dev)) {
++ err = PTR_ERR(dev);
++ goto failed_device_create;
++ }
++
++ dev_info(priv->dev, "initialised %u regions as /dev/%s\n",
++ priv->nr_wins, priv->name);
++
++ return 0;
++
++failed_device_create:
++ class_destroy(priv->class);
++failed_class_create:
++ cdev_del(&priv->rpi_gpiomem_cdev);
++failed_cdev_add:
++ unregister_chrdev_region(priv->devid, 1);
++failed_alloc_chrdev:
++failed_get_resource:
++ kfree(priv);
++failed_inst_alloc:
++ dev_err(&pdev->dev, "could not load rpi_gpiomem");
++ return err;
++}
++
++static int rpi_gpiomem_remove(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct rpi_gpiomem_priv *priv = platform_get_drvdata(pdev);
++
++ device_destroy(priv->class, priv->devid);
++ class_destroy(priv->class);
++ cdev_del(&priv->rpi_gpiomem_cdev);
++ unregister_chrdev_region(priv->devid, 1);
++ kfree(priv);
++
++ dev_info(dev, "%s driver removed - OK", priv->name);
++ return 0;
++}
++
++static const struct of_device_id rpi_gpiomem_of_match[] = {
++ {
++ .compatible = "brcm,bcm2835-gpiomem",
++ },
++ {
++ .compatible = "raspberrypi,gpiomem",
++ },
++ { /* sentinel */ },
++};
++
++MODULE_DEVICE_TABLE(of, rpi_gpiomem_of_match);
++
++static struct platform_driver rpi_gpiomem_driver = {
++ .probe = rpi_gpiomem_probe,
++ .remove = rpi_gpiomem_remove,
++ .driver = {
++ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
++ .of_match_table = rpi_gpiomem_of_match,
++ },
++};
++
++module_platform_driver(rpi_gpiomem_driver);
++
++MODULE_ALIAS("platform:rpi-gpiomem");
++MODULE_LICENSE("Dual BSD/GPL");
++MODULE_DESCRIPTION("Driver for accessing GPIOs from userspace");
++MODULE_AUTHOR("Jonathan Bell <jonathan@raspberrypi.com>");
--- /dev/null
+From 27bda80061b46e18fe83be11228df5365363b377 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 26 Apr 2023 13:44:15 +0100
+Subject: [PATCH] drivers: char: delete bcm2835-gpiomem
+
+This functionality is now provided by raspberrypi-gpiomem.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/char/broadcom/Kconfig | 8 -
+ drivers/char/broadcom/Makefile | 1 -
+ drivers/char/broadcom/bcm2835-gpiomem.c | 258 ------------------------
+ 3 files changed, 267 deletions(-)
+ delete mode 100644 drivers/char/broadcom/bcm2835-gpiomem.c
+
+--- a/drivers/char/broadcom/Kconfig
++++ b/drivers/char/broadcom/Kconfig
+@@ -23,14 +23,6 @@ config BCM_VCIO
+
+ endif
+
+-config BCM2835_DEVGPIOMEM
+- tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
+- default m
+- help
+- Provides users with root-free access to the GPIO registers
+- on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
+- register page to the user's pointer.
+-
+ config BCM2835_SMI_DEV
+ tristate "Character device driver for BCM2835 Secondary Memory Interface"
+ depends on BCM2835_SMI
+--- a/drivers/char/broadcom/Makefile
++++ b/drivers/char/broadcom/Makefile
+@@ -1,5 +1,4 @@
+ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
+ obj-$(CONFIG_BCM_VCIO) += vcio.o
+-obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
+ obj-$(CONFIG_BCM2835_SMI_DEV) += bcm2835_smi_dev.o
+ obj-$(CONFIG_RPIVID_MEM) += rpivid-mem.o
+--- a/drivers/char/broadcom/bcm2835-gpiomem.c
++++ /dev/null
+@@ -1,258 +0,0 @@
+-/**
+- * GPIO memory device driver
+- *
+- * Creates a chardev /dev/gpiomem which will provide user access to
+- * the BCM2835's GPIO registers when it is mmap()'d.
+- * No longer need root for user GPIO access, but without relaxing permissions
+- * on /dev/mem.
+- *
+- * Written by Luke Wren <luke@raspberrypi.org>
+- * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+- *
+- * Redistribution and use in source and binary forms, with or without
+- * modification, are permitted provided that the following conditions
+- * are met:
+- * 1. Redistributions of source code must retain the above copyright
+- * notice, this list of conditions, and the following disclaimer,
+- * without modification.
+- * 2. Redistributions in binary form must reproduce the above copyright
+- * notice, this list of conditions and the following disclaimer in the
+- * documentation and/or other materials provided with the distribution.
+- * 3. The names of the above-listed copyright holders may not be used
+- * to endorse or promote products derived from this software without
+- * specific prior written permission.
+- *
+- * ALTERNATIVELY, this software may be distributed under the terms of the
+- * GNU General Public License ("GPL") version 2, as published by the Free
+- * Software Foundation.
+- *
+- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+- * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/of.h>
+-#include <linux/platform_device.h>
+-#include <linux/mm.h>
+-#include <linux/slab.h>
+-#include <linux/cdev.h>
+-#include <linux/pagemap.h>
+-#include <linux/io.h>
+-
+-#define DEVICE_NAME "bcm2835-gpiomem"
+-#define DRIVER_NAME "gpiomem-bcm2835"
+-#define DEVICE_MINOR 0
+-
+-struct bcm2835_gpiomem_instance {
+- unsigned long gpio_regs_phys;
+- struct device *dev;
+-};
+-
+-static struct cdev bcm2835_gpiomem_cdev;
+-static dev_t bcm2835_gpiomem_devid;
+-static struct class *bcm2835_gpiomem_class;
+-static struct device *bcm2835_gpiomem_dev;
+-static struct bcm2835_gpiomem_instance *inst;
+-
+-
+-/****************************************************************************
+-*
+-* GPIO mem chardev file ops
+-*
+-***************************************************************************/
+-
+-static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
+-{
+- int dev = iminor(inode);
+- int ret = 0;
+-
+- if (dev != DEVICE_MINOR) {
+- dev_err(inst->dev, "Unknown minor device: %d", dev);
+- ret = -ENXIO;
+- }
+- return ret;
+-}
+-
+-static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
+-{
+- int dev = iminor(inode);
+- int ret = 0;
+-
+- if (dev != DEVICE_MINOR) {
+- dev_err(inst->dev, "Unknown minor device %d", dev);
+- ret = -ENXIO;
+- }
+- return ret;
+-}
+-
+-static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
+-#ifdef CONFIG_HAVE_IOREMAP_PROT
+- .access = generic_access_phys
+-#endif
+-};
+-
+-static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
+-{
+- /* Ignore what the user says - they're getting the GPIO regs
+- whether they like it or not! */
+- unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
+-
+- vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
+- PAGE_SIZE,
+- vma->vm_page_prot);
+- vma->vm_ops = &bcm2835_gpiomem_vm_ops;
+- if (remap_pfn_range(vma, vma->vm_start,
+- gpio_page,
+- PAGE_SIZE,
+- vma->vm_page_prot)) {
+- return -EAGAIN;
+- }
+- return 0;
+-}
+-
+-static const struct file_operations
+-bcm2835_gpiomem_fops = {
+- .owner = THIS_MODULE,
+- .open = bcm2835_gpiomem_open,
+- .release = bcm2835_gpiomem_release,
+- .mmap = bcm2835_gpiomem_mmap,
+-};
+-
+-
+- /****************************************************************************
+-*
+-* Probe and remove functions
+-*
+-***************************************************************************/
+-
+-
+-static int bcm2835_gpiomem_probe(struct platform_device *pdev)
+-{
+- int err;
+- void *ptr_err;
+- struct device *dev = &pdev->dev;
+- struct resource *ioresource;
+-
+- /* Allocate buffers and instance data */
+-
+- inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
+-
+- if (!inst) {
+- err = -ENOMEM;
+- goto failed_inst_alloc;
+- }
+-
+- inst->dev = dev;
+-
+- ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- if (ioresource) {
+- inst->gpio_regs_phys = ioresource->start;
+- } else {
+- dev_err(inst->dev, "failed to get IO resource");
+- err = -ENOENT;
+- goto failed_get_resource;
+- }
+-
+- /* Create character device entries */
+-
+- err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
+- DEVICE_MINOR, 1, DEVICE_NAME);
+- if (err != 0) {
+- dev_err(inst->dev, "unable to allocate device number");
+- goto failed_alloc_chrdev;
+- }
+- cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
+- bcm2835_gpiomem_cdev.owner = THIS_MODULE;
+- err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
+- if (err != 0) {
+- dev_err(inst->dev, "unable to register device");
+- goto failed_cdev_add;
+- }
+-
+- /* Create sysfs entries */
+-
+- bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
+- ptr_err = bcm2835_gpiomem_class;
+- if (IS_ERR(ptr_err))
+- goto failed_class_create;
+-
+- bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
+- bcm2835_gpiomem_devid, NULL,
+- "gpiomem");
+- ptr_err = bcm2835_gpiomem_dev;
+- if (IS_ERR(ptr_err))
+- goto failed_device_create;
+-
+- dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
+- inst->gpio_regs_phys);
+-
+- return 0;
+-
+-failed_device_create:
+- class_destroy(bcm2835_gpiomem_class);
+-failed_class_create:
+- cdev_del(&bcm2835_gpiomem_cdev);
+- err = PTR_ERR(ptr_err);
+-failed_cdev_add:
+- unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
+-failed_alloc_chrdev:
+-failed_get_resource:
+- kfree(inst);
+-failed_inst_alloc:
+- dev_err(inst->dev, "could not load bcm2835_gpiomem");
+- return err;
+-}
+-
+-static int bcm2835_gpiomem_remove(struct platform_device *pdev)
+-{
+- struct device *dev = inst->dev;
+-
+- kfree(inst);
+- device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
+- class_destroy(bcm2835_gpiomem_class);
+- cdev_del(&bcm2835_gpiomem_cdev);
+- unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
+-
+- dev_info(dev, "GPIO mem driver removed - OK");
+- return 0;
+-}
+-
+- /****************************************************************************
+-*
+-* Register the driver with device tree
+-*
+-***************************************************************************/
+-
+-static const struct of_device_id bcm2835_gpiomem_of_match[] = {
+- {.compatible = "brcm,bcm2835-gpiomem",},
+- { /* sentinel */ },
+-};
+-
+-MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
+-
+-static struct platform_driver bcm2835_gpiomem_driver = {
+- .probe = bcm2835_gpiomem_probe,
+- .remove = bcm2835_gpiomem_remove,
+- .driver = {
+- .name = DRIVER_NAME,
+- .owner = THIS_MODULE,
+- .of_match_table = bcm2835_gpiomem_of_match,
+- },
+-};
+-
+-module_platform_driver(bcm2835_gpiomem_driver);
+-
+-MODULE_ALIAS("platform:gpiomem-bcm2835");
+-MODULE_LICENSE("GPL");
+-MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
+-MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");
--- /dev/null
+From 3cafcfbab9b5f3f1357b415b6ca09911eeb405d6 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Thu, 4 May 2023 15:48:53 +0100
+Subject: [PATCH] drivers: hwmon: rp1-adc: check conversion validity before
+ supplying value
+
+The SAR ADC architecture may complete a conversion but instability in the
+comparator can corrupt the result. Such corruption is signalled in the CS
+ERR bit, asserted alongside each conversion result.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/hwmon/rp1-adc.c | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+--- a/drivers/hwmon/rp1-adc.c
++++ b/drivers/hwmon/rp1-adc.c
+@@ -97,8 +97,14 @@ static int rp1_adc_read(struct rp1_adc_d
+ data->base + RP1_ADC_RWTYPE_SET + RP1_ADC_CS);
+
+ ret = rp1_adc_ready_wait(data);
+- if (!ret)
+- *val = readl(data->base + RP1_ADC_RESULT);
++ if (ret)
++ return ret;
++
++ /* Asserted if the completed conversion had a convergence error */
++ if (readl(data->base + RP1_ADC_CS) & RP1_ADC_CS_ERR)
++ return -EIO;
++
++ *val = readl(data->base + RP1_ADC_RESULT);
+
+ spin_unlock(&data->lock);
+
--- /dev/null
+From 87c5545f9a66984894384da5f8c2eeb60983732a Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Mar 2023 16:53:38 +0000
+Subject: [PATCH] dmaengine: bcm2835: Add BCM2712 support
+
+BCM2712 has 6 40-bit channels - DMA6 to DMA11. Add a new compatible
+string to indicate that the current platform is BCM2712.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/dma/bcm2835-dma.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -331,6 +331,12 @@ static const struct bcm2835_dma_cfg_data
+ .dma_mask = DMA_BIT_MASK(36),
+ };
+
++static const struct bcm2835_dma_cfg_data bcm2712_dma_cfg = {
++ .chan_40bit_mask = BIT(6) | BIT(7) | BIT(8) | BIT(9) |
++ BIT(10) | BIT(11),
++ .dma_mask = DMA_BIT_MASK(40),
++};
++
+ static inline size_t bcm2835_dma_max_frame_length(struct bcm2835_chan *c)
+ {
+ /* lite and normal channels have different max frame length */
+@@ -1260,6 +1266,7 @@ EXPORT_SYMBOL(bcm2711_dma40_memcpy);
+ static const struct of_device_id bcm2835_dma_of_match[] = {
+ { .compatible = "brcm,bcm2835-dma", .data = &bcm2835_dma_cfg },
+ { .compatible = "brcm,bcm2711-dma", .data = &bcm2711_dma_cfg },
++ { .compatible = "brcm,bcm2712-dma", .data = &bcm2712_dma_cfg },
+ {},
+ };
+ MODULE_DEVICE_TABLE(of, bcm2835_dma_of_match);
--- /dev/null
+From a671a2774cb3bcfb144622149757f6821aa0604c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Apr 2023 16:52:19 +0200
+Subject: [PATCH] dmaengine: bcm2835: HACK: Support DMA-Lite channels
+
+The BCM2712 has a DMA-Lite controller that is basically a BCM2835-style
+DMA controller that supports 40 bits DMA addresses.
+
+We need it for HDMI audio to work, but this breaks BCM2835-38 so we
+should rework this later.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/dma/bcm2835-dma.c | 11 +++++++----
+ 1 file changed, 7 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -550,7 +550,7 @@ static struct bcm2835_desc *bcm2835_dma_
+ control_block->info = info;
+ control_block->src = src;
+ control_block->dst = dst;
+- control_block->stride = 0;
++ control_block->stride = (upper_32_bits(dst) << 8) | upper_32_bits(src);
+ control_block->next = 0;
+ }
+
+@@ -575,7 +575,7 @@ static struct bcm2835_desc *bcm2835_dma_
+ d->cb_list[frame - 1].cb)->next_cb =
+ to_bcm2711_cbaddr(cb_entry->paddr);
+ if (frame && !c->is_40bit_channel)
+- d->cb_list[frame - 1].cb->next = cb_entry->paddr;
++ d->cb_list[frame - 1].cb->next = to_bcm2711_cbaddr(cb_entry->paddr);
+
+ /* update src and dst and length */
+ if (src && (info & BCM2835_DMA_S_INC)) {
+@@ -760,7 +760,10 @@ static void bcm2835_dma_start_desc(struc
+ writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT | BCM2711_DMA40_CS_FLAGS(c->dreq),
+ c->chan_base + BCM2711_DMA40_CS);
+ } else {
+- writel(d->cb_list[0].paddr, c->chan_base + BCM2835_DMA_ADDR);
++ writel(BIT(31), c->chan_base + BCM2835_DMA_CS);
++
++ writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++ c->chan_base + BCM2835_DMA_ADDR);
+ writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+ c->chan_base + BCM2835_DMA_CS);
+ }
+@@ -1129,7 +1132,7 @@ static struct dma_async_tx_descriptor *b
+ d->cb_list[frames - 1].cb)->next_cb =
+ to_bcm2711_cbaddr(d->cb_list[0].paddr);
+ else
+- d->cb_list[d->frames - 1].cb->next = d->cb_list[0].paddr;
++ d->cb_list[d->frames - 1].cb->next = to_bcm2711_cbaddr(d->cb_list[0].paddr);
+
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
--- /dev/null
+From c8fd69c6f567bd43ba084b95a987532940465ef5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Feb 2023 14:12:50 +0100
+Subject: [PATCH] clk: bcm: rpi: Add disp clock
+
+BCM2712 has an extra clock exposed by the firmware called DISP, and used
+by (at least) the HVS. Let's add it to the list of clocks to register in
+Linux.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/clk/bcm/clk-raspberrypi.c | 5 +++++
+ include/soc/bcm2835/raspberrypi-firmware.h | 1 +
+ 2 files changed, 6 insertions(+)
+
+--- a/drivers/clk/bcm/clk-raspberrypi.c
++++ b/drivers/clk/bcm/clk-raspberrypi.c
+@@ -34,6 +34,7 @@ static char *rpi_firmware_clk_names[] =
+ [RPI_FIRMWARE_M2MC_CLK_ID] = "m2mc",
+ [RPI_FIRMWARE_PIXEL_BVB_CLK_ID] = "pixel-bvb",
+ [RPI_FIRMWARE_VEC_CLK_ID] = "vec",
++ [RPI_FIRMWARE_DISP_CLK_ID] = "disp",
+ };
+
+ #define RPI_FIRMWARE_STATE_ENABLE_BIT BIT(0)
+@@ -139,6 +140,10 @@ raspberrypi_clk_variants[RPI_FIRMWARE_NU
+ .export = true,
+ .minimize = true,
+ },
++ [RPI_FIRMWARE_DISP_CLK_ID] = {
++ .export = true,
++ .minimize = true,
++ },
+ };
+
+ /*
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -176,6 +176,7 @@ enum rpi_firmware_clk_id {
+ RPI_FIRMWARE_M2MC_CLK_ID,
+ RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
+ RPI_FIRMWARE_VEC_CLK_ID,
++ RPI_FIRMWARE_DISP_CLK_ID,
+ RPI_FIRMWARE_NUM_CLK_ID,
+ };
+
--- /dev/null
+From 6370a6cd16a5aa9726bf209c0f0a3179f4011cb1 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 22 May 2023 15:31:17 +0100
+Subject: [PATCH] net: phy: broadcom: optionally enable link-down powersave
+ based on DT
+
+It's really a function of the board whether or not to use this feature
+as it may require MAC compatibility as well as interop testing.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/net/phy/broadcom.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -370,6 +370,9 @@ static int bcm54xx_config_init(struct ph
+ (phydev->dev_flags & PHY_BRCM_CLEAR_RGMII_MODE))
+ bcm_phy_write_shadow(phydev, BCM54XX_SHD_RGMII_MODE, 0);
+
++ if (of_property_read_bool(np, "brcm,powerdown-enable"))
++ phydev->dev_flags |= PHY_BRCM_AUTO_PWRDWN_ENABLE;
++
+ bcm54xx_adjust_rxrefclk(phydev);
+
+ switch (BRCM_PHY_MODEL(phydev)) {
--- /dev/null
+From cfad3f71fc450639fc259d576d0903e9132fe34a Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 25 May 2023 14:48:28 +0100
+Subject: [PATCH] dmaengine: bcm2835: Rename to_bcm2711_cbaddr to
+ to_40bit_cbaddr
+
+As the shifted address also applies to bcm2712,
+give the function a more specific name.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/dma/bcm2835-dma.c | 16 ++++++++--------
+ 1 file changed, 8 insertions(+), 8 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -390,7 +390,7 @@ static inline uint32_t to_bcm2711_dsti(u
+ BCM2711_DMA40_BURST_LEN(BCM2835_DMA_GET_BURST_LENGTH(info));
+ }
+
+-static inline uint32_t to_bcm2711_cbaddr(dma_addr_t addr)
++static inline uint32_t to_40bit_cbaddr(dma_addr_t addr)
+ {
+ BUG_ON(addr & 0x1f);
+ return (addr >> 5);
+@@ -573,9 +573,9 @@ static struct bcm2835_desc *bcm2835_dma_
+ if (frame && c->is_40bit_channel)
+ ((struct bcm2711_dma40_scb *)
+ d->cb_list[frame - 1].cb)->next_cb =
+- to_bcm2711_cbaddr(cb_entry->paddr);
++ to_40bit_cbaddr(cb_entry->paddr);
+ if (frame && !c->is_40bit_channel)
+- d->cb_list[frame - 1].cb->next = to_bcm2711_cbaddr(cb_entry->paddr);
++ d->cb_list[frame - 1].cb->next = to_40bit_cbaddr(cb_entry->paddr);
+
+ /* update src and dst and length */
+ if (src && (info & BCM2835_DMA_S_INC)) {
+@@ -755,14 +755,14 @@ static void bcm2835_dma_start_desc(struc
+ c->desc = d = to_bcm2835_dma_desc(&vd->tx);
+
+ if (c->is_40bit_channel) {
+- writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++ writel(to_40bit_cbaddr(d->cb_list[0].paddr),
+ c->chan_base + BCM2711_DMA40_CB);
+ writel(BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT | BCM2711_DMA40_CS_FLAGS(c->dreq),
+ c->chan_base + BCM2711_DMA40_CS);
+ } else {
+ writel(BIT(31), c->chan_base + BCM2835_DMA_CS);
+
+- writel(to_bcm2711_cbaddr(d->cb_list[0].paddr),
++ writel(to_40bit_cbaddr(d->cb_list[0].paddr),
+ c->chan_base + BCM2835_DMA_ADDR);
+ writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+ c->chan_base + BCM2835_DMA_CS);
+@@ -1130,9 +1130,9 @@ static struct dma_async_tx_descriptor *b
+ if (c->is_40bit_channel)
+ ((struct bcm2711_dma40_scb *)
+ d->cb_list[frames - 1].cb)->next_cb =
+- to_bcm2711_cbaddr(d->cb_list[0].paddr);
++ to_40bit_cbaddr(d->cb_list[0].paddr);
+ else
+- d->cb_list[d->frames - 1].cb->next = to_bcm2711_cbaddr(d->cb_list[0].paddr);
++ d->cb_list[d->frames - 1].cb->next = to_40bit_cbaddr(d->cb_list[0].paddr);
+
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
+@@ -1252,7 +1252,7 @@ void bcm2711_dma40_memcpy(dma_addr_t dst
+ scb->len = size;
+ scb->next_cb = 0;
+
+- writel(to_bcm2711_cbaddr(memcpy_scb_dma), memcpy_chan + BCM2711_DMA40_CB);
++ writel(to_40bit_cbaddr(memcpy_scb_dma), memcpy_chan + BCM2711_DMA40_CB);
+ writel(BCM2711_DMA40_MEMCPY_FLAGS | BCM2711_DMA40_ACTIVE | BCM2711_DMA40_PROT,
+ memcpy_chan + BCM2711_DMA40_CS);
+
--- /dev/null
+From 75f44d1416c5de17865247d6d012c37f7650437c Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 24 May 2023 19:32:16 +0100
+Subject: [PATCH] dmaengine: bcm2835: Fix dma driver for BCM2835-38
+
+The previous commit broke support on older devices.
+Make the breaking parts of patch conditional on
+the device being used.
+
+Fixes: 6e1856ac7c39 ("dmaengine: bcm2835: HACK: Support DMA-Lite channels")
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/dma/bcm2835-dma.c | 17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/dma/bcm2835-dma.c
++++ b/drivers/dma/bcm2835-dma.c
+@@ -102,6 +102,7 @@ struct bcm2835_chan {
+
+ bool is_lite_channel;
+ bool is_40bit_channel;
++ bool is_2712;
+ };
+
+ struct bcm2835_desc {
+@@ -550,7 +551,11 @@ static struct bcm2835_desc *bcm2835_dma_
+ control_block->info = info;
+ control_block->src = src;
+ control_block->dst = dst;
+- control_block->stride = (upper_32_bits(dst) << 8) | upper_32_bits(src);
++ if (c->is_2712)
++ control_block->stride = (upper_32_bits(dst) << 8) |
++ upper_32_bits(src);
++ else
++ control_block->stride = 0;
+ control_block->next = 0;
+ }
+
+@@ -575,7 +580,8 @@ static struct bcm2835_desc *bcm2835_dma_
+ d->cb_list[frame - 1].cb)->next_cb =
+ to_40bit_cbaddr(cb_entry->paddr);
+ if (frame && !c->is_40bit_channel)
+- d->cb_list[frame - 1].cb->next = to_40bit_cbaddr(cb_entry->paddr);
++ d->cb_list[frame - 1].cb->next = c->is_2712 ?
++ to_40bit_cbaddr(cb_entry->paddr) : cb_entry->paddr;
+
+ /* update src and dst and length */
+ if (src && (info & BCM2835_DMA_S_INC)) {
+@@ -762,7 +768,7 @@ static void bcm2835_dma_start_desc(struc
+ } else {
+ writel(BIT(31), c->chan_base + BCM2835_DMA_CS);
+
+- writel(to_40bit_cbaddr(d->cb_list[0].paddr),
++ writel(c->is_2712 ? to_40bit_cbaddr(d->cb_list[0].paddr) : d->cb_list[0].paddr,
+ c->chan_base + BCM2835_DMA_ADDR);
+ writel(BCM2835_DMA_ACTIVE | BCM2835_DMA_CS_FLAGS(c->dreq),
+ c->chan_base + BCM2835_DMA_CS);
+@@ -1132,7 +1138,8 @@ static struct dma_async_tx_descriptor *b
+ d->cb_list[frames - 1].cb)->next_cb =
+ to_40bit_cbaddr(d->cb_list[0].paddr);
+ else
+- d->cb_list[d->frames - 1].cb->next = to_40bit_cbaddr(d->cb_list[0].paddr);
++ d->cb_list[d->frames - 1].cb->next = c->is_2712 ?
++ to_40bit_cbaddr(d->cb_list[0].paddr) : d->cb_list[0].paddr;
+
+ return vchan_tx_prep(&c->vc, &d->vd, flags);
+ }
+@@ -1199,6 +1206,8 @@ static int bcm2835_dma_chan_init(struct
+ else if (readl(c->chan_base + BCM2835_DMA_DEBUG) &
+ BCM2835_DMA_DEBUG_LITE)
+ c->is_lite_channel = true;
++ if (d->cfg_data->dma_mask == DMA_BIT_MASK(40))
++ c->is_2712 = true;
+
+ return 0;
+ }
--- /dev/null
+From 5fd6ee7fd084838e09d4e463ae53cd9aaa7fce70 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Thu, 11 May 2023 16:37:34 +0100
+Subject: [PATCH] drivers: iommu: Add BCM2712 IOMMU
+
+Add a driver for BCM2712 IOMMUs.
+There is a small driver for the Shared IOMMU TLB Cache.
+Each IOMMU instance is a separate device.
+
+IOMMUs are set up with a "pass-through" range covering
+the lowest 40BGytes (which should cover all of SDRAM)
+for the benefit of non-IOMMU-aware devices that share
+a physical IOMMU; and translation for addresses in the
+range 40GB to 42GB.
+
+An optional parameter adds a DMA offset (which otherwise
+would be lost?) to virtual addresses for DMA masters on a
+bus such as PCIe.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/iommu/Kconfig | 7 +
+ drivers/iommu/Makefile | 1 +
+ drivers/iommu/bcm2712-iommu-cache.c | 77 ++++
+ drivers/iommu/bcm2712-iommu.c | 672 ++++++++++++++++++++++++++++
+ drivers/iommu/bcm2712-iommu.h | 45 ++
+ 5 files changed, 802 insertions(+)
+ create mode 100644 drivers/iommu/bcm2712-iommu-cache.c
+ create mode 100644 drivers/iommu/bcm2712-iommu.c
+ create mode 100644 drivers/iommu/bcm2712-iommu.h
+
+--- a/drivers/iommu/Kconfig
++++ b/drivers/iommu/Kconfig
+@@ -506,4 +506,11 @@ config SPRD_IOMMU
+
+ Say Y here if you want to use the multimedia devices listed above.
+
++config BCM2712_IOMMU
++ tristate "BCM2712 IOMMU driver"
++ depends on ARM64 && ARCH_BCM
++ select IOMMU_API
++ help
++ IOMMU driver for BCM2712
++
+ endif # IOMMU_SUPPORT
+--- a/drivers/iommu/Makefile
++++ b/drivers/iommu/Makefile
+@@ -31,3 +31,4 @@ obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iom
+ obj-$(CONFIG_IOMMU_SVA) += iommu-sva-lib.o io-pgfault.o
+ obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
+ obj-$(CONFIG_APPLE_DART) += apple-dart.o
++obj-$(CONFIG_BCM2712_IOMMU) += bcm2712-iommu.o bcm2712-iommu-cache.o
+--- /dev/null
++++ b/drivers/iommu/bcm2712-iommu-cache.c
+@@ -0,0 +1,77 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * IOMMU driver for BCM2712
++ *
++ * Copyright (c) 2023 Raspberry Pi Ltd.
++ */
++
++#include "bcm2712-iommu.h"
++
++#include <linux/err.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define MMUC_CONTROL_ENABLE 1
++#define MMUC_CONTROL_FLUSH 2
++#define MMUC_CONTROL_FLUSHING 4
++
++void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache)
++{
++ unsigned long flags;
++ int i;
++
++ spin_lock_irqsave(&cache->hw_lock, flags);
++ if (cache->reg_base) {
++ /* Enable and flush the TLB cache */
++ writel(MMUC_CONTROL_ENABLE | MMUC_CONTROL_FLUSH,
++ cache->reg_base);
++
++ /* Wait for flush to complete: it should be very quick */
++ for (i = 0; i < 1024; i++) {
++ if (!(MMUC_CONTROL_FLUSHING & readl(cache->reg_base)))
++ break;
++ cpu_relax();
++ }
++ }
++ spin_unlock_irqrestore(&cache->hw_lock, flags);
++}
++
++static int bcm2712_iommu_cache_probe(struct platform_device *pdev)
++{
++ struct bcm2712_iommu_cache *cache;
++
++ dev_info(&pdev->dev, __func__);
++ cache = devm_kzalloc(&pdev->dev, sizeof(*cache), GFP_KERNEL);
++ if (!cache)
++ return -ENOMEM;
++
++ cache->dev = &pdev->dev;
++ platform_set_drvdata(pdev, cache);
++ spin_lock_init(&cache->hw_lock);
++
++ /* Get IOMMUC registers; we only use the first register (IOMMUC_CTRL) */
++ cache->reg_base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(cache->reg_base)) {
++ dev_err(&pdev->dev, "Failed to get IOMMU Cache registers address\n");
++ cache->reg_base = NULL;
++ }
++ return 0;
++}
++
++static const struct of_device_id bcm2712_iommu_cache_of_match[] = {
++ {
++ . compatible = "brcm,bcm2712-iommuc"
++ },
++ { /* sentinel */ },
++};
++
++static struct platform_driver bcm2712_iommu_cache_driver = {
++ .probe = bcm2712_iommu_cache_probe,
++ .driver = {
++ .name = "bcm2712-iommu-cache",
++ .of_match_table = bcm2712_iommu_cache_of_match
++ },
++};
++
++builtin_platform_driver(bcm2712_iommu_cache_driver);
+--- /dev/null
++++ b/drivers/iommu/bcm2712-iommu.c
+@@ -0,0 +1,672 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * IOMMU driver for BCM2712
++ *
++ * Copyright (c) 2023 Raspberry Pi Ltd.
++ */
++
++#include "bcm2712-iommu.h"
++
++#include <linux/dma-mapping.h>
++#include <linux/err.h>
++#include <linux/iommu.h>
++#include <linux/of_platform.h>
++#include <linux/platform_device.h>
++#include <linux/spinlock.h>
++
++#define MMU_WR(off, val) writel(val, mmu->reg_base + (off))
++#define MMU_RD(off) readl(mmu->reg_base + (off))
++
++#define domain_to_mmu(d) (container_of(d, struct bcm2712_iommu_domain, base)->mmu)
++
++#define MMMU_CTRL_OFFSET 0x00
++#define MMMU_CTRL_CAP_EXCEEDED BIT(27)
++#define MMMU_CTRL_CAP_EXCEEDED_ABORT_EN BIT(26)
++#define MMMU_CTRL_CAP_EXCEEDED_INT_EN BIT(25)
++#define MMMU_CTRL_CAP_EXCEEDED_EXCEPTION_EN BIT(24)
++#define MMMU_CTRL_PT_INVALID BIT(20)
++#define MMMU_CTRL_PT_INVALID_ABORT_EN BIT(19)
++#define MMMU_CTRL_PT_INVALID_EXCEPTION_EN BIT(18)
++#define MMMU_CTRL_PT_INVALID_EN BIT(17)
++#define MMMU_CTRL_WRITE_VIOLATION BIT(12)
++#define MMMU_CTRL_WRITE_VIOLATION_ABORT_EN BIT(11)
++#define MMMU_CTRL_WRITE_VIOLATION_INT_EN BIT(10)
++#define MMMU_CTRL_WRITE_VIOLATION_EXCEPTION_EN BIT(9)
++#define MMMU_CTRL_BYPASS BIT(8)
++#define MMMU_CTRL_TLB_CLEARING BIT(7)
++#define MMMU_CTRL_STATS_CLEAR BIT(3)
++#define MMMU_CTRL_TLB_CLEAR BIT(2)
++#define MMMU_CTRL_STATS_ENABLE BIT(1)
++#define MMMU_CTRL_ENABLE BIT(0)
++
++#define MMMU_PT_PA_BASE_OFFSET 0x04
++
++#define MMMU_HIT_OFFSET 0x08
++#define MMMU_MISS_OFFSET 0x0C
++#define MMMU_STALL_OFFSET 0x10
++
++#define MMMU_ADDR_CAP_OFFSET 0x14
++#define MMMU_ADDR_CAP_ENABLE BIT(31)
++#define ADDR_CAP_SHIFT 28 /* ADDR_CAP is defined to be in 256 MByte units */
++
++#define MMMU_SHOOT_DOWN_OFFSET 0x18
++#define MMMU_SHOOT_DOWN_SHOOTING BIT(31)
++#define MMMU_SHOOT_DOWN_SHOOT BIT(30)
++
++#define MMMU_BYPASS_START_OFFSET 0x1C
++#define MMMU_BYPASS_START_ENABLE BIT(31)
++#define MMMU_BYPASS_START_INVERT BIT(30)
++
++#define MMMU_BYPASS_END_OFFSET 0x20
++#define MMMU_BYPASS_END_ENABLE BIT(31)
++
++#define MMMU_MISC_OFFSET 0x24
++#define MMMU_MISC_SINGLE_TABLE BIT(31)
++
++#define MMMU_ILLEGAL_ADR_OFFSET 0x30
++#define MMMU_ILLEGAL_ADR_ENABLE BIT(31)
++
++#define MMMU_DEBUG_INFO_OFFSET 0x38
++#define MMMU_DEBUG_INFO_VERSION_MASK 0x0000000Fu
++#define MMMU_DEBUG_INFO_VA_WIDTH_MASK 0x000000F0u
++#define MMMU_DEBUG_INFO_PA_WIDTH_MASK 0x00000F00u
++#define MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK 0x000FF000u
++#define MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK 0x0FF00000u
++#define MMMU_DEBUG_INFO_BYPASS_4M BIT(28)
++#define MMMU_DEBUG_INFO_BYPASS BIT(29)
++
++#define MMMU_PTE_PAGESIZE_MASK 0xC0000000u
++#define MMMU_PTE_WRITEABLE BIT(29)
++#define MMMU_PTE_VALID BIT(28)
++
++/*
++ * BCM2712 IOMMU is organized around 4Kbyte pages (MMU_PAGE_SIZE).
++ * Linux PAGE_SIZE must not be smaller but may be larger (e.g. 4K, 16K).
++ *
++ * Unlike many larger MMUs, this one uses a 4-byte word size, allowing
++ * 1024 entries within each 4K table page, and two-level translation.
++ *
++ * Let's allocate enough table space for 2GB of translated memory (IOVA).
++ * This requires 512 4K pages (2MB) of level-2 tables, one page of
++ * top-level table (only half-filled in this particular configuration),
++ * plus one "default" page to catch illegal requests.
++ *
++ * The translated virtual address region is between 40GB and 42GB;
++ * addresses below this range pass straight through to the SDRAM.
++ *
++ * Currently we assume a 1:1:1 correspondence of IOMMU, group and domain.
++ */
++
++#define MMU_PAGE_SHIFT 12
++#define MMU_PAGE_SIZE BIT(MMU_PAGE_SHIFT)
++
++#define PAGEWORDS_SHIFT (MMU_PAGE_SHIFT - 2)
++#define HUGEPAGE_SHIFT (MMU_PAGE_SHIFT + PAGEWORDS_SHIFT)
++#define L1_CHUNK_SHIFT (MMU_PAGE_SHIFT + 2 * PAGEWORDS_SHIFT)
++
++#define APERTURE_BASE (40ul << 30)
++#define APERTURE_SIZE (2ul << 30)
++#define APERTURE_TOP (APERTURE_BASE + APERTURE_SIZE)
++#define TRANSLATED_PAGES (APERTURE_SIZE >> MMU_PAGE_SHIFT)
++#define L2_PAGES (TRANSLATED_PAGES >> PAGEWORDS_SHIFT)
++#define TABLES_ALLOC_SIZE (L2_PAGES * MMU_PAGE_SIZE + 2 * PAGE_SIZE)
++
++static void bcm2712_iommu_init(struct bcm2712_iommu *mmu)
++{
++ unsigned int i, bypass_shift;
++ struct sg_dma_page_iter it;
++ u32 u = MMU_RD(MMMU_DEBUG_INFO_OFFSET);
++
++ /*
++ * Check IOMMU version and hardware configuration.
++ * This driver is for VC IOMMU version >= 4 (with 2-level tables)
++ * and assumes at least 36 bits of virtual and physical address space.
++ * Bigpage and superpage sizes are typically 64K and 1M, but may vary
++ * (hugepage size is fixed at 4M, the range covered by an L2 page).
++ */
++ dev_info(mmu->dev, "%s: DEBUG_INFO = 0x%08x\n", __func__, u);
++ WARN_ON(FIELD_GET(MMMU_DEBUG_INFO_VERSION_MASK, u) < 4 ||
++ FIELD_GET(MMMU_DEBUG_INFO_VA_WIDTH_MASK, u) < 6 ||
++ FIELD_GET(MMMU_DEBUG_INFO_PA_WIDTH_MASK, u) < 6 ||
++ !(u & MMMU_DEBUG_INFO_BYPASS));
++
++ mmu->bigpage_mask =
++ ((1u << FIELD_GET(MMMU_DEBUG_INFO_BIGPAGE_WIDTH_MASK, u)) - 1u) << MMU_PAGE_SHIFT;
++ mmu->superpage_mask =
++ ((1u << FIELD_GET(MMMU_DEBUG_INFO_SUPERPAGE_WIDTH_MASK, u)) - 1u) << MMU_PAGE_SHIFT;
++ bypass_shift = (u & MMMU_DEBUG_INFO_BYPASS_4M) ?
++ HUGEPAGE_SHIFT : ADDR_CAP_SHIFT;
++
++ /* Disable MMU and clear sticky flags; meanwhile flush the TLB */
++ MMU_WR(MMMU_CTRL_OFFSET,
++ MMMU_CTRL_CAP_EXCEEDED |
++ MMMU_CTRL_PT_INVALID |
++ MMMU_CTRL_WRITE_VIOLATION |
++ MMMU_CTRL_STATS_CLEAR |
++ MMMU_CTRL_TLB_CLEAR);
++
++ /*
++ * Put MMU into 2-level mode; set address cap and "bypass" range
++ * (note that some of these registers have unintuitive off-by-ones).
++ * Addresses below APERTURE_BASE are passed unchanged: this is
++ * useful for blocks which share an IOMMU with other blocks
++ * whose drivers are not IOMMU-aware.
++ */
++ MMU_WR(MMMU_MISC_OFFSET,
++ MMU_RD(MMMU_MISC_OFFSET) & ~MMMU_MISC_SINGLE_TABLE);
++ MMU_WR(MMMU_ADDR_CAP_OFFSET,
++ MMMU_ADDR_CAP_ENABLE +
++ (APERTURE_TOP >> ADDR_CAP_SHIFT) - 1);
++ if (APERTURE_BASE > 0) {
++ MMU_WR(MMMU_BYPASS_START_OFFSET,
++ MMMU_BYPASS_START_ENABLE + MMMU_BYPASS_START_INVERT +
++ (APERTURE_BASE >> bypass_shift) - 1);
++ MMU_WR(MMMU_BYPASS_END_OFFSET,
++ MMMU_BYPASS_END_ENABLE +
++ (APERTURE_TOP >> bypass_shift));
++ } else {
++ MMU_WR(MMMU_BYPASS_START_OFFSET, 0);
++ MMU_WR(MMMU_BYPASS_END_OFFSET, 0);
++ }
++
++ /* Ensure tables are zeroed (which marks all pages as invalid) */
++ dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++ memset(mmu->tables, 0, TABLES_ALLOC_SIZE);
++ mmu->nmapped_pages = 0;
++
++ /* Initialize the high-level table to point to the low-level pages */
++ __sg_page_iter_start(&it.base, mmu->sgt->sgl, mmu->sgt->nents, 0);
++ for (i = 0; i < L2_PAGES; i++) {
++ if (!(i % (PAGE_SIZE / MMU_PAGE_SIZE))) {
++ __sg_page_iter_dma_next(&it);
++ u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT);
++ } else {
++ u++;
++ }
++ mmu->tables[TRANSLATED_PAGES + i] = MMMU_PTE_VALID + u;
++ }
++
++ /*
++ * Configure the addresses of the top-level table (offset because
++ * the aperture does not start from zero), and of the default page.
++ * For simplicity, both these regions are whole Linux pages.
++ */
++ __sg_page_iter_dma_next(&it);
++ u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT);
++ MMU_WR(MMMU_PT_PA_BASE_OFFSET, u - (APERTURE_BASE >> L1_CHUNK_SHIFT));
++ __sg_page_iter_dma_next(&it);
++ u = (sg_page_iter_dma_address(&it) >> MMU_PAGE_SHIFT);
++ MMU_WR(MMMU_ILLEGAL_ADR_OFFSET, MMMU_ILLEGAL_ADR_ENABLE + u);
++ dma_sync_sgtable_for_device(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++ mmu->dirty = false;
++
++ /* Flush (and enable) the shared TLB cache; enable this MMU. */
++ if (mmu->cache)
++ bcm2712_iommu_cache_flush(mmu->cache);
++ MMU_WR(MMMU_CTRL_OFFSET,
++ MMMU_CTRL_CAP_EXCEEDED_ABORT_EN |
++ MMMU_CTRL_PT_INVALID_ABORT_EN |
++ MMMU_CTRL_WRITE_VIOLATION_ABORT_EN |
++ MMMU_CTRL_STATS_ENABLE |
++ MMMU_CTRL_ENABLE);
++}
++
++static int bcm2712_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
++{
++ struct bcm2712_iommu *mmu = dev ? dev_iommu_priv_get(dev) : 0;
++ struct bcm2712_iommu_domain *mydomain =
++ container_of(domain, struct bcm2712_iommu_domain, base);
++
++ dev_info(dev, "%s: MMU %s\n",
++ __func__, mmu ? dev_name(mmu->dev) : "");
++
++ if (mmu) {
++ mydomain->mmu = mmu;
++ mmu->domain = mydomain;
++
++ if (mmu->dma_iova_offset) {
++ domain->geometry.aperture_start =
++ mmu->dma_iova_offset + APERTURE_BASE;
++ domain->geometry.aperture_end =
++ mmu->dma_iova_offset + APERTURE_TOP - 1ul;
++ }
++
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static void bcm2712_iommu_detach_dev(struct iommu_domain *domain, struct device *dev)
++{
++ (void)domain;
++ (void)dev;
++}
++
++static int bcm2712_iommu_map(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t pa, size_t bytes, int prot, gfp_t gfp)
++{
++ struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++
++ (void)gfp;
++ iova -= mmu->dma_iova_offset;
++ if (iova >= APERTURE_BASE && iova + bytes <= APERTURE_TOP) {
++ unsigned int p;
++ u32 entry = MMMU_PTE_VALID | (pa >> MMU_PAGE_SHIFT);
++ u32 align = (u32)(iova | pa | bytes);
++
++ /* large page and write enable flags */
++ if (!(align & ((1 << HUGEPAGE_SHIFT) - 1)))
++ entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 3);
++ else if (!(align & mmu->superpage_mask) && mmu->superpage_mask)
++ entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 2);
++ else if (!(align & mmu->bigpage_mask) && mmu->bigpage_mask)
++ entry |= FIELD_PREP(MMMU_PTE_PAGESIZE_MASK, 1);
++ if (prot & IOMMU_WRITE)
++ entry |= MMMU_PTE_WRITEABLE;
++
++ /* Ensure tables are cache-coherent with CPU */
++ if (!mmu->dirty) {
++ dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++ mmu->dirty = true;
++ }
++
++ iova -= APERTURE_BASE;
++ for (p = iova >> MMU_PAGE_SHIFT;
++ p < (iova + bytes) >> MMU_PAGE_SHIFT; p++) {
++ mmu->nmapped_pages += !(mmu->tables[p]);
++ mmu->tables[p] = entry++;
++ }
++ } else if (iova + bytes > APERTURE_BASE || iova != pa) {
++ dev_warn(mmu->dev, "%s: iova=0x%lx pa=0x%llx size=0x%llx OUT OF RANGE!\n",
++ __func__, iova,
++ (unsigned long long)pa, (unsigned long long)bytes);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static size_t bcm2712_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
++ size_t bytes, struct iommu_iotlb_gather *gather)
++{
++ struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++
++ if (iova >= mmu->dma_iova_offset + APERTURE_BASE &&
++ iova + bytes <= mmu->dma_iova_offset + APERTURE_TOP) {
++ unsigned int p;
++
++ /* Record just the lower and upper bounds in "gather" */
++ if (gather) {
++ bool empty = (gather->end <= gather->start);
++
++ if (empty || gather->start < iova)
++ gather->start = iova;
++ if (empty || gather->end < iova + bytes)
++ gather->end = iova + bytes;
++ }
++
++ /* Ensure tables are cache-coherent with CPU */
++ if (!mmu->dirty) {
++ dma_sync_sgtable_for_cpu(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++ mmu->dirty = true;
++ }
++
++ /* Clear table entries, this marks the addresses as illegal */
++ iova -= (mmu->dma_iova_offset + APERTURE_BASE);
++ for (p = iova >> MMU_PAGE_SHIFT;
++ p < (iova + bytes) >> MMU_PAGE_SHIFT;
++ p++) {
++ mmu->nmapped_pages -= !!(mmu->tables[p]);
++ mmu->tables[p] = 0;
++ }
++ }
++
++ return bytes;
++}
++
++static void bcm2712_iommu_sync_range(struct iommu_domain *domain,
++ unsigned long iova, size_t size)
++{
++ struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++ unsigned long iova_end;
++ unsigned int i, p4;
++
++ if (!mmu || !mmu->dirty)
++ return;
++
++ /* Ensure tables are cleaned from CPU cache or write-buffer */
++ dma_sync_sgtable_for_device(mmu->dev, mmu->sgt, DMA_TO_DEVICE);
++ mmu->dirty = false;
++
++ /* Flush the shared TLB cache */
++ if (mmu->cache)
++ bcm2712_iommu_cache_flush(mmu->cache);
++
++ /*
++ * When flushing a large range or when nothing needs to be kept,
++ * it's quicker to use the"TLB_CLEAR" flag. Otherwise, invalidate
++ * TLB entries in lines of 4 words each. Each flush/clear operation
++ * should complete almost instantaneously.
++ */
++ iova -= mmu->dma_iova_offset;
++ iova_end = min(APERTURE_TOP, iova + size);
++ iova = max(APERTURE_BASE, iova);
++ if (mmu->nmapped_pages == 0 || iova_end - iova >= APERTURE_SIZE / 8) {
++ MMU_WR(MMMU_CTRL_OFFSET,
++ MMMU_CTRL_CAP_EXCEEDED_ABORT_EN |
++ MMMU_CTRL_PT_INVALID_ABORT_EN |
++ MMMU_CTRL_WRITE_VIOLATION_ABORT_EN |
++ MMMU_CTRL_TLB_CLEAR |
++ MMMU_CTRL_STATS_ENABLE |
++ MMMU_CTRL_ENABLE);
++ for (i = 0; i < 1024; i++) {
++ if (!(MMMU_CTRL_TLB_CLEARING & MMU_RD(MMMU_CTRL_OFFSET)))
++ break;
++ cpu_relax();
++ }
++ } else {
++ for (p4 = iova >> (MMU_PAGE_SHIFT + 2);
++ p4 < (iova_end + 3 * MMU_PAGE_SIZE) >> (MMU_PAGE_SHIFT + 2);
++ p4++) {
++ MMU_WR(MMMU_SHOOT_DOWN_OFFSET,
++ MMMU_SHOOT_DOWN_SHOOT + (p4 << 2));
++ for (i = 0; i < 1024; i++) {
++ if (!(MMMU_SHOOT_DOWN_SHOOTING & MMU_RD(MMMU_SHOOT_DOWN_OFFSET)))
++ break;
++ cpu_relax();
++ }
++ }
++ }
++}
++
++static void bcm2712_iommu_sync(struct iommu_domain *domain,
++ struct iommu_iotlb_gather *gather)
++{
++ bcm2712_iommu_sync_range(domain, gather->start,
++ gather->end - gather->start);
++}
++
++static void bcm2712_iommu_sync_all(struct iommu_domain *domain)
++{
++ bcm2712_iommu_sync_range(domain, APERTURE_BASE, APERTURE_SIZE);
++}
++
++static phys_addr_t bcm2712_iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
++{
++ struct bcm2712_iommu *mmu = domain_to_mmu(domain);
++ u32 p;
++
++ iova -= mmu->dma_iova_offset;
++ if (iova >= APERTURE_BASE && iova < APERTURE_TOP) {
++ p = (iova - APERTURE_BASE) >> MMU_PAGE_SHIFT;
++ p = mmu->tables[p] & 0x0FFFFFFFu;
++ return (((phys_addr_t)p) << MMU_PAGE_SHIFT) + (iova & (MMU_PAGE_SIZE - 1u));
++ } else if (iova < APERTURE_BASE) {
++ return (phys_addr_t)iova;
++ } else {
++ return (phys_addr_t)-EINVAL;
++ }
++}
++
++static void bcm2712_iommu_domain_free(struct iommu_domain *domain)
++{
++ struct bcm2712_iommu_domain *mydomain =
++ container_of(domain, struct bcm2712_iommu_domain, base);
++
++ kfree(mydomain);
++}
++
++static const struct iommu_domain_ops bcm2712_iommu_domain_ops = {
++ .attach_dev = bcm2712_iommu_attach_dev,
++ .detach_dev = bcm2712_iommu_detach_dev,
++ .map = bcm2712_iommu_map,
++ .unmap = bcm2712_iommu_unmap,
++ .iotlb_sync = bcm2712_iommu_sync,
++ .iotlb_sync_map = bcm2712_iommu_sync_range,
++ .flush_iotlb_all = bcm2712_iommu_sync_all,
++ .iova_to_phys = bcm2712_iommu_iova_to_phys,
++ .free = bcm2712_iommu_domain_free,
++};
++
++static struct iommu_domain *bcm2712_iommu_domain_alloc(unsigned int type)
++{
++ struct bcm2712_iommu_domain *domain;
++
++ if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
++ return NULL;
++
++ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
++ if (!domain)
++ return NULL;
++
++ domain->base.type = type;
++ domain->base.ops = &bcm2712_iommu_domain_ops;
++ domain->base.geometry.aperture_start = APERTURE_BASE;
++ domain->base.geometry.aperture_end = APERTURE_TOP - 1ul;
++ domain->base.geometry.force_aperture = true;
++ return &domain->base;
++}
++
++static struct iommu_device *bcm2712_iommu_probe_device(struct device *dev)
++{
++ struct bcm2712_iommu *mmu;
++
++ /*
++ * For reasons I don't fully understand, we need to try both
++ * cases (dev_iommu_priv_get() and platform_get_drvdata())
++ * in order to get both GPU and ISP-BE to probe successfully.
++ */
++ mmu = dev_iommu_priv_get(dev);
++ if (!mmu) {
++ struct device_node *np;
++ struct platform_device *pdev;
++
++ /* Ignore devices that don't have an "iommus" property with exactly one phandle */
++ if (!dev->of_node ||
++ of_property_count_elems_of_size(dev->of_node, "iommus", sizeof(phandle)) != 1)
++ return ERR_PTR(-ENODEV);
++
++ np = of_parse_phandle(dev->of_node, "iommus", 0);
++ if (!np)
++ return ERR_PTR(-EINVAL);
++
++ pdev = of_find_device_by_node(np);
++ of_node_put(np);
++ if (pdev)
++ mmu = platform_get_drvdata(pdev);
++
++ if (!mmu)
++ return ERR_PTR(-ENODEV);
++ }
++
++ dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev));
++ dev_iommu_priv_set(dev, mmu);
++ return &mmu->iommu;
++}
++
++static void bcm2712_iommu_release_device(struct device *dev)
++{
++ dev_iommu_priv_set(dev, NULL);
++}
++
++static struct iommu_group *bcm2712_iommu_device_group(struct device *dev)
++{
++ struct bcm2712_iommu *mmu = dev_iommu_priv_get(dev);
++
++ if (!mmu || !mmu->group)
++ return ERR_PTR(-EINVAL);
++
++ dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev));
++ return iommu_group_ref_get(mmu->group);
++}
++
++static int bcm2712_iommu_of_xlate(struct device *dev,
++ struct of_phandle_args *args)
++{
++ struct platform_device *iommu_dev;
++ struct bcm2712_iommu *mmu;
++
++ iommu_dev = of_find_device_by_node(args->np);
++ mmu = platform_get_drvdata(iommu_dev);
++ dev_iommu_priv_set(dev, mmu);
++ dev_info(dev, "%s: MMU %s\n", __func__, dev_name(mmu->dev));
++
++ return 0;
++}
++
++static bool bcm2712_iommu_capable(struct device *dev, enum iommu_cap cap)
++{
++ return false;
++}
++
++static const struct iommu_ops bcm2712_iommu_ops = {
++ .capable = bcm2712_iommu_capable,
++ .domain_alloc = bcm2712_iommu_domain_alloc,
++ .probe_device = bcm2712_iommu_probe_device,
++ .release_device = bcm2712_iommu_release_device,
++ .device_group = bcm2712_iommu_device_group,
++ /* Advertise native page sizes as well as 2M, 16K which Linux may prefer */
++ .pgsize_bitmap = (SZ_4M | SZ_2M | SZ_1M | SZ_64K | SZ_16K | SZ_4K),
++ .default_domain_ops = &bcm2712_iommu_domain_ops,
++ .of_xlate = bcm2712_iommu_of_xlate,
++};
++
++static int bcm2712_iommu_probe(struct platform_device *pdev)
++{
++ struct bcm2712_iommu *mmu;
++ struct bcm2712_iommu_cache *cache = NULL;
++ int ret;
++
++ /* First of all, check for an IOMMU shared cache */
++ if (pdev->dev.of_node) {
++ struct device_node *cache_np;
++ struct platform_device *cache_pdev;
++
++ cache_np = of_parse_phandle(pdev->dev.of_node, "cache", 0);
++ if (cache_np) {
++ cache_pdev = of_find_device_by_node(cache_np);
++ of_node_put(cache_np);
++ if (cache_pdev && !IS_ERR(cache_pdev))
++ cache = platform_get_drvdata(cache_pdev);
++ if (!cache)
++ return -EPROBE_DEFER;
++ }
++ }
++
++ /* Allocate private data */
++ mmu = devm_kzalloc(&pdev->dev, sizeof(*mmu), GFP_KERNEL);
++ if (!mmu)
++ return -ENOMEM;
++
++ mmu->name = dev_name(&pdev->dev);
++ mmu->dev = &pdev->dev;
++ mmu->cache = cache;
++ platform_set_drvdata(pdev, mmu);
++ spin_lock_init(&mmu->hw_lock);
++
++ /*
++ * XXX When an IOMMU is downstream of a PCIe RC or some other chip/bus
++ * and serves some of the masters thereon (others using pass-through),
++ * we seem to fumble and lose the "dma-ranges" address offset for
++ * masters using IOMMU. This property restores it, where needed.
++ */
++ if (!pdev->dev.of_node ||
++ of_property_read_u64(pdev->dev.of_node, "dma-iova-offset",
++ &mmu->dma_iova_offset))
++ mmu->dma_iova_offset = 0;
++
++ /*
++ * The IOMMU is itself a device that allocates DMA-able memory
++ * to hold its translation tables. Provided the IOVA aperture
++ * is no larger than 4 GBytes (so that the L1 table fits within
++ * a single 4K page), we don't need the tables to be contiguous.
++ * Assume we can address at least 36 bits (64 GB).
++ */
++ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36));
++ WARN_ON(ret);
++ mmu->sgt = dma_alloc_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++ DMA_TO_DEVICE, GFP_KERNEL,
++ DMA_ATTR_ALLOC_SINGLE_PAGES);
++ if (!mmu->sgt) {
++ ret = -ENOMEM;
++ goto done_err;
++ }
++ mmu->tables = dma_vmap_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++ mmu->sgt);
++ if (!mmu->tables) {
++ ret = -ENOMEM;
++ goto done_err;
++ }
++
++ /* Get IOMMU registers */
++ mmu->reg_base = devm_platform_ioremap_resource(pdev, 0);
++ if (IS_ERR(mmu->reg_base)) {
++ dev_err(&pdev->dev, "Failed to get IOMMU registers address\n");
++ ret = PTR_ERR(mmu->reg_base);
++ goto done_err;
++ }
++
++ /* Stuff */
++ mmu->group = iommu_group_alloc();
++ if (IS_ERR(mmu->group)) {
++ ret = PTR_ERR(mmu->group);
++ mmu->group = NULL;
++ goto done_err;
++ }
++ ret = iommu_device_sysfs_add(&mmu->iommu, mmu->dev, NULL, mmu->name);
++ if (ret)
++ goto done_err;
++
++ /* Initialize table and hardware */
++ bcm2712_iommu_init(mmu);
++ ret = iommu_device_register(&mmu->iommu, &bcm2712_iommu_ops, &pdev->dev);
++
++ dev_info(&pdev->dev, "%s: Success\n", __func__);
++ return 0;
++
++done_err:
++ dev_info(&pdev->dev, "%s: Failure %d\n", __func__, ret);
++ if (mmu->group)
++ iommu_group_put(mmu->group);
++ if (mmu->tables)
++ dma_vunmap_noncontiguous(&pdev->dev,
++ (void *)(mmu->tables));
++ mmu->tables = NULL;
++ if (mmu->sgt)
++ dma_free_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++ mmu->sgt, DMA_TO_DEVICE);
++ mmu->sgt = NULL;
++ kfree(mmu);
++ return ret;
++}
++
++static int bcm2712_iommu_remove(struct platform_device *pdev)
++{
++ struct bcm2712_iommu *mmu = platform_get_drvdata(pdev);
++
++ if (mmu->reg_base)
++ MMU_WR(MMMU_CTRL_OFFSET, 0); /* disable the MMU */
++ if (mmu->sgt)
++ dma_free_noncontiguous(&pdev->dev, TABLES_ALLOC_SIZE,
++ mmu->sgt, DMA_TO_DEVICE);
++
++ return 0;
++}
++
++static const struct of_device_id bcm2712_iommu_of_match[] = {
++ {
++ . compatible = "brcm,bcm2712-iommu"
++ },
++ { /* sentinel */ },
++};
++
++static struct platform_driver bcm2712_iommu_driver = {
++ .probe = bcm2712_iommu_probe,
++ .remove = bcm2712_iommu_remove,
++ .driver = {
++ .name = "bcm2712-iommu",
++ .of_match_table = bcm2712_iommu_of_match
++ },
++};
++
++builtin_platform_driver(bcm2712_iommu_driver);
+--- /dev/null
++++ b/drivers/iommu/bcm2712-iommu.h
+@@ -0,0 +1,45 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * IOMMU driver for BCM2712
++ *
++ * Copyright (c) 2023 Raspberry Pi Ltd.
++ */
++
++#ifndef _BCM2712_IOMMU_H
++#define _BCM2712_IOMMU_H
++
++#include <linux/iommu.h>
++#include <linux/scatterlist.h>
++
++struct bcm2712_iommu_cache {
++ struct device *dev;
++ spinlock_t hw_lock; /* to protect HW registers */
++ void __iomem *reg_base;
++};
++
++void bcm2712_iommu_cache_flush(struct bcm2712_iommu_cache *cache);
++
++struct bcm2712_iommu {
++ struct device *dev;
++ struct iommu_device iommu;
++ struct iommu_group *group;
++ struct bcm2712_iommu_domain *domain;
++ char const *name;
++ struct sg_table *sgt; /* allocated memory for page tables */
++ u32 *tables; /* kernel mapping for page tables */
++ struct bcm2712_iommu_cache *cache;
++ spinlock_t hw_lock; /* to protect HW registers */
++ void __iomem *reg_base;
++ u64 dma_iova_offset; /* Hack for IOMMU attached to PCIe RC */
++ u32 bigpage_mask;
++ u32 superpage_mask;
++ unsigned int nmapped_pages;
++ bool dirty; /* true when tables are oriented towards CPU */
++};
++
++struct bcm2712_iommu_domain {
++ struct iommu_domain base;
++ struct bcm2712_iommu *mmu;
++};
++
++#endif
--- /dev/null
+From fa4d4ed28c92cf4470e518f1a7362dc7941632d7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 28 Jun 2023 16:24:29 +0100
+Subject: [PATCH] irqchip/irq-brcmstb-l2: Add config for 2711 controller
+
+We currently see these regularly:
+[ 25.157560] irq 31, desc: 00000000c15e6d2c, depth: 0, count: 0, unhandled: 0
+[ 25.164658] ->handle_irq(): 00000000b1775675, brcmstb_l2_intc_irq_handle+0x0/0x1a8
+[ 25.172352] ->irq_data.chip(): 00000000fea59f1c, gic_chip_mode1+0x0/0x108
+[ 25.179166] ->action(): 000000003eda6d6f
+[ 25.183096] ->action->handler(): 000000002c09e646, bad_chained_irq+0x0/0x58
+[ 25.190084] IRQ_LEVEL set
+[ 25.193142] IRQ_NOPROBE set
+[ 25.196198] IRQ_NOREQUEST set
+[ 25.199255] IRQ_NOTHREAD set
+
+with:
+$ cat /proc/interrupts | grep 31:
+ 31: 1 0 0 0 GICv2 129 Level (null)
+
+The interrupt is described in DT with IRQ_TYPE_LEVEL_HIGH
+
+But the current compatible string uses the controller in edge triggered mode
+(as that config matches our register layout).
+
+Add a new compatible structure for level driven interrupt with our register layout.
+
+We had already been using this compatible string in device tree, so no change needed
+there.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/irqchip/irq-brcmstb-l2.c | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+
+--- a/drivers/irqchip/irq-brcmstb-l2.c
++++ b/drivers/irqchip/irq-brcmstb-l2.c
+@@ -52,6 +52,16 @@ static const struct brcmstb_intc_init_pa
+ .cpu_mask_clear = 0x0C
+ };
+
++/* Register offsets in the 2711 L2 level interrupt controller */
++static const struct brcmstb_intc_init_params l2_2711_lvl_intc_init = {
++ .handler = handle_level_irq,
++ .cpu_status = 0x00,
++ .cpu_clear = 0x08,
++ .cpu_mask_status = 0x0c,
++ .cpu_mask_set = 0x10,
++ .cpu_mask_clear = 0x14
++};
++
+ /* L2 intc private data structure */
+ struct brcmstb_l2_intc_data {
+ struct irq_domain *domain;
+@@ -286,11 +296,18 @@ static int __init brcmstb_l2_lvl_intc_of
+ return brcmstb_l2_intc_of_init(np, parent, &l2_lvl_intc_init);
+ }
+
++static int __init brcmstb_l2_2711_lvl_intc_of_init(struct device_node *np,
++ struct device_node *parent)
++{
++ return brcmstb_l2_intc_of_init(np, parent, &l2_2711_lvl_intc_init);
++}
++
+ IRQCHIP_PLATFORM_DRIVER_BEGIN(brcmstb_l2)
+ IRQCHIP_MATCH("brcm,l2-intc", brcmstb_l2_edge_intc_of_init)
+ IRQCHIP_MATCH("brcm,hif-spi-l2-intc", brcmstb_l2_edge_intc_of_init)
+ IRQCHIP_MATCH("brcm,upg-aux-aon-l2-intc", brcmstb_l2_edge_intc_of_init)
+ IRQCHIP_MATCH("brcm,bcm7271-l2-intc", brcmstb_l2_lvl_intc_of_init)
++IRQCHIP_MATCH("brcm,bcm2711-l2-intc", brcmstb_l2_2711_lvl_intc_of_init)
+ IRQCHIP_PLATFORM_DRIVER_END(brcmstb_l2)
+ MODULE_DESCRIPTION("Broadcom STB generic L2 interrupt controller");
+ MODULE_LICENSE("GPL v2");
--- /dev/null
+From 222dedcdc09247126d39364a614ff2019789f52a Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 7 Jul 2023 20:00:45 +0100
+Subject: [PATCH] rtc: rtc-rpi: Add simple RTC driver for Raspberry Pi
+
+This supports setting and reading the real time clock
+and supports wakeup alarms.
+
+To support wake up alarms you want this bootloader config:
+ POWER_OFF_ON_HALT=1
+ WAKE_ON_GPIO=0
+
+You can test with:
+ echo +600 | sudo tee /sys/class/rtc/rtc0/wakealarm
+ sudo halt
+
+That will halt (in an almost no power state),
+then wake and restart after 10 minutes.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/rtc/Kconfig | 11 +++
+ drivers/rtc/Makefile | 1 +
+ drivers/rtc/rtc-rpi.c | 177 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 189 insertions(+)
+ create mode 100644 drivers/rtc/rtc-rpi.c
+
+--- a/drivers/rtc/Kconfig
++++ b/drivers/rtc/Kconfig
+@@ -223,6 +223,17 @@ config RTC_DRV_AC100
+ This driver can also be built as a module. If so, the module
+ will be called rtc-ac100.
+
++config RTC_DRV_RPI
++ tristate "Raspberry Pi RTC"
++ depends on ARCH_BRCMSTB || COMPILE_TEST
++ default ARCH_BRCMSTB
++ help
++ If you say yes here you get support for the RTC found on
++ Raspberry Pi devices.
++
++ This driver can also be built as a module. If so, the module
++ will be called rtc-rpi.
++
+ config RTC_DRV_BRCMSTB
+ tristate "Broadcom STB wake-timer"
+ depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST
+--- a/drivers/rtc/Makefile
++++ b/drivers/rtc/Makefile
+@@ -140,6 +140,7 @@ obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5
+ obj-$(CONFIG_RTC_DRV_RC5T619) += rtc-rc5t619.o
+ obj-$(CONFIG_RTC_DRV_RK808) += rtc-rk808.o
+ obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
++obj-$(CONFIG_RTC_DRV_RPI) += rtc-rpi.o
+ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
+ obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
+ obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
+--- /dev/null
++++ b/drivers/rtc/rtc-rpi.c
+@@ -0,0 +1,177 @@
++// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
++/**
++ * rtc-rpi.c
++ *
++ * RTC driver using firmware mailbox
++ * Supports battery backed RTC and wake alarms
++ *
++ * Based on rtc-meson-vrtc by Neil Armstrong
++ *
++ * Copyright (c) 2023, Raspberry Pi Ltd.
++ */
++
++#include <linux/module.h>
++#include <linux/platform_device.h>
++#include <linux/rtc.h>
++#include <linux/of.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++
++struct rpi_rtc_data {
++ struct rtc_device *rtc;
++ struct rpi_firmware *fw;
++};
++
++#define RPI_FIRMWARE_GET_RTC_REG 0x00030087
++#define RPI_FIRMWARE_SET_RTC_REG 0x00038087
++enum {RTC_TIME, RTC_ALARM, RTC_ALARM_PENDING, RTC_ALARM_ENABLE};
++
++static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_TIME};
++ int err;
++
++ err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++ &data, sizeof(data));
++ rtc_time64_to_tm(data[1], tm);
++ return err;
++}
++
++static int rpi_rtc_set_time(struct device *dev, struct rtc_time *tm)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_TIME, rtc_tm_to_time64(tm)};
++
++ return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++ &data, sizeof(data));
++}
++
++static int rpi_rtc_alarm_irq_is_enabled(struct device *dev, unsigned char *enabled)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_ALARM_ENABLE};
++ s32 err = 0;
++
++ err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++ &data, sizeof(data));
++ *enabled = data[1] & 0x1;
++ return err;
++}
++
++static int rpi_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_ALARM_ENABLE, enabled};
++
++ return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++ &data, sizeof(data));
++}
++
++static int rpi_rtc_alarm_clear_pending(struct device *dev)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_ALARM_PENDING, 1};
++
++ return rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++ &data, sizeof(data));
++}
++
++static int rpi_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_ALARM};
++ s32 err = 0;
++
++ err = rpi_rtc_alarm_irq_is_enabled(dev, &alarm->enabled);
++ if (!err)
++ err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++ &data, sizeof(data));
++ rtc_time64_to_tm(data[1], &alarm->time);
++
++ return err;
++}
++
++static int rpi_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_ALARM, rtc_tm_to_time64(&alarm->time)};
++ int err;
++
++ err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++ &data, sizeof(data));
++
++ if (err == 0)
++ err = rpi_rtc_alarm_irq_enable(dev, alarm->enabled);
++
++ return err;
++}
++
++static const struct rtc_class_ops rpi_rtc_ops = {
++ .read_time = rpi_rtc_read_time,
++ .set_time = rpi_rtc_set_time,
++ .read_alarm = rpi_rtc_read_alarm,
++ .set_alarm = rpi_rtc_set_alarm,
++ .alarm_irq_enable = rpi_rtc_alarm_irq_enable,
++};
++
++static int rpi_rtc_probe(struct platform_device *pdev)
++{
++ struct rpi_rtc_data *vrtc;
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ struct device_node *fw_node;
++ struct rpi_firmware *fw;
++ int ret;
++
++ fw_node = of_parse_phandle(np, "firmware", 0);
++ if (!fw_node) {
++ dev_err(dev, "Missing firmware node\n");
++ return -ENOENT;
++ }
++
++ fw = rpi_firmware_get(fw_node);
++ if (!fw)
++ return -EPROBE_DEFER;
++
++ vrtc = devm_kzalloc(&pdev->dev, sizeof(*vrtc), GFP_KERNEL);
++ if (!vrtc)
++ return -ENOMEM;
++
++ vrtc->fw = fw;
++
++ device_init_wakeup(&pdev->dev, 1);
++
++ platform_set_drvdata(pdev, vrtc);
++
++ vrtc->rtc = devm_rtc_allocate_device(&pdev->dev);
++ if (IS_ERR(vrtc->rtc))
++ return PTR_ERR(vrtc->rtc);
++
++ set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, vrtc->rtc->features);
++ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, vrtc->rtc->features);
++
++ vrtc->rtc->ops = &rpi_rtc_ops;
++ ret = devm_rtc_register_device(vrtc->rtc);
++
++ rpi_rtc_alarm_clear_pending(dev);
++ return ret;
++}
++
++static const struct of_device_id rpi_rtc_dt_match[] = {
++ { .compatible = "raspberrypi,rpi-rtc"},
++ {},
++};
++MODULE_DEVICE_TABLE(of, rpi_rtc_dt_match);
++
++static struct platform_driver rpi_rtc_driver = {
++ .probe = rpi_rtc_probe,
++ .driver = {
++ .name = "rpi-rtc",
++ .of_match_table = rpi_rtc_dt_match,
++ },
++};
++
++module_platform_driver(rpi_rtc_driver);
++
++MODULE_DESCRIPTION("Raspberry Pi RTC driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From ff2c2f67689e10ad66c1e33ae6a7552d82ac983c Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Fri, 7 Jul 2023 20:16:06 +0100
+Subject: [PATCH] dt-bindings: rtc: new binding for Raspberry Pi RTC driver
+
+Add binding for the new RTC driver for Raspberry Pi.
+This platform has an RTC managed by firmware, and this RTC
+driver provides the simple mailbox interface to access it.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ .../devicetree/bindings/rtc/rtc-rpi.txt | 17 +++++++++++++++++
+ 1 file changed, 17 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/rtc/rtc-rpi.txt
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/rtc/rtc-rpi.txt
+@@ -0,0 +1,17 @@
++* Raspberry Pi RTC
++
++This is a Linux interface to an RTC managed by firmware, hence it's
++virtual from a Linux perspective.
++
++The interface uses the firmware mailbox api to access the RTC registers.
++
++Required properties:
++compatible: should be "raspberrypi,rpi-rtc"
++firmware: Reference to the RPi firmware device node.
++
++Example:
++
++ rpi_rtc: rpi_rtc {
++ compatible = "raspberrypi,rpi-rtc";
++ firmware = <&firmware>;
++ };
--- /dev/null
+From 96a8a4776cb142f5d2bb7f6379df9af40e727c0b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 11 Jul 2023 10:17:29 +0100
+Subject: [PATCH] hwmon: (pwm-fan) Add fan speed register support
+
+Some platforms include a fan-speed register that reports RPM directly
+as an alternative to counting interrupts from the fan tachometer input.
+Add support for reading a register at a given offset (rpm-offset) within
+a block declared in another node (rpm-regmap). This indirection allows
+the usual address mapping to be performed, and for address sharing with
+another driver.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/hwmon/pwm-fan.c | 59 ++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 52 insertions(+), 7 deletions(-)
+
+--- a/drivers/hwmon/pwm-fan.c
++++ b/drivers/hwmon/pwm-fan.c
+@@ -12,6 +12,7 @@
+ #include <linux/module.h>
+ #include <linux/mutex.h>
+ #include <linux/of.h>
++#include <linux/of_address.h>
+ #include <linux/platform_device.h>
+ #include <linux/pwm.h>
+ #include <linux/regulator/consumer.h>
+@@ -51,6 +52,9 @@ struct pwm_fan_ctx {
+ ktime_t sample_start;
+ struct timer_list rpm_timer;
+
++ void __iomem *rpm_regbase;
++ unsigned int rpm_offset;
++
+ unsigned int pwm_value;
+ unsigned int pwm_fan_state;
+ unsigned int pwm_fan_max_state;
+@@ -61,6 +65,10 @@ struct pwm_fan_ctx {
+ struct hwmon_channel_info fan_channel;
+ };
+
++static const u32 rpm_reg_channel_config[] = {
++ HWMON_F_INPUT, 0
++};
++
+ /* This handler assumes self resetting edge triggered interrupt. */
+ static irqreturn_t pulse_handler(int irq, void *dev_id)
+ {
+@@ -335,7 +343,10 @@ static int pwm_fan_read(struct device *d
+ }
+ return -EOPNOTSUPP;
+ case hwmon_fan:
+- *val = ctx->tachs[channel].rpm;
++ if (ctx->rpm_regbase)
++ *val = (long)readl(ctx->rpm_regbase + ctx->rpm_offset);
++ else
++ *val = ctx->tachs[channel].rpm;
+ return 0;
+
+ default:
+@@ -470,6 +481,7 @@ static void pwm_fan_cleanup(void *__ctx)
+ /* Switch off everything */
+ ctx->enable_mode = pwm_disable_reg_disable;
+ pwm_fan_power_off(ctx);
++ iounmap(ctx->rpm_regbase);
+ }
+
+ static int pwm_fan_probe(struct platform_device *pdev)
+@@ -534,10 +546,23 @@ static int pwm_fan_probe(struct platform
+ return ret;
+
+ ctx->tach_count = platform_irq_count(pdev);
++ if (ctx->tach_count == 0) {
++ struct device_node *rpm_node;
++
++ rpm_node = of_parse_phandle(dev->of_node, "rpm-regmap", 0);
++ if (rpm_node)
++ ctx->rpm_regbase = of_iomap(rpm_node, 0);
++ }
++
+ if (ctx->tach_count < 0)
+ return dev_err_probe(dev, ctx->tach_count,
+ "Could not get number of fan tachometer inputs\n");
+- dev_dbg(dev, "%d fan tachometer inputs\n", ctx->tach_count);
++ if (IS_ERR(ctx->rpm_regbase))
++ return dev_err_probe(dev, PTR_ERR(ctx->rpm_regbase),
++ "Could not get rpm reg\n");
++
++ dev_dbg(dev, "%d fan tachometer inputs, %d rpm regmap\n", ctx->tach_count,
++ !!ctx->rpm_regbase);
+
+ if (ctx->tach_count) {
+ channel_count++; /* We also have a FAN channel. */
+@@ -554,12 +579,24 @@ static int pwm_fan_probe(struct platform
+ if (!fan_channel_config)
+ return -ENOMEM;
+ ctx->fan_channel.config = fan_channel_config;
++ } else if (ctx->rpm_regbase) {
++ channel_count++; /* We also have a FAN channel. */
++ ctx->fan_channel.type = hwmon_fan;
++ ctx->fan_channel.config = rpm_reg_channel_config;
++
++ if (of_property_read_u32(pdev->dev.of_node, "rpm-offset", &ctx->rpm_offset)) {
++ dev_err(&pdev->dev, "unable to read 'rpm-offset'");
++ ret = -EINVAL;
++ goto error;
++ }
+ }
+
+ channels = devm_kcalloc(dev, channel_count + 1,
+ sizeof(struct hwmon_channel_info *), GFP_KERNEL);
+- if (!channels)
+- return -ENOMEM;
++ if (!channels) {
++ ret = -ENOMEM;
++ goto error;
++ }
+
+ channels[0] = HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE);
+
+@@ -602,6 +639,8 @@ static int pwm_fan_probe(struct platform
+ mod_timer(&ctx->rpm_timer, jiffies + HZ);
+
+ channels[1] = &ctx->fan_channel;
++ } else if (ctx->rpm_regbase) {
++ channels[1] = &ctx->fan_channel;
+ }
+
+ ctx->info.ops = &pwm_fan_hwmon_ops;
+@@ -611,12 +650,13 @@ static int pwm_fan_probe(struct platform
+ ctx, &ctx->info, NULL);
+ if (IS_ERR(hwmon)) {
+ dev_err(dev, "Failed to register hwmon device\n");
+- return PTR_ERR(hwmon);
++ ret = PTR_ERR(hwmon);
++ goto error;
+ }
+
+ ret = pwm_fan_of_get_cooling_data(dev, ctx);
+ if (ret)
+- return ret;
++ goto error;
+
+ ctx->pwm_fan_state = ctx->pwm_fan_max_state;
+ if (IS_ENABLED(CONFIG_THERMAL)) {
+@@ -627,12 +667,17 @@ static int pwm_fan_probe(struct platform
+ dev_err(dev,
+ "Failed to register pwm-fan as cooling device: %d\n",
+ ret);
+- return ret;
++ goto error;
+ }
+ ctx->cdev = cdev;
+ }
+
+ return 0;
++
++error:
++ if (ctx->rpm_regbase)
++ iounmap(ctx->rpm_regbase);
++ return ret;
+ }
+
+ static void pwm_fan_shutdown(struct platform_device *pdev)
--- /dev/null
+From 07419175fdb507be2c9d3aaf4b7d18306a336348 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 11:38:03 +0100
+Subject: [PATCH] dt-bindings: input: Add bindings for raspberrypi-button
+
+Add bindings for the firmware-based button driver.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ .../input/raspberrypi,firmware-button.yaml | 47 +++++++++++++++++++
+ 1 file changed, 47 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/input/raspberrypi,firmware-button.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/input/raspberrypi,firmware-button.yaml
+@@ -0,0 +1,47 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/input/raspberrypi,firmware-button.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Raspberry Pi firmware buttons
++
++maintainers:
++ - Phil Elwell <phil@raspberrypi.com>
++
++description: >
++ The Raspberry Pi 5 firmware exposes the state of the power button. The
++ raspberrypi-button driver generates a keycode when it is pressed.
++
++properties:
++ compatible:
++ enum:
++ - raspberrypi,firmware-button
++
++ id:
++ description: A numeric identifier of the button
++
++ label:
++ description: Descriptive name of the button.
++
++ linux,code:
++ description: Key code to emit.
++
++required:
++ - compatible
++ - linux,code
++
++additionalProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/input/raspberrypi-button.h>
++
++ pwr_button: pwr_button {
++ compatible = "raspberrypi,firmware-button";
++ id = <RASPBERRYPI_BUTTON_POWER>;
++ label = "pwr_button";
++ linux,code = <116>; // KEY_POWER
++ };
++
++...
--- /dev/null
+From 93c8947bc7813b49fe27a5251eef97c6df1e14c6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 15:01:29 +0100
+Subject: [PATCH] dt-bindings: input: Add bindings for raspberrypi-button
+
+Add bindings for the firmware-based button driver.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ include/dt-bindings/input/raspberrypi-button.h | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+ create mode 100644 include/dt-bindings/input/raspberrypi-button.h
+
+--- /dev/null
++++ b/include/dt-bindings/input/raspberrypi-button.h
+@@ -0,0 +1,11 @@
++/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
++/*
++ * This header provides constants the raspberrypi-button driver.
++ */
++
++#ifndef _DT_BINDINGS_RASPBERRYPI_BUTTON_H
++#define _DT_BINDINGS_RASPBERRYPI_BUTTON_H
++
++#define RASPBERRYPI_BUTTON_POWER 0
++
++#endif
--- /dev/null
+From 7c0d40384b0648030d5202114d90239b8db7d4e0 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 2 Aug 2023 11:30:48 +0100
+Subject: [PATCH] Input: Add raspberrypi-button firmware driver
+
+Raspberry Pi 5s have a power/suspend button that is only accessible to
+the firmware. Add a driver to read it and generate key events.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/input/misc/Kconfig | 10 ++
+ drivers/input/misc/Makefile | 1 +
+ drivers/input/misc/raspberrypi-button.c | 138 +++++++++++++++++++++
+ include/soc/bcm2835/raspberrypi-firmware.h | 1 +
+ 4 files changed, 150 insertions(+)
+ create mode 100644 drivers/input/misc/raspberrypi-button.c
+
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -918,6 +918,16 @@ config INPUT_RT5120_PWRKEY
+ To compile this driver as a module, choose M here. the module will
+ be called rt5120-pwrkey.
+
++config INPUT_RASPBERRYPI_BUTTON
++ tristate "Raspberry Pi button support"
++ depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
++ help
++ This enables support for firmware-controlled buttons on Raspberry
++ Pi devices.
++
++ To compile this driver as a module, choose M here. the module will
++ be called raspberrypi-button.
++
+ config INPUT_STPMIC1_ONKEY
+ tristate "STPMIC1 PMIC Onkey support"
+ depends on MFD_STPMIC1
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -70,6 +70,7 @@ obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) +=
+ obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
+ obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
+ obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
++obj-$(CONFIG_INPUT_RASPBERRYPI_BUTTON) += raspberrypi-button.o
+ obj-$(CONFIG_INPUT_RT5120_PWRKEY) += rt5120-pwrkey.o
+ obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
+ obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
+--- /dev/null
++++ b/drivers/input/misc/raspberrypi-button.c
+@@ -0,0 +1,138 @@
++// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
++/*
++ * Driver for Raspberry Pi power button
++ *
++ * Copyright (C) 2023 Raspberry Pi Ltd.
++ *
++ * This driver is based on drivers/hwmon/raspberrypi-hwmon.c and
++ * input/misc/pm8941-pwrkey.c/ - see original files for copyright information
++ */
++
++#include <linux/delay.h>
++#include <linux/devm-helpers.h>
++#include <dt-bindings/input/raspberrypi-button.h>
++#include <linux/errno.h>
++#include <linux/input.h>
++#include <linux/kernel.h>
++#include <linux/ktime.h>
++#include <linux/module.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/of_device.h>
++#include <linux/platform_device.h>
++#include <linux/workqueue.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
++
++struct rpi_button {
++ struct device *dev;
++ struct rpi_firmware *fw;
++ struct input_dev *input;
++ struct delayed_work poll_work;
++ unsigned long poll_rate;
++ const char *name;
++ u32 id;
++ u32 code;
++};
++
++static void button_poll(struct work_struct *work)
++{
++ struct rpi_button *button;
++ u32 value;
++ int err;
++
++ button = container_of(work, struct rpi_button,
++ poll_work.work);
++
++ value = BIT(button->id);
++ err = rpi_firmware_property(button->fw, RPI_FIRMWARE_GET_BUTTONS_PRESSED,
++ &value, sizeof(value));
++ if (err) {
++ dev_err_once(button->dev, "GET_BUTTON_PRESSED not implemented?\n");
++ return;
++ }
++
++ if (value & BIT(button->id)) {
++ input_event(button->input, EV_KEY, button->code, 1);
++ input_sync(button->input);
++ input_event(button->input, EV_KEY, button->code, 0);
++ input_sync(button->input);
++ }
++
++ schedule_delayed_work(&button->poll_work, button->poll_rate);
++}
++
++static int rpi_button_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct rpi_button *button;
++ int err;
++
++ button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL);
++ if (!button)
++ return -ENOMEM;
++
++ button->dev = dev;
++
++ /* Get the firmware pointer from our parent */
++ button->fw = dev_get_drvdata(dev->parent);
++
++ if (device_property_read_u32(dev, "id", &button->id))
++ button->id = RASPBERRYPI_BUTTON_POWER;
++
++ if (device_property_read_string(dev, "label", &button->name))
++ button->name = "raspberrypi-button";
++
++ if (device_property_read_u32(dev, "linux,code", &button->code)) {
++ dev_err(&pdev->dev, "no linux,code property\n");
++ return -EINVAL;
++ }
++
++ button->input = devm_input_allocate_device(dev);
++ if (!button->input) {
++ dev_dbg(&pdev->dev, "unable to allocate input device\n");
++ return -ENOMEM;
++ }
++
++ input_set_capability(button->input, EV_KEY, button->code);
++
++ button->input->name = button->name;
++ button->input->phys = "raspberrypi-button/input0";
++ button->input->dev.parent = dev;
++ button->poll_rate = HZ;
++
++ err = input_register_device(button->input);
++ if (err) {
++ dev_err(&pdev->dev, "failed to register input device: %d\n",
++ err);
++ return err;
++ }
++
++ err = devm_delayed_work_autocancel(dev, &button->poll_work,
++ button_poll);
++ if (err)
++ return err;
++
++ platform_set_drvdata(pdev, button);
++ schedule_delayed_work(&button->poll_work, button->poll_rate);
++
++ return 0;
++}
++
++static const struct of_device_id rpi_button_match[] = {
++ { .compatible = "raspberrypi,firmware-button", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, rpi_button_match);
++
++static struct platform_driver rpi_button_driver = {
++ .probe = rpi_button_probe,
++ .driver = {
++ .name = "raspberrypi-button",
++ .of_match_table = of_match_ptr(rpi_button_match),
++ },
++};
++module_platform_driver(rpi_button_driver);
++
++MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
++MODULE_DESCRIPTION("Raspberry Pi button driver");
++MODULE_LICENSE("Dual BSD/GPL");
+--- a/include/soc/bcm2835/raspberrypi-firmware.h
++++ b/include/soc/bcm2835/raspberrypi-firmware.h
+@@ -98,6 +98,7 @@ 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_BUTTONS_PRESSED = 0x00030088,
+
+ /* Dispmanx TAGS */
+ RPI_FIRMWARE_FRAMEBUFFER_ALLOCATE = 0x00040001,
--- /dev/null
+From a7a3679a148e40879f1ce77580d9edf64cb5b51c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Mon, 18 Sep 2023 16:33:06 +0100
+Subject: [PATCH] dt: bindings: update rpi-rtc binding
+
+Add property for bcm2712 firmware RTC driver charger control
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/rtc/rtc-rpi.txt | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/Documentation/devicetree/bindings/rtc/rtc-rpi.txt
++++ b/Documentation/devicetree/bindings/rtc/rtc-rpi.txt
+@@ -9,9 +9,14 @@ Required properties:
+ compatible: should be "raspberrypi,rpi-rtc"
+ firmware: Reference to the RPi firmware device node.
+
++Optional property:
++trickle-charge-microvolt: specify a trickle charge voltage for the backup
++ battery in microvolts.
++
+ Example:
+
+ rpi_rtc: rpi_rtc {
+ compatible = "raspberrypi,rpi-rtc";
+ firmware = <&firmware>;
++ trickle-charge-microvolt = <3000000>;
+ };
--- /dev/null
+From 33b514cb16dbf13395a0becf7442d19676ae4224 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Fri, 15 Sep 2023 17:33:03 +0100
+Subject: [PATCH] drivers: rtc-rpi: add battery charge circuit control and
+ readback
+
+Parse devicetree for a charger voltage and apply it. If nonzero and a
+valid voltage, the firmware will enable charging, otherwise the charger
+circuit is disabled.
+
+Add sysfs attributes to read back the supported charge voltage range,
+the measured battery voltage, and the charger setpoint.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/rtc/rtc-rpi.c | 106 ++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 103 insertions(+), 3 deletions(-)
+
+--- a/drivers/rtc/rtc-rpi.c
++++ b/drivers/rtc/rtc-rpi.c
+@@ -19,11 +19,22 @@
+ struct rpi_rtc_data {
+ struct rtc_device *rtc;
+ struct rpi_firmware *fw;
++ u32 bbat_vchg_microvolts;
+ };
+
+ #define RPI_FIRMWARE_GET_RTC_REG 0x00030087
+ #define RPI_FIRMWARE_SET_RTC_REG 0x00038087
+-enum {RTC_TIME, RTC_ALARM, RTC_ALARM_PENDING, RTC_ALARM_ENABLE};
++
++enum {
++ RTC_TIME,
++ RTC_ALARM,
++ RTC_ALARM_PENDING,
++ RTC_ALARM_ENABLE,
++ RTC_BBAT_CHG_VOLTS,
++ RTC_BBAT_CHG_VOLTS_MIN,
++ RTC_BBAT_CHG_VOLTS_MAX,
++ RTC_BBAT_VOLTS
++};
+
+ static int rpi_rtc_read_time(struct device *dev, struct rtc_time *tm)
+ {
+@@ -114,6 +125,83 @@ static const struct rtc_class_ops rpi_rt
+ .alarm_irq_enable = rpi_rtc_alarm_irq_enable,
+ };
+
++static int rpi_rtc_set_charge_voltage(struct device *dev)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev);
++ u32 data[2] = {RTC_BBAT_CHG_VOLTS, vrtc->bbat_vchg_microvolts};
++ int err;
++
++ err = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_SET_RTC_REG,
++ &data, sizeof(data));
++
++ if (err)
++ dev_err(dev, "failed to set trickle charge voltage to %uuV: %d\n",
++ vrtc->bbat_vchg_microvolts, err);
++ else if (vrtc->bbat_vchg_microvolts)
++ dev_info(dev, "trickle charging enabled at %uuV\n",
++ vrtc->bbat_vchg_microvolts);
++
++ return err;
++}
++
++static ssize_t rpi_rtc_print_uint_reg(struct device *dev, char *buf, u32 reg)
++{
++ struct rpi_rtc_data *vrtc = dev_get_drvdata(dev->parent);
++ u32 data[2] = {reg, 0};
++ int ret = 0;
++
++ ret = rpi_firmware_property(vrtc->fw, RPI_FIRMWARE_GET_RTC_REG,
++ &data, sizeof(data));
++ if (ret < 0)
++ return ret;
++
++ return sprintf(buf, "%u\n", data[1]);
++}
++
++static ssize_t charging_voltage_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS);
++}
++static DEVICE_ATTR_RO(charging_voltage);
++
++static ssize_t charging_voltage_min_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS_MIN);
++}
++static DEVICE_ATTR_RO(charging_voltage_min);
++
++static ssize_t charging_voltage_max_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_CHG_VOLTS_MAX);
++}
++static DEVICE_ATTR_RO(charging_voltage_max);
++
++static ssize_t battery_voltage_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ return rpi_rtc_print_uint_reg(dev, buf, RTC_BBAT_VOLTS);
++}
++static DEVICE_ATTR_RO(battery_voltage);
++
++static struct attribute *rpi_rtc_attrs[] = {
++ &dev_attr_charging_voltage.attr,
++ &dev_attr_charging_voltage_min.attr,
++ &dev_attr_charging_voltage_max.attr,
++ &dev_attr_battery_voltage.attr,
++ NULL
++};
++
++static const struct attribute_group rpi_rtc_sysfs_files = {
++ .attrs = rpi_rtc_attrs,
++};
++
+ static int rpi_rtc_probe(struct platform_device *pdev)
+ {
+ struct rpi_rtc_data *vrtc;
+@@ -151,10 +239,22 @@ static int rpi_rtc_probe(struct platform
+ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, vrtc->rtc->features);
+
+ vrtc->rtc->ops = &rpi_rtc_ops;
+- ret = devm_rtc_register_device(vrtc->rtc);
++ ret = rtc_add_group(vrtc->rtc, &rpi_rtc_sysfs_files);
++ if (ret)
++ return ret;
+
+ rpi_rtc_alarm_clear_pending(dev);
+- return ret;
++
++ /*
++ * Optionally enable trickle charging - if the property isn't
++ * present (or set to zero), trickle charging is disabled.
++ */
++ of_property_read_u32(np, "trickle-charge-microvolt",
++ &vrtc->bbat_vchg_microvolts);
++
++ rpi_rtc_set_charge_voltage(dev);
++
++ return devm_rtc_register_device(vrtc->rtc);
+ }
+
+ static const struct of_device_id rpi_rtc_dt_match[] = {
--- /dev/null
+From 0f5fd4538774aa6c936bb8fc78611c3113bf19d7 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 17 Apr 2023 15:21:41 +0100
+Subject: [PATCH] vc4_drv: Avoid panic when booted with no kms
+
+If kms/fkms overlay is not present we have no matching drivers
+and so match is NULL.
+
+It is not safe to call component_master_add_with_match with a null match argument.
+
+So don't do that
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -456,6 +456,9 @@ static int vc4_platform_drm_probe(struct
+ vc4_match_add_drivers(dev, &match,
+ component_drivers, ARRAY_SIZE(component_drivers));
+
++ if (!match)
++ return -ENODEV;
++
+ return component_master_add_with_match(dev, &vc4_drm_ops, match);
+ }
+
--- /dev/null
+From 2c987545a88507acdd8a572a3bd23a4ca0124d14 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 13 Apr 2023 17:41:11 +0100
+Subject: [PATCH] drm/vc4: Treat zero sized destination as full screen
+
+Kodi video planes come through with all zeros for fullscreen
+Without this check, we WARN when writing width-1, height-1
+to destination dlist
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -484,6 +484,11 @@ static int vc4_plane_setup_clipping_and_
+ vc4_state->crtc_w = state->dst.x2 - state->dst.x1;
+ vc4_state->crtc_h = state->dst.y2 - state->dst.y1;
+
++ if (!vc4_state->crtc_w)
++ vc4_state->crtc_w = state->crtc->mode.hdisplay;
++ if (!vc4_state->crtc_h)
++ vc4_state->crtc_h = state->crtc->mode.vdisplay;
++
+ ret = vc4_plane_margins_adj(state);
+ if (ret)
+ return ret;
--- /dev/null
+From bb1ee75de382c7a5218750476aa2a5792309cc70 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 30 Mar 2023 17:18:36 +0100
+Subject: [PATCH] drm/vc4: Fix FKMS for when the YUV chroma planes are
+ different buffers
+
+The code was assuming that it was a single buffer with offsets,
+when kmstest uses separate buffers and 0 offsets for each plane.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -528,7 +528,7 @@ static int vc4_plane_to_mb(struct drm_pl
+ struct drm_plane_state *state)
+ {
+ struct drm_framebuffer *fb = state->fb;
+- struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
++ struct drm_gem_dma_object *bo;
+ const struct drm_format_info *drm_fmt = fb->format;
+ const struct vc_image_format *vc_fmt =
+ vc4_get_vc_image_fmt(drm_fmt->format);
+@@ -552,6 +552,7 @@ static int vc4_plane_to_mb(struct drm_pl
+ state->normalized_zpos : -127;
+ mb->plane.num_planes = num_planes;
+ mb->plane.is_vu = vc_fmt->is_vu;
++ bo = drm_fb_dma_get_gem_obj(fb, 0);
+ mb->plane.planes[0] = bo->dma_addr + fb->offsets[0];
+
+ rotation = drm_rotation_simplify(state->rotation,
+@@ -572,11 +573,14 @@ static int vc4_plane_to_mb(struct drm_pl
+ /* Makes assumptions on the stride for the chroma planes as we
+ * can't easily plumb in non-standard pitches.
+ */
++ bo = drm_fb_dma_get_gem_obj(fb, 1);
+ mb->plane.planes[1] = bo->dma_addr + fb->offsets[1];
+- if (num_planes > 2)
++ if (num_planes > 2) {
++ bo = drm_fb_dma_get_gem_obj(fb, 2);
+ mb->plane.planes[2] = bo->dma_addr + fb->offsets[2];
+- else
++ } else {
+ mb->plane.planes[2] = 0;
++ }
+
+ /* Special case the YUV420 with U and V as line interleaved
+ * planes as we have special handling for that case.
--- /dev/null
+From 0da58dfbd2cc2cfa14a629787b9ba6fa10b5f666 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 29 Mar 2023 15:26:52 +0100
+Subject: [PATCH] drm/vc4: hdmi: Enable the audio clock
+
+The audio clock is used by the HDMI controller driver and we were using
+it to get its audio rate and compute the dividers needed to reach a
+given audio sample rate.
+
+However, we were never enabling it, which was resulting in lockups on
+the BCM2712.
+
+Fixes: 632ee3aa8786 ("drm/vc4: hdmi: Add audio-related callbacks")
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -3625,6 +3625,7 @@ static int vc4_hdmi_runtime_suspend(stru
+ {
+ struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+
++ clk_disable_unprepare(vc4_hdmi->audio_clock);
+ clk_disable_unprepare(vc4_hdmi->hsm_rpm_clock);
+
+ return 0;
+@@ -3666,6 +3667,10 @@ static int vc4_hdmi_runtime_resume(struc
+ goto err_disable_clk;
+ }
+
++ ret = clk_prepare_enable(vc4_hdmi->audio_clock);
++ if (ret)
++ goto err_disable_clk;
++
+ if (vc4_hdmi->variant->reset)
+ vc4_hdmi->variant->reset(vc4_hdmi);
+
--- /dev/null
+From cdbebb3a92aca7327c88c6dc6ef5d4cd470d49fc Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 23 Feb 2023 19:44:32 +0100
+Subject: [PATCH] drm/vc4: hdmi: Warn if writing to an unknown HDMI register
+
+The VC4 HDMI driver has a bunch of accessors to read from a register.
+The read accessor was warning when accessing an unknown register, but
+the write one was just returning silently.
+
+Let's make sure we warn also when writing to an unknown register.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -498,8 +498,11 @@ static inline void vc4_hdmi_write(struct
+
+ field = &variant->registers[reg];
+ base = __vc4_hdmi_get_field_base(hdmi, field->reg);
+- if (!base)
++ if (!base) {
++ dev_warn(&hdmi->pdev->dev,
++ "Unknown register ID %u\n", reg);
+ return;
++ }
+
+ writel(value, base + field->offset);
+ }
--- /dev/null
+From 4ebd8283403daf5507e5aafb42fe3e4c7612eb14 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 22 Mar 2023 09:51:51 +0100
+Subject: [PATCH] drm/vc4: hvs: More logging for dlist generation
+
+DLIST generation can get pretty tricky and there's not a lot of debug in
+the driver to help. Let's add a few more to track the generated DLIST
+size.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 15 +++++++++++++--
+ 1 file changed, 13 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -826,11 +826,22 @@ int vc4_hvs_atomic_check(struct drm_crtc
+ if (hweight32(crtc_state->connector_mask) > 1)
+ return -EINVAL;
+
+- drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state)
+- dlist_count += vc4_plane_dlist_size(plane_state);
++ drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) {
++ u32 plane_dlist_count = vc4_plane_dlist_size(plane_state);
++
++ drm_dbg_driver(dev, "[CRTC:%d:%s] Found [PLANE:%d:%s] with DLIST size: %u\n",
++ crtc->base.id, crtc->name,
++ plane->base.id, plane->name,
++ plane_dlist_count);
++
++ dlist_count += plane_dlist_count;
++ }
+
+ dlist_count++; /* Account for SCALER_CTL0_END. */
+
++ drm_dbg_driver(dev, "[CRTC:%d:%s] Allocating DLIST block with size: %u\n",
++ crtc->base.id, crtc->name, dlist_count);
++
+ alloc = vc4_hvs_alloc_dlist_entry(vc4->hvs, vc4_state->assigned_channel, dlist_count);
+ if (IS_ERR(alloc))
+ return PTR_ERR(alloc);
--- /dev/null
+From c0af63193bd307f281211e7fb32a02a52c2869b2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 22 Mar 2023 09:53:17 +0100
+Subject: [PATCH] drm/vc4: hvs: Print error if we fail an allocation
+
+We need to allocate a few additional structures when checking our
+atomic_state, especially related to hardware SRAM that will hold the
+plane descriptors (DLIST) and the current line context (LBM) during
+composition.
+
+Since those allocation can fail, let's add some error message in that
+case to help debug what goes wrong.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 6 +++++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 7 +++++--
+ 2 files changed, 10 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -441,6 +441,8 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
+ unsigned int channel,
+ size_t dlist_count)
+ {
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *dev = &vc4->base;
+ struct vc4_hvs_dlist_allocation *alloc;
+ unsigned long flags;
+ int ret;
+@@ -458,8 +460,10 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
+ ret = drm_mm_insert_node(&hvs->dlist_mm, &alloc->mm_node,
+ dlist_count);
+ spin_unlock_irqrestore(&hvs->mm_lock, flags);
+- if (ret)
++ if (ret) {
++ drm_err(dev, "Failed to allocate DLIST entry: %d\n", ret);
+ return ERR_PTR(ret);
++ }
+
+ alloc->channel = channel;
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -733,7 +733,8 @@ static void vc4_plane_calc_load(struct d
+
+ static int vc4_plane_allocate_lbm(struct drm_plane_state *state)
+ {
+- struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
++ struct drm_device *drm = state->plane->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+ unsigned long irqflags;
+ u32 lbm_size;
+@@ -759,8 +760,10 @@ static int vc4_plane_allocate_lbm(struct
+ 0, 0);
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+
+- if (ret)
++ if (ret) {
++ drm_err(drm, "Failed to allocate LBM entry: %d\n", ret);
+ return ret;
++ }
+ } else {
+ WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
+ }
--- /dev/null
+From 164f7e94da446984f275be1c082b93243beadfba Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Wed, 22 Mar 2023 16:17:57 +0100
+Subject: [PATCH] drm/vc4: plane: Add more debugging for LBM allocation
+
+LBM allocations need a different size depending on the line length,
+format, etc.
+
+This can get tricky, and fail. Let's add some more prints to ease the
+debugging when it does.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -735,6 +735,7 @@ static int vc4_plane_allocate_lbm(struct
+ {
+ struct drm_device *drm = state->plane->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
++ struct drm_plane *plane = state->plane;
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+ unsigned long irqflags;
+ u32 lbm_size;
+@@ -743,6 +744,9 @@ static int vc4_plane_allocate_lbm(struct
+ if (!lbm_size)
+ return 0;
+
++ drm_dbg_driver(drm, "[PLANE:%d:%s] LBM Allocation Size: %u\n",
++ plane->base.id, plane->name, lbm_size);
++
+ if (WARN_ON(!vc4_state->lbm_offset))
+ return -EINVAL;
+
--- /dev/null
+From 950394a39f659746e5933cbc203a8bedef8246b7 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 14:26:44 +0100
+Subject: [PATCH] drm/vc4: plane: Use return variable in atomic_check
+
+The vc4_plane_atomic_check() directly returns the result of the final
+function it calls.
+
+Using the already defined ret variable to check its content on error,
+and a separate return 0 on success, makes it easier to extend.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1378,7 +1378,11 @@ static int vc4_plane_atomic_check(struct
+ if (ret)
+ return ret;
+
+- return vc4_plane_allocate_lbm(new_plane_state);
++ ret = vc4_plane_allocate_lbm(new_plane_state);
++ if (ret)
++ return ret;
++
++ return 0;
+ }
+
+ static void vc4_plane_atomic_update(struct drm_plane *plane,
--- /dev/null
+From f3c6acc345113c57011f2b1c8421e6cf78f0bc30 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:39:13 +0100
+Subject: [PATCH] drm/vc4: crtc: Move assigned_channel to a variable
+
+We access multiple times the vc4_crtc_state->assigned_channel variable
+in the vc4_crtc_get_scanout_position() function, so let's store it in a
+local variable.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -104,6 +104,7 @@ static bool vc4_crtc_get_scanout_positio
+ struct vc4_hvs *hvs = vc4->hvs;
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
++ unsigned int channel = vc4_crtc_state->assigned_channel;
+ unsigned int cob_size;
+ u32 val;
+ int fifo_lines;
+@@ -120,7 +121,7 @@ static bool vc4_crtc_get_scanout_positio
+ * Read vertical scanline which is currently composed for our
+ * pixelvalve by the HVS, and also the scaler status.
+ */
+- val = HVS_READ(SCALER_DISPSTATX(vc4_crtc_state->assigned_channel));
++ val = HVS_READ(SCALER_DISPSTATX(channel));
+
+ /* Get optional system timestamp after query. */
+ if (etime)
+@@ -136,11 +137,11 @@ static bool vc4_crtc_get_scanout_positio
+ *vpos /= 2;
+
+ /* Use hpos to correct for field offset in interlaced mode. */
+- if (vc4_hvs_get_fifo_frame_count(hvs, vc4_crtc_state->assigned_channel) % 2)
++ if (vc4_hvs_get_fifo_frame_count(hvs, channel) % 2)
+ *hpos += mode->crtc_htotal / 2;
+ }
+
+- cob_size = vc4_crtc_get_cob_allocation(vc4, vc4_crtc_state->assigned_channel);
++ cob_size = vc4_crtc_get_cob_allocation(vc4, channel);
+ /* This is the offset we need for translating hvs -> pv scanout pos. */
+ fifo_lines = cob_size / mode->crtc_hdisplay;
+
--- /dev/null
+From fa2571d625bb53b642cd9f29a7cfc3434e1cf576 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:07:36 +0100
+Subject: [PATCH] drm/vc4: Introduce generation number enum
+
+With the introduction of the BCM2712 support, we will get yet another
+generation of display engine to support.
+
+The binary check of whether it's VC5 or not thus doesn't work anymore,
+especially since some parts of the driver will have changed with BCM2711,
+and some others with BCM2712.
+
+Let's introduce an enum to store the generation the driver is running
+on, which should provide more flexibility.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 12 +++---
+ drivers/gpu/drm/vc4/vc4_bo.c | 28 ++++++------
+ drivers/gpu/drm/vc4/vc4_crtc.c | 14 +++---
+ drivers/gpu/drm/vc4/vc4_drv.c | 22 ++++++----
+ drivers/gpu/drm/vc4/vc4_drv.h | 7 ++-
+ drivers/gpu/drm/vc4/vc4_gem.c | 24 +++++------
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_hvs.c | 50 ++++++++++++----------
+ drivers/gpu/drm/vc4/vc4_irq.c | 10 ++---
+ drivers/gpu/drm/vc4/vc4_kms.c | 14 +++---
+ drivers/gpu/drm/vc4/vc4_perfmon.c | 20 ++++-----
+ drivers/gpu/drm/vc4/vc4_plane.c | 12 +++---
+ drivers/gpu/drm/vc4/vc4_render_cl.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_v3d.c | 10 ++---
+ drivers/gpu/drm/vc4/vc4_validate.c | 8 ++--
+ drivers/gpu/drm/vc4/vc4_validate_shaders.c | 2 +-
+ 16 files changed, 126 insertions(+), 111 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -153,11 +153,11 @@ static int __build_mock(struct kunit *te
+ return 0;
+ }
+
+-static struct vc4_dev *__mock_device(struct kunit *test, bool is_vc5)
++static struct vc4_dev *__mock_device(struct kunit *test, enum vc4_gen gen)
+ {
+ struct drm_device *drm;
+- const struct drm_driver *drv = is_vc5 ? &vc5_drm_driver : &vc4_drm_driver;
+- const struct vc4_mock_desc *desc = is_vc5 ? &vc5_mock : &vc4_mock;
++ const struct drm_driver *drv = (gen == VC4_GEN_5) ? &vc5_drm_driver : &vc4_drm_driver;
++ const struct vc4_mock_desc *desc = (gen == VC4_GEN_5) ? &vc5_mock : &vc4_mock;
+ struct vc4_dev *vc4;
+ struct device *dev;
+ int ret;
+@@ -171,7 +171,7 @@ static struct vc4_dev *__mock_device(str
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
+
+ vc4->dev = dev;
+- vc4->is_vc5 = is_vc5;
++ vc4->gen = gen;
+
+ vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);
+@@ -191,10 +191,10 @@ static struct vc4_dev *__mock_device(str
+
+ struct vc4_dev *vc4_mock_device(struct kunit *test)
+ {
+- return __mock_device(test, false);
++ return __mock_device(test, VC4_GEN_4);
+ }
+
+ struct vc4_dev *vc5_mock_device(struct kunit *test)
+ {
+- return __mock_device(test, true);
++ return __mock_device(test, VC4_GEN_5);
+ }
+--- a/drivers/gpu/drm/vc4/vc4_bo.c
++++ b/drivers/gpu/drm/vc4/vc4_bo.c
+@@ -251,7 +251,7 @@ void vc4_bo_add_to_purgeable_pool(struct
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ mutex_lock(&vc4->purgeable.lock);
+@@ -265,7 +265,7 @@ static void vc4_bo_remove_from_purgeable
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ /* list_del_init() is used here because the caller might release
+@@ -396,7 +396,7 @@ struct drm_gem_object *vc4_create_object
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return ERR_PTR(-ENODEV);
+
+ bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+@@ -427,7 +427,7 @@ struct vc4_bo *vc4_bo_create(struct drm_
+ struct drm_gem_dma_object *dma_obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return ERR_PTR(-ENODEV);
+
+ if (size == 0)
+@@ -496,7 +496,7 @@ int vc4_bo_dumb_create(struct drm_file *
+ struct vc4_bo *bo = NULL;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ ret = vc4_dumb_fixup_args(args);
+@@ -622,7 +622,7 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo)
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ /* Fast path: if the BO is already retained by someone, no need to
+@@ -661,7 +661,7 @@ void vc4_bo_dec_usecnt(struct vc4_bo *bo
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ /* Fast path: if the BO is still retained by someone, no need to test
+@@ -783,7 +783,7 @@ int vc4_create_bo_ioctl(struct drm_devic
+ struct vc4_bo *bo = NULL;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ ret = vc4_grab_bin_bo(vc4, vc4file);
+@@ -813,7 +813,7 @@ int vc4_mmap_bo_ioctl(struct drm_device
+ struct drm_vc4_mmap_bo *args = data;
+ struct drm_gem_object *gem_obj;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ gem_obj = drm_gem_object_lookup(file_priv, args->handle);
+@@ -839,7 +839,7 @@ vc4_create_shader_bo_ioctl(struct drm_de
+ struct vc4_bo *bo = NULL;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (args->size == 0)
+@@ -918,7 +918,7 @@ int vc4_set_tiling_ioctl(struct drm_devi
+ struct vc4_bo *bo;
+ bool t_format;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (args->flags != 0)
+@@ -964,7 +964,7 @@ int vc4_get_tiling_ioctl(struct drm_devi
+ struct drm_gem_object *gem_obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (args->flags != 0 || args->modifier != 0)
+@@ -1011,7 +1011,7 @@ int vc4_bo_cache_init(struct drm_device
+ int ret;
+ int i;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ /* Create the initial set of BO labels that the kernel will
+@@ -1075,7 +1075,7 @@ int vc4_label_bo_ioctl(struct drm_device
+ struct drm_gem_object *gem_obj;
+ int ret = 0, label;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!args->len)
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -263,7 +263,7 @@ static u32 vc4_get_fifo_full_level(struc
+ * Removing 1 from the FIFO full level however
+ * seems to completely remove that issue.
+ */
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX - 1;
+
+ return fifo_len_bytes - 3 * HVS_FIFO_LATENCY_PIX;
+@@ -445,7 +445,7 @@ static void vc4_crtc_config_pv(struct dr
+ if (is_dsi)
+ CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
+
+- if (vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_5)
+ CRTC_WRITE(PV_MUX_CFG,
+ VC4_SET_FIELD(PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP,
+ PV_MUX_CFG_RGB_PIXEL_MUX_MODE));
+@@ -936,7 +936,7 @@ static int vc4_async_set_fence_cb(struct
+ struct dma_fence *fence;
+ int ret;
+
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ struct vc4_bo *bo = to_vc4_bo(&dma_bo->base);
+
+ return vc4_queue_seqno_cb(dev, &flip_state->cb.seqno, bo->seqno,
+@@ -1023,7 +1023,7 @@ static int vc4_async_page_flip(struct dr
+ struct vc4_bo *bo = to_vc4_bo(&dma_bo->base);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ /*
+@@ -1066,7 +1066,7 @@ int vc4_page_flip(struct drm_crtc *crtc,
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_5)
+ return vc5_async_page_flip(crtc, fb, event, flags);
+ else
+ return vc4_async_page_flip(crtc, fb, event, flags);
+@@ -1358,13 +1358,13 @@ int __vc4_crtc_init(struct drm_device *d
+
+ drm_crtc_helper_add(crtc, crtc_helper_funcs);
+
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ drm_mode_crtc_set_gamma_size(crtc, ARRAY_SIZE(vc4_crtc->lut_r));
+ drm_crtc_enable_color_mgmt(crtc, 0, false, crtc->gamma_size);
+ }
+
+
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ /* We support CTM, but only for one CRTC at a time. It's therefore
+ * implemented as private driver state in vc4_kms, not here.
+ */
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -98,7 +98,7 @@ static int vc4_get_param_ioctl(struct dr
+ if (args->pad != 0)
+ return -EINVAL;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!vc4->v3d)
+@@ -147,7 +147,7 @@ static int vc4_open(struct drm_device *d
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_file *vc4file;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL);
+@@ -165,7 +165,7 @@ static void vc4_close(struct drm_device
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_file *vc4file = file->driver_priv;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ if (vc4file->bin_bo_used)
+@@ -305,13 +305,17 @@ static int vc4_drm_bind(struct device *d
+ struct vc4_dev *vc4;
+ struct device_node *node;
+ struct drm_crtc *crtc;
+- bool is_vc5;
++ enum vc4_gen gen;
+ int ret = 0;
+
+ dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+- is_vc5 = of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5");
+- if (is_vc5)
++ if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
++ gen = VC4_GEN_5;
++ else
++ gen = VC4_GEN_4;
++
++ if (gen == VC4_GEN_5)
+ driver = &vc5_drm_driver;
+ else
+ driver = &vc4_drm_driver;
+@@ -329,14 +333,14 @@ static int vc4_drm_bind(struct device *d
+ vc4 = devm_drm_dev_alloc(dev, driver, struct vc4_dev, base);
+ if (IS_ERR(vc4))
+ return PTR_ERR(vc4);
+- vc4->is_vc5 = is_vc5;
++ vc4->gen = gen;
+ vc4->dev = dev;
+
+ drm = &vc4->base;
+ platform_set_drvdata(pdev, drm);
+ INIT_LIST_HEAD(&vc4->debugfs_list);
+
+- if (!is_vc5) {
++ if (gen == VC4_GEN_4) {
+ ret = drmm_mutex_init(drm, &vc4->bin_bo_lock);
+ if (ret)
+ return ret;
+@@ -350,7 +354,7 @@ static int vc4_drm_bind(struct device *d
+ if (ret)
+ return ret;
+
+- if (!is_vc5) {
++ if (gen == VC4_GEN_4) {
+ ret = vc4_gem_init(drm);
+ if (ret)
+ return ret;
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -80,11 +80,16 @@ struct vc4_perfmon {
+ u64 counters[];
+ };
+
++enum vc4_gen {
++ VC4_GEN_4,
++ VC4_GEN_5,
++};
++
+ struct vc4_dev {
+ struct drm_device base;
+ struct device *dev;
+
+- bool is_vc5;
++ enum vc4_gen gen;
+
+ unsigned int irq;
+
+--- a/drivers/gpu/drm/vc4/vc4_gem.c
++++ b/drivers/gpu/drm/vc4/vc4_gem.c
+@@ -76,7 +76,7 @@ vc4_get_hang_state_ioctl(struct drm_devi
+ u32 i;
+ int ret = 0;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -389,7 +389,7 @@ vc4_wait_for_seqno(struct drm_device *de
+ unsigned long timeout_expire;
+ DEFINE_WAIT(wait);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (vc4->finished_seqno >= seqno)
+@@ -474,7 +474,7 @@ vc4_submit_next_bin_job(struct drm_devic
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_exec_info *exec;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ again:
+@@ -522,7 +522,7 @@ vc4_submit_next_render_job(struct drm_de
+ if (!exec)
+ return;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ /* A previous RCL may have written to one of our textures, and
+@@ -543,7 +543,7 @@ vc4_move_job_to_render(struct drm_device
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ bool was_empty = list_empty(&vc4->render_job_list);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ list_move_tail(&exec->head, &vc4->render_job_list);
+@@ -1012,7 +1012,7 @@ vc4_job_handle_completed(struct vc4_dev
+ unsigned long irqflags;
+ struct vc4_seqno_cb *cb, *cb_temp;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
+@@ -1051,7 +1051,7 @@ int vc4_queue_seqno_cb(struct drm_device
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ unsigned long irqflags;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ cb->func = func;
+@@ -1107,7 +1107,7 @@ vc4_wait_seqno_ioctl(struct drm_device *
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_vc4_wait_seqno *args = data;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
+@@ -1124,7 +1124,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev
+ struct drm_gem_object *gem_obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (args->pad != 0)
+@@ -1173,7 +1173,7 @@ vc4_submit_cl_ioctl(struct drm_device *d
+ args->shader_rec_size,
+ args->bo_handle_count);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -1310,7 +1310,7 @@ int vc4_gem_init(struct drm_device *dev)
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ vc4->dma_fence_context = dma_fence_context_alloc(1);
+@@ -1369,7 +1369,7 @@ int vc4_gem_madvise_ioctl(struct drm_dev
+ struct vc4_bo *bo;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ switch (args->madv) {
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -2614,7 +2614,7 @@ static int vc4_hdmi_audio_prepare(struct
+ VC4_HDMI_AUDIO_PACKET_CEA_MASK);
+
+ /* Set the MAI threshold */
+- if (vc4->is_vc5)
++ if (vc4->gen >= VC4_GEN_5)
+ HDMI_WRITE(HDMI_MAI_THR,
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) |
+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) |
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -416,7 +416,7 @@ static void vc4_hvs_irq_enable_eof(const
+ unsigned int channel)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+- u32 irq_mask = vc4->is_vc5 ?
++ u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+ SCALER5_DISPCTRL_DSPEIEOF(channel) :
+ SCALER_DISPCTRL_DSPEIEOF(channel);
+
+@@ -428,7 +428,7 @@ static void vc4_hvs_irq_clear_eof(const
+ unsigned int channel)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+- u32 irq_mask = vc4->is_vc5 ?
++ u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+ SCALER5_DISPCTRL_DSPEIEOF(channel) :
+ SCALER_DISPCTRL_DSPEIEOF(channel);
+
+@@ -620,7 +620,7 @@ int vc4_hvs_get_fifo_from_output(struct
+ u32 reg;
+ int ret;
+
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ return output;
+
+ /*
+@@ -701,7 +701,7 @@ static int vc4_hvs_init_channel(struct v
+ dispctrl = SCALER_DISPCTRLX_ENABLE;
+ dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan));
+
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ dispctrl |= VC4_SET_FIELD(mode->hdisplay,
+ SCALER_DISPCTRLX_WIDTH) |
+ VC4_SET_FIELD(mode->vdisplay,
+@@ -732,7 +732,7 @@ static int vc4_hvs_init_channel(struct v
+ /* Reload the LUT, since the SRAMs would have been disabled if
+ * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once.
+ */
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ vc4_hvs_lut_load(hvs, vc4_crtc);
+ else
+ vc5_hvs_lut_load(hvs, vc4_crtc);
+@@ -782,7 +782,7 @@ static int vc4_hvs_gamma_check(struct dr
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ return 0;
+
+ if (!crtc_state->color_mgmt_changed)
+@@ -1036,7 +1036,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel));
+
+ if (crtc->state->gamma_lut) {
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ vc4_hvs_update_gamma_lut(hvs, vc4_crtc);
+ dispbkgndx |= SCALER_DISPBKGND_GAMMA;
+ } else {
+@@ -1053,7 +1053,7 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ * should already be disabling/enabling the pipeline
+ * when gamma changes.
+ */
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ dispbkgndx &= ~SCALER_DISPBKGND_GAMMA;
+ }
+ HVS_WRITE(SCALER_DISPBKGNDX(channel), dispbkgndx);
+@@ -1069,7 +1069,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+
+ void vc4_hvs_mask_underrun(struct vc4_hvs *hvs, int channel)
+ {
+- struct drm_device *drm = &hvs->vc4->base;
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
+ u32 dispctrl;
+ int idx;
+
+@@ -1077,8 +1078,9 @@ void vc4_hvs_mask_underrun(struct vc4_hv
+ return;
+
+ dispctrl = HVS_READ(SCALER_DISPCTRL);
+- dispctrl &= ~(hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+- SCALER_DISPCTRL_DSPEISLUR(channel));
++ dispctrl &= ~((vc4->gen == VC4_GEN_5) ?
++ SCALER5_DISPCTRL_DSPEISLUR(channel) :
++ SCALER_DISPCTRL_DSPEISLUR(channel));
+
+ HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+
+@@ -1087,7 +1089,8 @@ void vc4_hvs_mask_underrun(struct vc4_hv
+
+ void vc4_hvs_unmask_underrun(struct vc4_hvs *hvs, int channel)
+ {
+- struct drm_device *drm = &hvs->vc4->base;
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
+ u32 dispctrl;
+ int idx;
+
+@@ -1095,8 +1098,9 @@ void vc4_hvs_unmask_underrun(struct vc4_
+ return;
+
+ dispctrl = HVS_READ(SCALER_DISPCTRL);
+- dispctrl |= (hvs->vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+- SCALER_DISPCTRL_DSPEISLUR(channel));
++ dispctrl |= ((vc4->gen == VC4_GEN_5) ?
++ SCALER5_DISPCTRL_DSPEISLUR(channel) :
++ SCALER_DISPCTRL_DSPEISLUR(channel));
+
+ HVS_WRITE(SCALER_DISPSTAT,
+ SCALER_DISPSTAT_EUFLOW(channel));
+@@ -1139,8 +1143,10 @@ static irqreturn_t vc4_hvs_irq_handler(i
+ control = HVS_READ(SCALER_DISPCTRL);
+
+ for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) {
+- dspeislur = vc4->is_vc5 ? SCALER5_DISPCTRL_DSPEISLUR(channel) :
+- SCALER_DISPCTRL_DSPEISLUR(channel);
++ dspeislur = (vc4->gen == VC4_GEN_5) ?
++ SCALER5_DISPCTRL_DSPEISLUR(channel) :
++ SCALER_DISPCTRL_DSPEISLUR(channel);
++
+ /* Interrupt masking is not always honored, so check it here. */
+ if (status & SCALER_DISPSTAT_EUFLOW(channel) &&
+ control & dspeislur) {
+@@ -1177,7 +1183,7 @@ int vc4_hvs_debugfs_init(struct drm_mino
+ if (!vc4->hvs)
+ return -ENODEV;
+
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ debugfs_create_bool("hvs_load_tracker", S_IRUGO | S_IWUSR,
+ minor->debugfs_root,
+ &vc4->load_tracker_enabled);
+@@ -1235,7 +1241,7 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ * between planes when they don't overlap on the screen, but
+ * for now we just allocate globally.
+ */
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ /* 48k words of 2x12-bit pixels */
+ drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024);
+ else
+@@ -1269,7 +1275,7 @@ static int vc4_hvs_bind(struct device *d
+ hvs->regset.regs = hvs_regs;
+ hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+
+- if (vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_5) {
+ struct rpi_firmware *firmware;
+ struct device_node *node;
+ unsigned int max_rate;
+@@ -1307,7 +1313,7 @@ static int vc4_hvs_bind(struct device *d
+ }
+ }
+
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ hvs->dlist = hvs->regs + SCALER_DLIST_START;
+ else
+ hvs->dlist = hvs->regs + SCALER5_DLIST_START;
+@@ -1348,7 +1354,7 @@ static int vc4_hvs_bind(struct device *d
+ SCALER_DISPCTRL_DISPEIRQ(1) |
+ SCALER_DISPCTRL_DISPEIRQ(2);
+
+- if (!vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_4)
+ dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ |
+ SCALER_DISPCTRL_SLVWREIRQ |
+ SCALER_DISPCTRL_SLVRDEIRQ |
+@@ -1403,7 +1409,7 @@ static int vc4_hvs_bind(struct device *d
+
+ /* Recompute Composite Output Buffer (COB) allocations for the displays
+ */
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
+ * The bottom 2048 pixels are full 32bpp RGBA (intended for the
+ * TXP composing RGBA to memory), whilst the remainder are only
+--- a/drivers/gpu/drm/vc4/vc4_irq.c
++++ b/drivers/gpu/drm/vc4/vc4_irq.c
+@@ -265,7 +265,7 @@ vc4_irq_enable(struct drm_device *dev)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ if (!vc4->v3d)
+@@ -282,7 +282,7 @@ vc4_irq_disable(struct drm_device *dev)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ if (!vc4->v3d)
+@@ -305,7 +305,7 @@ int vc4_irq_install(struct drm_device *d
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (irq == IRQ_NOTCONNECTED)
+@@ -326,7 +326,7 @@ void vc4_irq_uninstall(struct drm_device
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ vc4_irq_disable(dev);
+@@ -339,7 +339,7 @@ void vc4_irq_reset(struct drm_device *de
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ unsigned long irqflags;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ /* Acknowledge any stale IRQs. */
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -378,7 +378,7 @@ static void vc4_atomic_commit_tail(struc
+ old_hvs_state->fifo_state[channel].pending_commit = NULL;
+ }
+
+- if (vc4->is_vc5 && !vc4->firmware_kms) {
++ if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+ unsigned long state_rate = max(old_hvs_state->core_clock_rate,
+ new_hvs_state->core_clock_rate);
+ unsigned long core_rate = clamp_t(unsigned long, state_rate,
+@@ -398,7 +398,7 @@ static void vc4_atomic_commit_tail(struc
+ vc4_ctm_commit(vc4, state);
+
+ if (!vc4->firmware_kms) {
+- if (vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_5)
+ vc5_hvs_pv_muxing_commit(vc4, state);
+ else
+ vc4_hvs_pv_muxing_commit(vc4, state);
+@@ -417,7 +417,7 @@ static void vc4_atomic_commit_tail(struc
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+- if (vc4->is_vc5 && !vc4->firmware_kms) {
++ if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+ unsigned long core_rate = min_t(unsigned long,
+ hvs->max_core_rate,
+ new_hvs_state->core_clock_rate);
+@@ -482,7 +482,7 @@ static struct drm_framebuffer *vc4_fb_cr
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_mode_fb_cmd2 mode_cmd_local;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return ERR_PTR(-ENODEV);
+
+ /* If the user didn't specify a modifier, use the
+@@ -1065,7 +1065,7 @@ int vc4_kms_load(struct drm_device *dev)
+ * the BCM2711, but the load tracker computations are used for
+ * the core clock rate calculation.
+ */
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ /* Start with the load tracker enabled. Can be
+ * disabled through the debugfs load_tracker file.
+ */
+@@ -1081,7 +1081,7 @@ int vc4_kms_load(struct drm_device *dev)
+ return ret;
+ }
+
+- if (vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_5) {
+ dev->mode_config.max_width = 7680;
+ dev->mode_config.max_height = 7680;
+ } else {
+@@ -1089,7 +1089,7 @@ int vc4_kms_load(struct drm_device *dev)
+ dev->mode_config.max_height = 2048;
+ }
+
+- dev->mode_config.funcs = vc4->is_vc5 ? &vc5_mode_funcs : &vc4_mode_funcs;
++ dev->mode_config.funcs = (vc4->gen > VC4_GEN_4) ? &vc5_mode_funcs : &vc4_mode_funcs;
+ dev->mode_config.helper_private = &vc4_mode_config_helpers;
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.async_page_flip = true;
+--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
++++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
+@@ -23,7 +23,7 @@ void vc4_perfmon_get(struct vc4_perfmon
+ return;
+
+ vc4 = perfmon->dev;
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ refcount_inc(&perfmon->refcnt);
+@@ -37,7 +37,7 @@ void vc4_perfmon_put(struct vc4_perfmon
+ return;
+
+ vc4 = perfmon->dev;
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ if (refcount_dec_and_test(&perfmon->refcnt))
+@@ -49,7 +49,7 @@ void vc4_perfmon_start(struct vc4_dev *v
+ unsigned int i;
+ u32 mask;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
+@@ -69,7 +69,7 @@ void vc4_perfmon_stop(struct vc4_dev *vc
+ {
+ unsigned int i;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ if (WARN_ON_ONCE(!vc4->active_perfmon ||
+@@ -90,7 +90,7 @@ struct vc4_perfmon *vc4_perfmon_find(str
+ struct vc4_dev *vc4 = vc4file->dev;
+ struct vc4_perfmon *perfmon;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return NULL;
+
+ mutex_lock(&vc4file->perfmon.lock);
+@@ -105,7 +105,7 @@ void vc4_perfmon_open_file(struct vc4_fi
+ {
+ struct vc4_dev *vc4 = vc4file->dev;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ mutex_init(&vc4file->perfmon.lock);
+@@ -126,7 +126,7 @@ void vc4_perfmon_close_file(struct vc4_f
+ {
+ struct vc4_dev *vc4 = vc4file->dev;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ mutex_lock(&vc4file->perfmon.lock);
+@@ -146,7 +146,7 @@ int vc4_perfmon_create_ioctl(struct drm_
+ unsigned int i;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -200,7 +200,7 @@ int vc4_perfmon_destroy_ioctl(struct drm
+ struct drm_vc4_perfmon_destroy *req = data;
+ struct vc4_perfmon *perfmon;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -228,7 +228,7 @@ int vc4_perfmon_get_values_ioctl(struct
+ struct vc4_perfmon *perfmon;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -633,10 +633,10 @@ static u32 vc4_lbm_size(struct drm_plane
+ }
+
+ /* Align it to 64 or 128 (hvs5) bytes */
+- lbm = roundup(lbm, vc4->is_vc5 ? 128 : 64);
++ lbm = roundup(lbm, vc4->gen == VC4_GEN_5 ? 128 : 64);
+
+ /* Each "word" of the LBM memory contains 2 or 4 (hvs5) pixels */
+- lbm /= vc4->is_vc5 ? 4 : 2;
++ lbm /= vc4->gen == VC4_GEN_5 ? 4 : 2;
+
+ return lbm;
+ }
+@@ -760,7 +760,7 @@ static int vc4_plane_allocate_lbm(struct
+ ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+ &vc4_state->lbm,
+ lbm_size,
+- vc4->is_vc5 ? 64 : 32,
++ vc4->gen == VC4_GEN_5 ? 64 : 32,
+ 0, 0);
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+
+@@ -1141,7 +1141,7 @@ static int vc4_plane_mode_set(struct drm
+ mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
+ fb->format->has_alpha;
+
+- if (!vc4->is_vc5) {
++ if (vc4->gen == VC4_GEN_4) {
+ /* Control word */
+ vc4_dlist_write(vc4_state,
+ SCALER_CTL0_VALID |
+@@ -1717,7 +1717,7 @@ struct drm_plane *vc4_plane_init(struct
+ };
+
+ for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+- if (!hvs_formats[i].hvs5_only || vc4->is_vc5) {
++ if (!hvs_formats[i].hvs5_only || vc4->gen == VC4_GEN_5) {
+ formats[num_formats] = hvs_formats[i].drm;
+ num_formats++;
+ }
+@@ -1732,7 +1732,7 @@ struct drm_plane *vc4_plane_init(struct
+ return ERR_CAST(vc4_plane);
+ plane = &vc4_plane->base;
+
+- if (vc4->is_vc5)
++ if (vc4->gen == VC4_GEN_5)
+ drm_plane_helper_add(plane, &vc5_plane_helper_funcs);
+ else
+ drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
+@@ -599,7 +599,7 @@ int vc4_get_rcl(struct drm_device *dev,
+ bool has_bin = args->bin_cl_size != 0;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ if (args->min_x_tile > args->max_x_tile ||
+--- a/drivers/gpu/drm/vc4/vc4_v3d.c
++++ b/drivers/gpu/drm/vc4/vc4_v3d.c
+@@ -127,7 +127,7 @@ static int vc4_v3d_debugfs_ident(struct
+ int
+ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ {
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ mutex_lock(&vc4->power_lock);
+@@ -148,7 +148,7 @@ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ void
+ vc4_v3d_pm_put(struct vc4_dev *vc4)
+ {
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ mutex_lock(&vc4->power_lock);
+@@ -178,7 +178,7 @@ int vc4_v3d_get_bin_slot(struct vc4_dev
+ uint64_t seqno = 0;
+ struct vc4_exec_info *exec;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ try_again:
+@@ -325,7 +325,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *v
+ {
+ int ret = 0;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ mutex_lock(&vc4->bin_bo_lock);
+@@ -360,7 +360,7 @@ static void bin_bo_release(struct kref *
+
+ void vc4_v3d_bin_bo_put(struct vc4_dev *vc4)
+ {
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return;
+
+ mutex_lock(&vc4->bin_bo_lock);
+--- a/drivers/gpu/drm/vc4/vc4_validate.c
++++ b/drivers/gpu/drm/vc4/vc4_validate.c
+@@ -109,7 +109,7 @@ vc4_use_bo(struct vc4_exec_info *exec, u
+ struct drm_gem_dma_object *obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return NULL;
+
+ if (hindex >= exec->bo_count) {
+@@ -169,7 +169,7 @@ vc4_check_tex_size(struct vc4_exec_info
+ uint32_t utile_w = utile_width(cpp);
+ uint32_t utile_h = utile_height(cpp);
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return false;
+
+ /* The shaded vertex format stores signed 12.4 fixed point
+@@ -495,7 +495,7 @@ vc4_validate_bin_cl(struct drm_device *d
+ uint32_t dst_offset = 0;
+ uint32_t src_offset = 0;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ while (src_offset < len) {
+@@ -942,7 +942,7 @@ vc4_validate_shader_recs(struct drm_devi
+ uint32_t i;
+ int ret = 0;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return -ENODEV;
+
+ for (i = 0; i < exec->shader_state_count; i++) {
+--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+@@ -786,7 +786,7 @@ vc4_validate_shader(struct drm_gem_dma_o
+ struct vc4_validated_shader_info *validated_shader = NULL;
+ struct vc4_shader_validation_state validation_state;
+
+- if (WARN_ON_ONCE(vc4->is_vc5))
++ if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
+ return NULL;
+
+ memset(&validation_state, 0, sizeof(validation_state));
--- /dev/null
+From c382ea6b0457027b6ad883ee4348e03df515a785 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:29:27 +0100
+Subject: [PATCH] drm/vc4: Make v3d paths unavailable on any generation newer
+ than vc4
+
+The V3D IP has been separate since BCM2711, so let's make sure we issue
+a WARN if we're running not only on BCM2711, but also anything newer.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_bo.c | 28 +++++++++++-----------
+ drivers/gpu/drm/vc4/vc4_crtc.c | 4 ++--
+ drivers/gpu/drm/vc4/vc4_drv.c | 8 +++----
+ drivers/gpu/drm/vc4/vc4_gem.c | 24 +++++++++----------
+ drivers/gpu/drm/vc4/vc4_irq.c | 10 ++++----
+ drivers/gpu/drm/vc4/vc4_kms.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_perfmon.c | 20 ++++++++--------
+ drivers/gpu/drm/vc4/vc4_render_cl.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_v3d.c | 10 ++++----
+ drivers/gpu/drm/vc4/vc4_validate.c | 8 +++----
+ drivers/gpu/drm/vc4/vc4_validate_shaders.c | 2 +-
+ 11 files changed, 59 insertions(+), 59 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_bo.c
++++ b/drivers/gpu/drm/vc4/vc4_bo.c
+@@ -251,7 +251,7 @@ void vc4_bo_add_to_purgeable_pool(struct
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ mutex_lock(&vc4->purgeable.lock);
+@@ -265,7 +265,7 @@ static void vc4_bo_remove_from_purgeable
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ /* list_del_init() is used here because the caller might release
+@@ -396,7 +396,7 @@ struct drm_gem_object *vc4_create_object
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return ERR_PTR(-ENODEV);
+
+ bo = kzalloc(sizeof(*bo), GFP_KERNEL);
+@@ -427,7 +427,7 @@ struct vc4_bo *vc4_bo_create(struct drm_
+ struct drm_gem_dma_object *dma_obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return ERR_PTR(-ENODEV);
+
+ if (size == 0)
+@@ -496,7 +496,7 @@ int vc4_bo_dumb_create(struct drm_file *
+ struct vc4_bo *bo = NULL;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ ret = vc4_dumb_fixup_args(args);
+@@ -622,7 +622,7 @@ int vc4_bo_inc_usecnt(struct vc4_bo *bo)
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ /* Fast path: if the BO is already retained by someone, no need to
+@@ -661,7 +661,7 @@ void vc4_bo_dec_usecnt(struct vc4_bo *bo
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(bo->base.base.dev);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ /* Fast path: if the BO is still retained by someone, no need to test
+@@ -783,7 +783,7 @@ int vc4_create_bo_ioctl(struct drm_devic
+ struct vc4_bo *bo = NULL;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ ret = vc4_grab_bin_bo(vc4, vc4file);
+@@ -813,7 +813,7 @@ int vc4_mmap_bo_ioctl(struct drm_device
+ struct drm_vc4_mmap_bo *args = data;
+ struct drm_gem_object *gem_obj;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ gem_obj = drm_gem_object_lookup(file_priv, args->handle);
+@@ -839,7 +839,7 @@ vc4_create_shader_bo_ioctl(struct drm_de
+ struct vc4_bo *bo = NULL;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (args->size == 0)
+@@ -918,7 +918,7 @@ int vc4_set_tiling_ioctl(struct drm_devi
+ struct vc4_bo *bo;
+ bool t_format;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (args->flags != 0)
+@@ -964,7 +964,7 @@ int vc4_get_tiling_ioctl(struct drm_devi
+ struct drm_gem_object *gem_obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (args->flags != 0 || args->modifier != 0)
+@@ -1011,7 +1011,7 @@ int vc4_bo_cache_init(struct drm_device
+ int ret;
+ int i;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ /* Create the initial set of BO labels that the kernel will
+@@ -1075,7 +1075,7 @@ int vc4_label_bo_ioctl(struct drm_device
+ struct drm_gem_object *gem_obj;
+ int ret = 0, label;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!args->len)
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -1023,7 +1023,7 @@ static int vc4_async_page_flip(struct dr
+ struct vc4_bo *bo = to_vc4_bo(&dma_bo->base);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ /*
+@@ -1066,7 +1066,7 @@ int vc4_page_flip(struct drm_crtc *crtc,
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (vc4->gen == VC4_GEN_5)
++ if (vc4->gen > VC4_GEN_4)
+ return vc5_async_page_flip(crtc, fb, event, flags);
+ else
+ return vc4_async_page_flip(crtc, fb, event, flags);
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -98,7 +98,7 @@ static int vc4_get_param_ioctl(struct dr
+ if (args->pad != 0)
+ return -EINVAL;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!vc4->v3d)
+@@ -147,7 +147,7 @@ static int vc4_open(struct drm_device *d
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_file *vc4file;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ vc4file = kzalloc(sizeof(*vc4file), GFP_KERNEL);
+@@ -165,7 +165,7 @@ static void vc4_close(struct drm_device
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_file *vc4file = file->driver_priv;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ if (vc4file->bin_bo_used)
+@@ -315,7 +315,7 @@ static int vc4_drm_bind(struct device *d
+ else
+ gen = VC4_GEN_4;
+
+- if (gen == VC4_GEN_5)
++ if (gen > VC4_GEN_4)
+ driver = &vc5_drm_driver;
+ else
+ driver = &vc4_drm_driver;
+--- a/drivers/gpu/drm/vc4/vc4_gem.c
++++ b/drivers/gpu/drm/vc4/vc4_gem.c
+@@ -76,7 +76,7 @@ vc4_get_hang_state_ioctl(struct drm_devi
+ u32 i;
+ int ret = 0;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -389,7 +389,7 @@ vc4_wait_for_seqno(struct drm_device *de
+ unsigned long timeout_expire;
+ DEFINE_WAIT(wait);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (vc4->finished_seqno >= seqno)
+@@ -474,7 +474,7 @@ vc4_submit_next_bin_job(struct drm_devic
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_exec_info *exec;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ again:
+@@ -522,7 +522,7 @@ vc4_submit_next_render_job(struct drm_de
+ if (!exec)
+ return;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ /* A previous RCL may have written to one of our textures, and
+@@ -543,7 +543,7 @@ vc4_move_job_to_render(struct drm_device
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ bool was_empty = list_empty(&vc4->render_job_list);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ list_move_tail(&exec->head, &vc4->render_job_list);
+@@ -1012,7 +1012,7 @@ vc4_job_handle_completed(struct vc4_dev
+ unsigned long irqflags;
+ struct vc4_seqno_cb *cb, *cb_temp;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ spin_lock_irqsave(&vc4->job_lock, irqflags);
+@@ -1051,7 +1051,7 @@ int vc4_queue_seqno_cb(struct drm_device
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ unsigned long irqflags;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ cb->func = func;
+@@ -1107,7 +1107,7 @@ vc4_wait_seqno_ioctl(struct drm_device *
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_vc4_wait_seqno *args = data;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ return vc4_wait_for_seqno_ioctl_helper(dev, args->seqno,
+@@ -1124,7 +1124,7 @@ vc4_wait_bo_ioctl(struct drm_device *dev
+ struct drm_gem_object *gem_obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (args->pad != 0)
+@@ -1173,7 +1173,7 @@ vc4_submit_cl_ioctl(struct drm_device *d
+ args->shader_rec_size,
+ args->bo_handle_count);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -1310,7 +1310,7 @@ int vc4_gem_init(struct drm_device *dev)
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ vc4->dma_fence_context = dma_fence_context_alloc(1);
+@@ -1369,7 +1369,7 @@ int vc4_gem_madvise_ioctl(struct drm_dev
+ struct vc4_bo *bo;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ switch (args->madv) {
+--- a/drivers/gpu/drm/vc4/vc4_irq.c
++++ b/drivers/gpu/drm/vc4/vc4_irq.c
+@@ -265,7 +265,7 @@ vc4_irq_enable(struct drm_device *dev)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ if (!vc4->v3d)
+@@ -282,7 +282,7 @@ vc4_irq_disable(struct drm_device *dev)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ if (!vc4->v3d)
+@@ -305,7 +305,7 @@ int vc4_irq_install(struct drm_device *d
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (irq == IRQ_NOTCONNECTED)
+@@ -326,7 +326,7 @@ void vc4_irq_uninstall(struct drm_device
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ vc4_irq_disable(dev);
+@@ -339,7 +339,7 @@ void vc4_irq_reset(struct drm_device *de
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ unsigned long irqflags;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ /* Acknowledge any stale IRQs. */
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -482,7 +482,7 @@ static struct drm_framebuffer *vc4_fb_cr
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct drm_mode_fb_cmd2 mode_cmd_local;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return ERR_PTR(-ENODEV);
+
+ /* If the user didn't specify a modifier, use the
+--- a/drivers/gpu/drm/vc4/vc4_perfmon.c
++++ b/drivers/gpu/drm/vc4/vc4_perfmon.c
+@@ -23,7 +23,7 @@ void vc4_perfmon_get(struct vc4_perfmon
+ return;
+
+ vc4 = perfmon->dev;
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ refcount_inc(&perfmon->refcnt);
+@@ -37,7 +37,7 @@ void vc4_perfmon_put(struct vc4_perfmon
+ return;
+
+ vc4 = perfmon->dev;
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ if (refcount_dec_and_test(&perfmon->refcnt))
+@@ -49,7 +49,7 @@ void vc4_perfmon_start(struct vc4_dev *v
+ unsigned int i;
+ u32 mask;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ if (WARN_ON_ONCE(!perfmon || vc4->active_perfmon))
+@@ -69,7 +69,7 @@ void vc4_perfmon_stop(struct vc4_dev *vc
+ {
+ unsigned int i;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ if (WARN_ON_ONCE(!vc4->active_perfmon ||
+@@ -90,7 +90,7 @@ struct vc4_perfmon *vc4_perfmon_find(str
+ struct vc4_dev *vc4 = vc4file->dev;
+ struct vc4_perfmon *perfmon;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return NULL;
+
+ mutex_lock(&vc4file->perfmon.lock);
+@@ -105,7 +105,7 @@ void vc4_perfmon_open_file(struct vc4_fi
+ {
+ struct vc4_dev *vc4 = vc4file->dev;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ mutex_init(&vc4file->perfmon.lock);
+@@ -126,7 +126,7 @@ void vc4_perfmon_close_file(struct vc4_f
+ {
+ struct vc4_dev *vc4 = vc4file->dev;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ mutex_lock(&vc4file->perfmon.lock);
+@@ -146,7 +146,7 @@ int vc4_perfmon_create_ioctl(struct drm_
+ unsigned int i;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -200,7 +200,7 @@ int vc4_perfmon_destroy_ioctl(struct drm
+ struct drm_vc4_perfmon_destroy *req = data;
+ struct vc4_perfmon *perfmon;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+@@ -228,7 +228,7 @@ int vc4_perfmon_get_values_ioctl(struct
+ struct vc4_perfmon *perfmon;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (!vc4->v3d) {
+--- a/drivers/gpu/drm/vc4/vc4_render_cl.c
++++ b/drivers/gpu/drm/vc4/vc4_render_cl.c
+@@ -599,7 +599,7 @@ int vc4_get_rcl(struct drm_device *dev,
+ bool has_bin = args->bin_cl_size != 0;
+ int ret;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ if (args->min_x_tile > args->max_x_tile ||
+--- a/drivers/gpu/drm/vc4/vc4_v3d.c
++++ b/drivers/gpu/drm/vc4/vc4_v3d.c
+@@ -127,7 +127,7 @@ static int vc4_v3d_debugfs_ident(struct
+ int
+ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ {
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ mutex_lock(&vc4->power_lock);
+@@ -148,7 +148,7 @@ vc4_v3d_pm_get(struct vc4_dev *vc4)
+ void
+ vc4_v3d_pm_put(struct vc4_dev *vc4)
+ {
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ mutex_lock(&vc4->power_lock);
+@@ -178,7 +178,7 @@ int vc4_v3d_get_bin_slot(struct vc4_dev
+ uint64_t seqno = 0;
+ struct vc4_exec_info *exec;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ try_again:
+@@ -325,7 +325,7 @@ int vc4_v3d_bin_bo_get(struct vc4_dev *v
+ {
+ int ret = 0;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ mutex_lock(&vc4->bin_bo_lock);
+@@ -360,7 +360,7 @@ static void bin_bo_release(struct kref *
+
+ void vc4_v3d_bin_bo_put(struct vc4_dev *vc4)
+ {
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return;
+
+ mutex_lock(&vc4->bin_bo_lock);
+--- a/drivers/gpu/drm/vc4/vc4_validate.c
++++ b/drivers/gpu/drm/vc4/vc4_validate.c
+@@ -109,7 +109,7 @@ vc4_use_bo(struct vc4_exec_info *exec, u
+ struct drm_gem_dma_object *obj;
+ struct vc4_bo *bo;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return NULL;
+
+ if (hindex >= exec->bo_count) {
+@@ -169,7 +169,7 @@ vc4_check_tex_size(struct vc4_exec_info
+ uint32_t utile_w = utile_width(cpp);
+ uint32_t utile_h = utile_height(cpp);
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return false;
+
+ /* The shaded vertex format stores signed 12.4 fixed point
+@@ -495,7 +495,7 @@ vc4_validate_bin_cl(struct drm_device *d
+ uint32_t dst_offset = 0;
+ uint32_t src_offset = 0;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ while (src_offset < len) {
+@@ -942,7 +942,7 @@ vc4_validate_shader_recs(struct drm_devi
+ uint32_t i;
+ int ret = 0;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return -ENODEV;
+
+ for (i = 0; i < exec->shader_state_count; i++) {
+--- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
++++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
+@@ -786,7 +786,7 @@ vc4_validate_shader(struct drm_gem_dma_o
+ struct vc4_validated_shader_info *validated_shader = NULL;
+ struct vc4_shader_validation_state validation_state;
+
+- if (WARN_ON_ONCE(vc4->gen == VC4_GEN_5))
++ if (WARN_ON_ONCE(vc4->gen > VC4_GEN_4))
+ return NULL;
+
+ memset(&validation_state, 0, sizeof(validation_state));
--- /dev/null
+From 72e5eb3d9511af2f056911d70c4d033d4fc674b2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:07:29 +0100
+Subject: [PATCH] drm/vc4: hvs: Use switch statement to simplify
+ vc4_hvs_get_fifo_from_output
+
+Since we'll support BCM2712 soon, let's move the logic behind
+vc4_hvs_get_fifo_from_output() to a switch to extend it more easily.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 80 +++++++++++++++++++----------------
+ 1 file changed, 43 insertions(+), 37 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -620,57 +620,63 @@ int vc4_hvs_get_fifo_from_output(struct
+ u32 reg;
+ int ret;
+
+- if (vc4->gen == VC4_GEN_4)
++ switch (vc4->gen) {
++ case VC4_GEN_4:
+ return output;
+
+- /*
+- * NOTE: We should probably use drm_dev_enter()/drm_dev_exit()
+- * here, but this function is only used during the DRM device
+- * initialization, so we should be fine.
+- */
+-
+- switch (output) {
+- case 0:
+- return 0;
+-
+- case 1:
+- return 1;
+-
+- case 2:
+- reg = HVS_READ(SCALER_DISPECTRL);
+- ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg);
+- if (ret == 0)
+- return 2;
+-
+- return 0;
+-
+- case 3:
+- reg = HVS_READ(SCALER_DISPCTRL);
+- ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg);
+- if (ret == 3)
+- return -EPIPE;
+-
+- return ret;
+-
+- case 4:
+- reg = HVS_READ(SCALER_DISPEOLN);
+- ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg);
+- if (ret == 3)
+- return -EPIPE;
++ case VC4_GEN_5:
++ /*
++ * NOTE: We should probably use
++ * drm_dev_enter()/drm_dev_exit() here, but this
++ * function is only used during the DRM device
++ * initialization, so we should be fine.
++ */
++
++ switch (output) {
++ case 0:
++ return 0;
++
++ case 1:
++ return 1;
++
++ case 2:
++ reg = HVS_READ(SCALER_DISPECTRL);
++ ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg);
++ if (ret == 0)
++ return 2;
++
++ return 0;
++
++ case 3:
++ reg = HVS_READ(SCALER_DISPCTRL);
++ ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg);
++ if (ret == 3)
++ return -EPIPE;
++
++ return ret;
++
++ case 4:
++ reg = HVS_READ(SCALER_DISPEOLN);
++ ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg);
++ if (ret == 3)
++ return -EPIPE;
++
++ return ret;
++
++ case 5:
++ reg = HVS_READ(SCALER_DISPDITHER);
++ ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg);
++ if (ret == 3)
++ return -EPIPE;
+
+- return ret;
++ return ret;
+
+- case 5:
+- reg = HVS_READ(SCALER_DISPDITHER);
+- ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg);
+- if (ret == 3)
++ default:
+ return -EPIPE;
+-
+- return ret;
+-
+- default:
+- return -EPIPE;
++ }
+ }
++
++ return -EPIPE;
+ }
+
+ static int vc4_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
--- /dev/null
+From 72bfb10c9393688d00e4e0b00d416e23c2753318 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:07:29 +0100
+Subject: [PATCH] drm/vc4: hvs: Use switch statement to simplify
+ enabling/disabling irq
+
+Since we'll support BCM2712 soon, let's move the logic to enable and
+disable the end-of-frame interrupts to a switch to extend it more
+easily.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 42 ++++++++++++++++++++++++++---------
+ 1 file changed, 32 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -416,24 +416,46 @@ static void vc4_hvs_irq_enable_eof(const
+ unsigned int channel)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+- u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+- SCALER5_DISPCTRL_DSPEIEOF(channel) :
+- SCALER_DISPCTRL_DSPEIEOF(channel);
+
+- HVS_WRITE(SCALER_DISPCTRL,
+- HVS_READ(SCALER_DISPCTRL) | irq_mask);
++ switch (vc4->gen) {
++ case VC4_GEN_4:
++ HVS_WRITE(SCALER_DISPCTRL,
++ HVS_READ(SCALER_DISPCTRL) |
++ SCALER_DISPCTRL_DSPEIEOF(channel));
++ break;
++
++ case VC4_GEN_5:
++ HVS_WRITE(SCALER_DISPCTRL,
++ HVS_READ(SCALER_DISPCTRL) |
++ SCALER5_DISPCTRL_DSPEIEOF(channel));
++ break;
++
++ default:
++ break;
++ }
+ }
+
+ static void vc4_hvs_irq_clear_eof(const struct vc4_hvs *hvs,
+ unsigned int channel)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+- u32 irq_mask = vc4->gen == VC4_GEN_5 ?
+- SCALER5_DISPCTRL_DSPEIEOF(channel) :
+- SCALER_DISPCTRL_DSPEIEOF(channel);
+
+- HVS_WRITE(SCALER_DISPCTRL,
+- HVS_READ(SCALER_DISPCTRL) & ~irq_mask);
++ switch (vc4->gen) {
++ case VC4_GEN_4:
++ HVS_WRITE(SCALER_DISPCTRL,
++ HVS_READ(SCALER_DISPCTRL) &
++ ~SCALER_DISPCTRL_DSPEIEOF(channel));
++ break;
++
++ case VC4_GEN_5:
++ HVS_WRITE(SCALER_DISPCTRL,
++ HVS_READ(SCALER_DISPCTRL) &
++ ~SCALER5_DISPCTRL_DSPEIEOF(channel));
++ break;
++
++ default:
++ break;
++ }
+ }
+
+ static struct vc4_hvs_dlist_allocation *
--- /dev/null
+From bcf02f6ac0d429a425e8409f140bd875a1feed2e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 13:46:53 +0200
+Subject: [PATCH] drm/vc4: hvs: Test if the EOF interrupts are enabled
+
+We currently enable the EOF interrupts through the CRTC destroy_state
+implementation.
+
+However, nothing guarantees that we can't call destroy_state multiple
+times in a row, and therefore before the EOF interrupt even happens.
+
+This means we would enable the interrupt multiple times but disable it
+only once. It wasn't an issue so far since the interrupts were only
+enabled by setting a bit in a register, but with BCM2712 we will use an
+external interrupt controller, with a refcounted interrupt.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 8 ++++++--
+ drivers/gpu/drm/vc4/vc4_hvs.c | 14 ++++++++++++--
+ 2 files changed, 18 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -333,6 +333,8 @@ struct vc4_v3d {
+ struct debugfs_regset32 regset;
+ };
+
++#define HVS_NUM_CHANNELS 3
++
+ struct vc4_hvs {
+ struct vc4_dev *vc4;
+ struct platform_device *pdev;
+@@ -341,6 +343,10 @@ struct vc4_hvs {
+
+ struct clk *core_clk;
+
++ struct {
++ unsigned int enabled: 1;
++ } eof_irq[HVS_NUM_CHANNELS];
++
+ unsigned long max_core_rate;
+
+ /* Memory manager for CRTCs to allocate space in the display
+@@ -373,8 +379,6 @@ struct vc4_hvs {
+ bool vc5_hdmi_enable_4096by2160;
+ };
+
+-#define HVS_NUM_CHANNELS 3
+-
+ struct vc4_hvs_state {
+ struct drm_private_state base;
+ unsigned long core_clock_rate;
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -412,11 +412,14 @@ static void vc5_hvs_update_gamma_lut(str
+ vc5_hvs_lut_load(hvs, vc4_crtc);
+ }
+
+-static void vc4_hvs_irq_enable_eof(const struct vc4_hvs *hvs,
++static void vc4_hvs_irq_enable_eof(struct vc4_hvs *hvs,
+ unsigned int channel)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+
++ if (hvs->eof_irq[channel].enabled)
++ return;
++
+ switch (vc4->gen) {
+ case VC4_GEN_4:
+ HVS_WRITE(SCALER_DISPCTRL,
+@@ -433,13 +436,18 @@ static void vc4_hvs_irq_enable_eof(const
+ default:
+ break;
+ }
++
++ hvs->eof_irq[channel].enabled = true;
+ }
+
+-static void vc4_hvs_irq_clear_eof(const struct vc4_hvs *hvs,
++static void vc4_hvs_irq_clear_eof(struct vc4_hvs *hvs,
+ unsigned int channel)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+
++ if (!hvs->eof_irq[channel].enabled)
++ return;
++
+ switch (vc4->gen) {
+ case VC4_GEN_4:
+ HVS_WRITE(SCALER_DISPCTRL,
+@@ -456,6 +464,8 @@ static void vc4_hvs_irq_clear_eof(const
+ default:
+ break;
+ }
++
++ hvs->eof_irq[channel].enabled = false;
+ }
+
+ static struct vc4_hvs_dlist_allocation *
--- /dev/null
+From f3c84bb53107cef0009347d071c1a188ce24b8a3 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 14:36:28 +0100
+Subject: [PATCH] drm/vc4: hvs: Create hw_init function
+
+Since the BCM2712 will feature a significantly different HVS, let's move
+the hardware initialisation part of our bind function into a separate
+function.
+
+That way, it will be easier to extend in the future.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 155 ++++++++++++++++++----------------
+ 1 file changed, 83 insertions(+), 72 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1291,79 +1291,10 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ return hvs;
+ }
+
+-static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
++static int vc4_hvs_hw_init(struct vc4_hvs *hvs)
+ {
+- struct platform_device *pdev = to_platform_device(dev);
+- struct drm_device *drm = dev_get_drvdata(master);
+- struct vc4_dev *vc4 = to_vc4_dev(drm);
+- struct vc4_hvs *hvs = NULL;
+- int ret;
+- u32 dispctrl;
+- u32 reg, top;
+-
+- hvs = __vc4_hvs_alloc(vc4, NULL);
+- if (IS_ERR(hvs))
+- return PTR_ERR(hvs);
+-
+- hvs->regs = vc4_ioremap_regs(pdev, 0);
+- if (IS_ERR(hvs->regs))
+- return PTR_ERR(hvs->regs);
+-
+- hvs->regset.base = hvs->regs;
+- hvs->regset.regs = hvs_regs;
+- hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
+-
+- if (vc4->gen == VC4_GEN_5) {
+- struct rpi_firmware *firmware;
+- struct device_node *node;
+- unsigned int max_rate;
+-
+- node = rpi_firmware_find_node();
+- if (!node)
+- return -EINVAL;
+-
+- firmware = rpi_firmware_get(node);
+- of_node_put(node);
+- if (!firmware)
+- return -EPROBE_DEFER;
+-
+- hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
+- if (IS_ERR(hvs->core_clk)) {
+- dev_err(&pdev->dev, "Couldn't get core clock\n");
+- return PTR_ERR(hvs->core_clk);
+- }
+-
+- max_rate = rpi_firmware_clk_get_max_rate(firmware,
+- RPI_FIRMWARE_CORE_CLK_ID);
+- rpi_firmware_put(firmware);
+- if (max_rate >= 550000000)
+- hvs->vc5_hdmi_enable_hdmi_20 = true;
+-
+- if (max_rate >= 600000000)
+- hvs->vc5_hdmi_enable_4096by2160 = true;
+-
+- hvs->max_core_rate = max_rate;
+-
+- ret = clk_prepare_enable(hvs->core_clk);
+- if (ret) {
+- dev_err(&pdev->dev, "Couldn't enable the core clock\n");
+- return ret;
+- }
+- }
+-
+- if (vc4->gen == VC4_GEN_4)
+- hvs->dlist = hvs->regs + SCALER_DLIST_START;
+- else
+- hvs->dlist = hvs->regs + SCALER5_DLIST_START;
+-
+- /* Upload filter kernels. We only have the one for now, so we
+- * keep it around for the lifetime of the driver.
+- */
+- ret = vc4_hvs_upload_linear_kernel(hvs,
+- &hvs->mitchell_netravali_filter,
+- mitchell_netravali_1_3_1_3_kernel);
+- if (ret)
+- return ret;
++ struct vc4_dev *vc4 = hvs->vc4;
++ u32 dispctrl, reg;
+
+ reg = HVS_READ(SCALER_DISPECTRL);
+ reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK;
+@@ -1445,6 +1376,86 @@ static int vc4_hvs_bind(struct device *d
+
+ HVS_WRITE(SCALER_DISPCTRL, dispctrl);
+
++ return 0;
++}
++
++static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct drm_device *drm = dev_get_drvdata(master);
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
++ struct vc4_hvs *hvs = NULL;
++ int ret;
++ u32 reg, top;
++
++ hvs = __vc4_hvs_alloc(vc4, NULL);
++ if (IS_ERR(hvs))
++ return PTR_ERR(hvs);
++
++ hvs->regs = vc4_ioremap_regs(pdev, 0);
++ if (IS_ERR(hvs->regs))
++ return PTR_ERR(hvs->regs);
++
++ hvs->regset.base = hvs->regs;
++ hvs->regset.regs = hvs_regs;
++ hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
++
++ if (vc4->gen == VC4_GEN_5) {
++ struct rpi_firmware *firmware;
++ struct device_node *node;
++ unsigned int max_rate;
++
++ node = rpi_firmware_find_node();
++ if (!node)
++ return -EINVAL;
++
++ firmware = rpi_firmware_get(node);
++ of_node_put(node);
++ if (!firmware)
++ return -EPROBE_DEFER;
++
++ hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
++ if (IS_ERR(hvs->core_clk)) {
++ dev_err(&pdev->dev, "Couldn't get core clock\n");
++ return PTR_ERR(hvs->core_clk);
++ }
++
++ max_rate = rpi_firmware_clk_get_max_rate(firmware,
++ RPI_FIRMWARE_CORE_CLK_ID);
++ rpi_firmware_put(firmware);
++ if (max_rate >= 550000000)
++ hvs->vc5_hdmi_enable_hdmi_20 = true;
++
++ if (max_rate >= 600000000)
++ hvs->vc5_hdmi_enable_4096by2160 = true;
++
++ hvs->max_core_rate = max_rate;
++
++ ret = clk_prepare_enable(hvs->core_clk);
++ if (ret) {
++ dev_err(&pdev->dev, "Couldn't enable the core clock\n");
++ return ret;
++ }
++ }
++
++ if (vc4->gen == VC4_GEN_4)
++ hvs->dlist = hvs->regs + SCALER_DLIST_START;
++ else
++ hvs->dlist = hvs->regs + SCALER5_DLIST_START;
++
++ /* Upload filter kernels. We only have the one for now, so we
++ * keep it around for the lifetime of the driver.
++ */
++ ret = vc4_hvs_upload_linear_kernel(hvs,
++ &hvs->mitchell_netravali_filter,
++ mitchell_netravali_1_3_1_3_kernel);
++ if (ret)
++ return ret;
++
++ ret = vc4_hvs_hw_init(hvs);
++ if (ret)
++ return ret;
++
+ /* Recompute Composite Output Buffer (COB) allocations for the displays
+ */
+ if (vc4->gen == VC4_GEN_4) {
--- /dev/null
+From 99a13ce3a12303dfb54815637972627a7d207086 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:14:55 +0100
+Subject: [PATCH] drm/vc4: hvs: Create cob_init function
+
+Just like the HVS itself, the COB parameters will be fairly different in
+the BCM2712.
+
+Let's move the COB parameters computation and its initialisation to a
+separate function that will be easier to extend in the future.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 128 ++++++++++++++++++++--------------
+ 1 file changed, 74 insertions(+), 54 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1379,6 +1379,77 @@ static int vc4_hvs_hw_init(struct vc4_hv
+ return 0;
+ }
+
++static int vc4_hvs_cob_init(struct vc4_hvs *hvs)
++{
++ struct vc4_dev *vc4 = hvs->vc4;
++ u32 reg, top;
++
++ /*
++ * Recompute Composite Output Buffer (COB) allocations for the
++ * displays
++ */
++ switch (vc4->gen) {
++ case VC4_GEN_4:
++ /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
++ * The bottom 2048 pixels are full 32bpp RGBA (intended for the
++ * TXP composing RGBA to memory), whilst the remainder are only
++ * 24bpp RGB.
++ *
++ * Assign 3 lines to channels 1 & 2, and just over 4 lines to
++ * channel 0.
++ */
++ #define VC4_COB_SIZE 20736
++ #define VC4_COB_LINE_WIDTH 2048
++ #define VC4_COB_NUM_LINES 3
++ reg = 0;
++ top = VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
++ reg |= (top - 1) << 16;
++ HVS_WRITE(SCALER_DISPBASE2, reg);
++ reg = top;
++ top += VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
++ reg |= (top - 1) << 16;
++ HVS_WRITE(SCALER_DISPBASE1, reg);
++ reg = top;
++ top = VC4_COB_SIZE;
++ reg |= (top - 1) << 16;
++ HVS_WRITE(SCALER_DISPBASE0, reg);
++ break;
++
++ case VC4_GEN_5:
++ /* The COB is 44416 pixels, or 10.8 lines at 4096 wide.
++ * The bottom 4096 pixels are full RGBA (intended for the TXP
++ * composing RGBA to memory), whilst the remainder are only
++ * RGB. Addressing is always pixel wide.
++ *
++ * Assign 3 lines of 4096 to channels 1 & 2, and just over 4
++ * lines. to channel 0.
++ */
++ #define VC5_COB_SIZE 44416
++ #define VC5_COB_LINE_WIDTH 4096
++ #define VC5_COB_NUM_LINES 3
++ reg = 0;
++ top = VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
++ reg |= top << 16;
++ HVS_WRITE(SCALER_DISPBASE2, reg);
++ top += 16;
++ reg = top;
++ top += VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
++ reg |= top << 16;
++ HVS_WRITE(SCALER_DISPBASE1, reg);
++ top += 16;
++ reg = top;
++ top = VC5_COB_SIZE;
++ reg |= top << 16;
++ HVS_WRITE(SCALER_DISPBASE0, reg);
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
+ static int vc4_hvs_bind(struct device *dev, struct device *master, void *data)
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+@@ -1386,7 +1457,6 @@ static int vc4_hvs_bind(struct device *d
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_hvs *hvs = NULL;
+ int ret;
+- u32 reg, top;
+
+ hvs = __vc4_hvs_alloc(vc4, NULL);
+ if (IS_ERR(hvs))
+@@ -1456,59 +1526,9 @@ static int vc4_hvs_bind(struct device *d
+ if (ret)
+ return ret;
+
+- /* Recompute Composite Output Buffer (COB) allocations for the displays
+- */
+- if (vc4->gen == VC4_GEN_4) {
+- /* The COB is 20736 pixels, or just over 10 lines at 2048 wide.
+- * The bottom 2048 pixels are full 32bpp RGBA (intended for the
+- * TXP composing RGBA to memory), whilst the remainder are only
+- * 24bpp RGB.
+- *
+- * Assign 3 lines to channels 1 & 2, and just over 4 lines to
+- * channel 0.
+- */
+- #define VC4_COB_SIZE 20736
+- #define VC4_COB_LINE_WIDTH 2048
+- #define VC4_COB_NUM_LINES 3
+- reg = 0;
+- top = VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
+- reg |= (top - 1) << 16;
+- HVS_WRITE(SCALER_DISPBASE2, reg);
+- reg = top;
+- top += VC4_COB_LINE_WIDTH * VC4_COB_NUM_LINES;
+- reg |= (top - 1) << 16;
+- HVS_WRITE(SCALER_DISPBASE1, reg);
+- reg = top;
+- top = VC4_COB_SIZE;
+- reg |= (top - 1) << 16;
+- HVS_WRITE(SCALER_DISPBASE0, reg);
+- } else {
+- /* The COB is 44416 pixels, or 10.8 lines at 4096 wide.
+- * The bottom 4096 pixels are full RGBA (intended for the TXP
+- * composing RGBA to memory), whilst the remainder are only
+- * RGB. Addressing is always pixel wide.
+- *
+- * Assign 3 lines of 4096 to channels 1 & 2, and just over 4
+- * lines. to channel 0.
+- */
+- #define VC5_COB_SIZE 44416
+- #define VC5_COB_LINE_WIDTH 4096
+- #define VC5_COB_NUM_LINES 3
+- reg = 0;
+- top = VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
+- reg |= top << 16;
+- HVS_WRITE(SCALER_DISPBASE2, reg);
+- top += 16;
+- reg = top;
+- top += VC5_COB_LINE_WIDTH * VC5_COB_NUM_LINES;
+- reg |= top << 16;
+- HVS_WRITE(SCALER_DISPBASE1, reg);
+- top += 16;
+- reg = top;
+- top = VC5_COB_SIZE;
+- reg |= top << 16;
+- HVS_WRITE(SCALER_DISPBASE0, reg);
+- }
++ ret = vc4_hvs_cob_init(hvs);
++ if (ret)
++ return ret;
+
+ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+ vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
--- /dev/null
+From 7a1c157ac856384c47df38e1de2995f55a111b85 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:41:59 +0100
+Subject: [PATCH] drm/vc4: hvs: Rename hvs_regs list
+
+The HVS register set has been heavily modified in the BCM2712, and we'll
+thus need a separate debugfs_reg32 array for it.
+
+The name hvs_regs is thus a bit too generic, so let's rename it to
+something more specific.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -33,7 +33,7 @@
+ #include "vc4_drv.h"
+ #include "vc4_regs.h"
+
+-static const struct debugfs_reg32 hvs_regs[] = {
++static const struct debugfs_reg32 vc4_hvs_regs[] = {
+ VC4_REG32(SCALER_DISPCTRL),
+ VC4_REG32(SCALER_DISPSTAT),
+ VC4_REG32(SCALER_DISPID),
+@@ -1467,8 +1467,8 @@ static int vc4_hvs_bind(struct device *d
+ return PTR_ERR(hvs->regs);
+
+ hvs->regset.base = hvs->regs;
+- hvs->regset.regs = hvs_regs;
+- hvs->regset.nregs = ARRAY_SIZE(hvs_regs);
++ hvs->regset.regs = vc4_hvs_regs;
++ hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
+
+ if (vc4->gen == VC4_GEN_5) {
+ struct rpi_firmware *firmware;
--- /dev/null
+From 531f66804eb95323f807d240273087fbe162aeee Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 09:56:31 +0100
+Subject: [PATCH] drm/vc4: plane: Change ptr0_offset to an array
+
+The BCM2712 will have a fairly different dlist, that will feature one
+Pointer 0 word for each plane.
+
+Let's prepare by changing the ptr0_offset variable that holds the offset
+in a dlist of the pointer 0 word to an array.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 3 ++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 18 +++++++++---------
+ 2 files changed, 11 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -14,6 +14,7 @@
+ #include <drm/drm_debugfs.h>
+ #include <drm/drm_device.h>
+ #include <drm/drm_encoder.h>
++#include <drm/drm_fourcc.h>
+ #include <drm/drm_gem_dma_helper.h>
+ #include <drm/drm_managed.h>
+ #include <drm/drm_mm.h>
+@@ -430,7 +431,7 @@ struct vc4_plane_state {
+ */
+ u32 pos0_offset;
+ u32 pos2_offset;
+- u32 ptr0_offset;
++ u32 ptr0_offset[DRM_FORMAT_MAX_PLANES];
+ u32 lbm_offset;
+
+ /* Offset where the plane's dlist was last stored in the
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1242,7 +1242,7 @@ static int vc4_plane_mode_set(struct drm
+ *
+ * The pointers may be any byte address.
+ */
+- vc4_state->ptr0_offset = vc4_state->dlist_count;
++ vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
+ for (i = 0; i < num_planes; i++)
+ vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
+
+@@ -1447,13 +1447,13 @@ void vc4_plane_async_set_fb(struct drm_p
+ * scanout will start from this address as soon as the FIFO
+ * needs to refill with pixels.
+ */
+- writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
++ writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
+
+ /* Also update the CPU-side dlist copy, so that any later
+ * atomic updates that don't do a new modeset on our plane
+ * also use our updated address.
+ */
+- vc4_state->dlist[vc4_state->ptr0_offset] = addr;
++ vc4_state->dlist[vc4_state->ptr0_offset[0]] = addr;
+
+ drm_dev_exit(idx);
+ }
+@@ -1517,8 +1517,8 @@ static void vc4_plane_atomic_async_updat
+ new_vc4_state->dlist[vc4_state->pos0_offset];
+ vc4_state->dlist[vc4_state->pos2_offset] =
+ new_vc4_state->dlist[vc4_state->pos2_offset];
+- vc4_state->dlist[vc4_state->ptr0_offset] =
+- new_vc4_state->dlist[vc4_state->ptr0_offset];
++ vc4_state->dlist[vc4_state->ptr0_offset[0]] =
++ new_vc4_state->dlist[vc4_state->ptr0_offset[0]];
+
+ /* Note that we can't just call vc4_plane_write_dlist()
+ * because that would smash the context data that the HVS is
+@@ -1528,8 +1528,8 @@ static void vc4_plane_atomic_async_updat
+ &vc4_state->hw_dlist[vc4_state->pos0_offset]);
+ writel(vc4_state->dlist[vc4_state->pos2_offset],
+ &vc4_state->hw_dlist[vc4_state->pos2_offset]);
+- writel(vc4_state->dlist[vc4_state->ptr0_offset],
+- &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
++ writel(vc4_state->dlist[vc4_state->ptr0_offset[0]],
++ &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
+
+ drm_dev_exit(idx);
+ }
+@@ -1556,7 +1556,7 @@ static int vc4_plane_atomic_async_check(
+ if (old_vc4_state->dlist_count != new_vc4_state->dlist_count ||
+ old_vc4_state->pos0_offset != new_vc4_state->pos0_offset ||
+ old_vc4_state->pos2_offset != new_vc4_state->pos2_offset ||
+- old_vc4_state->ptr0_offset != new_vc4_state->ptr0_offset ||
++ old_vc4_state->ptr0_offset[0] != new_vc4_state->ptr0_offset[0] ||
+ vc4_lbm_size(plane->state) != vc4_lbm_size(new_plane_state))
+ return -EINVAL;
+
+@@ -1566,7 +1566,7 @@ static int vc4_plane_atomic_async_check(
+ for (i = 0; i < new_vc4_state->dlist_count; i++) {
+ if (i == new_vc4_state->pos0_offset ||
+ i == new_vc4_state->pos2_offset ||
+- i == new_vc4_state->ptr0_offset ||
++ i == new_vc4_state->ptr0_offset[0] ||
+ (new_vc4_state->lbm_offset &&
+ i == new_vc4_state->lbm_offset))
+ continue;
--- /dev/null
+From 2834b58a3b58198e551d8461a0786b75d3d76823 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 13 Apr 2023 10:12:19 +0200
+Subject: [PATCH] drm/vc4: hvs: Rework LBM alignment
+
+With the introduction of the support for BCM2712, the check of whether
+we're running on vc5 or not to compute the LBM alignment requirement
+doesn't work anymore.
+
+Moreover, the LBM size will need to be computed in words for the
+BCM2712, while we've had sizes in bytes so far.
+
+Aligning on either 64 or 32 words is thus fairly harmful on BCM2712, so
+let's just explicitly align the size when needed, and then call
+drm_mm_insert_node_generic() with an alignment of 1.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -744,6 +744,11 @@ static int vc4_plane_allocate_lbm(struct
+ if (!lbm_size)
+ return 0;
+
++ if (vc4->gen == VC4_GEN_5)
++ lbm_size = ALIGN(lbm_size, 64);
++ else if (vc4->gen == VC4_GEN_4)
++ lbm_size = ALIGN(lbm_size, 32);
++
+ drm_dbg_driver(drm, "[PLANE:%d:%s] LBM Allocation Size: %u\n",
+ plane->base.id, plane->name, lbm_size);
+
+@@ -759,8 +764,7 @@ static int vc4_plane_allocate_lbm(struct
+ spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
+ ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+ &vc4_state->lbm,
+- lbm_size,
+- vc4->gen == VC4_GEN_5 ? 64 : 32,
++ lbm_size, 1,
+ 0, 0);
+ spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+
--- /dev/null
+From 23cba2abd5ffbb7337e3f381c4724eaaf01cf5a1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 15:45:50 +0100
+Subject: [PATCH] drm/vc4: hvs: Change prototype of __vc4_hvs_alloc to pass
+ registers
+
+The BCM2712 HVS has registers to report the size of the various SRAM the
+driver uses, and their size actually differ depending on the stepping.
+
+The initialisation of the memory pools happen in the __vc4_hvs_alloc()
+function that also allocates the main HVS structure, that will then hold
+the pointer to the memory mapping of the registers.
+
+This creates some kind of circular dependency that we can break by
+passing the mapping pointer as an argument for __vc4_hvs_alloc() to use
+to query to get the SRAM sizes and initialise the memory pools
+accordingly.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_drv.h | 4 +++-
+ drivers/gpu/drm/vc4/vc4_hvs.c | 16 ++++++++++------
+ 3 files changed, 14 insertions(+), 8 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -173,7 +173,7 @@ static struct vc4_dev *__mock_device(str
+ vc4->dev = dev;
+ vc4->gen = gen;
+
+- vc4->hvs = __vc4_hvs_alloc(vc4, NULL);
++ vc4->hvs = __vc4_hvs_alloc(vc4, NULL, NULL);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4->hvs);
+
+ drm = &vc4->base;
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -1092,7 +1092,9 @@ void vc4_irq_reset(struct drm_device *de
+
+ /* vc4_hvs.c */
+ extern struct platform_driver vc4_hvs_driver;
+-struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev);
++struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4,
++ void __iomem *regs,
++ struct platform_device *pdev);
+ void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int output);
+ int vc4_hvs_get_fifo_from_output(struct vc4_hvs *hvs, unsigned int output);
+ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo);
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1248,7 +1248,9 @@ int vc4_hvs_debugfs_init(struct drm_mino
+ return 0;
+ }
+
+-struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4, struct platform_device *pdev)
++struct vc4_hvs *__vc4_hvs_alloc(struct vc4_dev *vc4,
++ void __iomem *regs,
++ struct platform_device *pdev)
+ {
+ struct drm_device *drm = &vc4->base;
+ struct vc4_hvs *hvs;
+@@ -1258,6 +1260,7 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ return ERR_PTR(-ENOMEM);
+
+ hvs->vc4 = vc4;
++ hvs->regs = regs;
+ hvs->pdev = pdev;
+
+ spin_lock_init(&hvs->mm_lock);
+@@ -1456,16 +1459,17 @@ static int vc4_hvs_bind(struct device *d
+ struct drm_device *drm = dev_get_drvdata(master);
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_hvs *hvs = NULL;
++ void __iomem *regs;
+ int ret;
+
+- hvs = __vc4_hvs_alloc(vc4, NULL);
++ regs = vc4_ioremap_regs(pdev, 0);
++ if (IS_ERR(regs))
++ return PTR_ERR(regs);
++
++ hvs = __vc4_hvs_alloc(vc4, regs, pdev);
+ if (IS_ERR(hvs))
+ return PTR_ERR(hvs);
+
+- hvs->regs = vc4_ioremap_regs(pdev, 0);
+- if (IS_ERR(hvs->regs))
+- return PTR_ERR(hvs->regs);
+-
+ hvs->regset.base = hvs->regs;
+ hvs->regset.regs = vc4_hvs_regs;
+ hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
--- /dev/null
+From 03e0e348df7b685439f2db61ec2d8c8da25d1217 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 23 Aug 2023 17:48:23 +0100
+Subject: [PATCH] drm/vc4: UV planes vertical scaling must always be enabled
+
+It has been observed that a YUV422 unity scaled plane isn't displayed.
+Enabling vertical scaling on the UV planes solves this. There is
+already a similar clause to always enable horizontal scaling on the
+UV planes.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -522,6 +522,12 @@ static int vc4_plane_setup_clipping_and_
+ */
+ if (vc4_state->x_scaling[1] == VC4_SCALING_NONE)
+ vc4_state->x_scaling[1] = VC4_SCALING_PPF;
++
++ /* Similarly UV needs vertical scaling to be enabled.
++ * Without this a 1:1 scaled YUV422 plane isn't rendered.
++ */
++ if (vc4_state->y_scaling[1] == VC4_SCALING_NONE)
++ vc4_state->y_scaling[1] = VC4_SCALING_PPF;
+ } else {
+ vc4_state->is_yuv = false;
+ vc4_state->x_scaling[1] = VC4_SCALING_NONE;
--- /dev/null
+From e5e7679d634b10e88df340c85cd8368c9f9989eb Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 5 Sep 2023 19:38:24 +0100
+Subject: [PATCH] drm/vc4: hdmi: Avoid hang with debug registers when suspended
+
+Trying to read /sys/kernel/debug/dri/1/hdmi1_regs
+when the hdmi is disconnected results in a fatal system hang.
+
+This is due to the pm suspend code disabling the dvp clock.
+That is just a gate of the 108MHz clock in DVP_HT_RPI_MISC_CONFIG,
+which results in accesses hanging AXI bus.
+
+Protect against this.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -193,6 +193,8 @@ static int vc4_hdmi_debugfs_regs(struct
+ if (!drm_dev_enter(drm, &idx))
+ return -ENODEV;
+
++ WARN_ON(pm_runtime_resume_and_get(&vc4_hdmi->pdev->dev));
++
+ drm_print_regset32(&p, &vc4_hdmi->hdmi_regset);
+ drm_print_regset32(&p, &vc4_hdmi->hd_regset);
+ drm_print_regset32(&p, &vc4_hdmi->cec_regset);
+@@ -202,6 +204,8 @@ static int vc4_hdmi_debugfs_regs(struct
+ drm_print_regset32(&p, &vc4_hdmi->ram_regset);
+ drm_print_regset32(&p, &vc4_hdmi->rm_regset);
+
++ pm_runtime_put(&vc4_hdmi->pdev->dev);
++
+ drm_dev_exit(idx);
+
+ return 0;
--- /dev/null
+From 11cf37e741b439b26fe932750bde841a16a96828 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 25 Sep 2023 16:57:07 +0100
+Subject: [PATCH] drm/vc4: Move the buffer offset out of the vc4_plane_state
+
+The offset fields in vc4_plane_state are described as being
+the offset for each buffer in the bo, however it is used to
+store the complete DMA address that is then written into the
+register.
+
+The DMA address including the fb ofset can be retrieved
+using drm_fb_dma_get_gem_addr, and the offset adjustment due to
+clipping is local to vc4_plane_mode_set.
+Drop the offset field from the state, and compute the complete
+DMA address in vc4_plane_mode_set.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 5 ----
+ drivers/gpu/drm/vc4/vc4_plane.c | 51 +++++++++++++--------------------
+ 2 files changed, 20 insertions(+), 36 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -451,11 +451,6 @@ struct vc4_plane_state {
+ bool is_unity;
+ bool is_yuv;
+
+- /* Offset to start scanning out from the start of the plane's
+- * BO.
+- */
+- u32 offsets[3];
+-
+ /* Our allocation in LBM for temporary storage during scaling. */
+ struct drm_mm_node lbm;
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -450,12 +450,11 @@ static int vc4_plane_setup_clipping_and_
+ {
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+ struct drm_framebuffer *fb = state->fb;
+- struct drm_gem_dma_object *bo;
+ int num_planes = fb->format->num_planes;
+ struct drm_crtc_state *crtc_state;
+ u32 h_subsample = fb->format->hsub;
+ u32 v_subsample = fb->format->vsub;
+- int i, ret;
++ int ret;
+
+ crtc_state = drm_atomic_get_existing_crtc_state(state->state,
+ state->crtc);
+@@ -469,11 +468,6 @@ static int vc4_plane_setup_clipping_and_
+ if (ret)
+ return ret;
+
+- for (i = 0; i < num_planes; i++) {
+- bo = drm_fb_dma_get_gem_obj(fb, i);
+- vc4_state->offsets[i] = bo->dma_addr + fb->offsets[i];
+- }
+-
+ vc4_state->src_x = state->src.x1;
+ vc4_state->src_y = state->src.y1;
+ vc4_state->src_w[0] = state->src.x2 - vc4_state->src_x;
+@@ -896,6 +890,7 @@ static int vc4_plane_mode_set(struct drm
+ u32 width, height;
+ u32 hvs_format = format->hvs;
+ unsigned int rotation;
++ u32 offsets[3] = { 0 };
+ int ret, i;
+
+ if (vc4_state->dlist_initialized)
+@@ -943,13 +938,8 @@ static int vc4_plane_mode_set(struct drm
+ * out.
+ */
+ for (i = 0; i < num_planes; i++) {
+- vc4_state->offsets[i] += src_y /
+- (i ? v_subsample : 1) *
+- fb->pitches[i];
+-
+- vc4_state->offsets[i] += src_x /
+- (i ? h_subsample : 1) *
+- fb->format->cpp[i];
++ offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i];
++ offsets[i] += src_x / (i ? h_subsample : 1) * fb->format->cpp[i];
+ }
+
+ break;
+@@ -1004,19 +994,18 @@ static int vc4_plane_mode_set(struct drm
+ VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) |
+ VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) |
+ VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R));
+- vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift);
+- vc4_state->offsets[0] += subtile_y << 8;
+- vc4_state->offsets[0] += utile_y << 4;
++ offsets[0] += tiles_t * (tiles_w << tile_size_shift);
++ offsets[0] += subtile_y << 8;
++ offsets[0] += utile_y << 4;
+
+ /* Rows of tiles alternate left-to-right and right-to-left. */
+ if (tiles_t & 1) {
+ pitch0 |= SCALER_PITCH0_TILE_INITIAL_LINE_DIR;
+- vc4_state->offsets[0] += (tiles_w - tiles_l) <<
+- tile_size_shift;
+- vc4_state->offsets[0] -= (1 + !tile_y) << 10;
++ offsets[0] += (tiles_w - tiles_l) << tile_size_shift;
++ offsets[0] -= (1 + !tile_y) << 10;
+ } else {
+- vc4_state->offsets[0] += tiles_l << tile_size_shift;
+- vc4_state->offsets[0] += tile_y << 10;
++ offsets[0] += tiles_l << tile_size_shift;
++ offsets[0] += tile_y << 10;
+ }
+
+ break;
+@@ -1105,11 +1094,9 @@ static int vc4_plane_mode_set(struct drm
+
+ tile = src_x / pix_per_tile;
+
+- vc4_state->offsets[i] += param * tile_w * tile;
+- vc4_state->offsets[i] += src_y /
+- (i ? v_subsample : 1) *
+- tile_w;
+- vc4_state->offsets[i] += x_off & ~(i ? 1 : 0);
++ offsets[i] += param * tile_w * tile;
++ offsets[i] += src_y / (i ? v_subsample : 1) * tile_w;
++ offsets[i] += x_off & ~(i ? 1 : 0);
+ }
+
+ pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);
+@@ -1253,8 +1240,12 @@ static int vc4_plane_mode_set(struct drm
+ * The pointers may be any byte address.
+ */
+ vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
+- for (i = 0; i < num_planes; i++)
+- vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
++
++ for (i = 0; i < num_planes; i++) {
++ dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
++
++ vc4_dlist_write(vc4_state, paddr + offsets[i]);
++ }
+
+ /* Pointer Context Word 0/1/2: Written by the HVS */
+ for (i = 0; i < num_planes; i++)
+@@ -1518,8 +1509,6 @@ static void vc4_plane_atomic_async_updat
+ sizeof(vc4_state->y_scaling));
+ vc4_state->is_unity = new_vc4_state->is_unity;
+ vc4_state->is_yuv = new_vc4_state->is_yuv;
+- memcpy(vc4_state->offsets, new_vc4_state->offsets,
+- sizeof(vc4_state->offsets));
+ vc4_state->needs_bg_fill = new_vc4_state->needs_bg_fill;
+
+ /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */
--- /dev/null
+From 3660abb4a8523e988f1345985e89149804e50ebe Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 24 Aug 2023 15:36:21 +0100
+Subject: [PATCH] drm/vc4: Fix dlist debug not resetting the next entry pointer
+
+The debug function to display the dlists didn't reset next_entry_start
+when starting each display, so resulting in not stopping the
+list at the correct place.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -110,7 +110,7 @@ static int vc4_hvs_debugfs_dlist(struct
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hvs *hvs = vc4->hvs;
+ struct drm_printer p = drm_seq_file_printer(m);
+- unsigned int next_entry_start = 0;
++ unsigned int next_entry_start;
+ unsigned int i, j;
+ u32 dlist_word, dispstat;
+
+@@ -124,6 +124,7 @@ static int vc4_hvs_debugfs_dlist(struct
+ }
+
+ drm_printf(&p, "HVS chan %u:\n", i);
++ next_entry_start = 0;
+
+ for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
+ dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
--- /dev/null
+From ebf11a4cfd9f1236fb9eeb7e32e87b18f5f56f16 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 1 Sep 2023 13:45:08 +0100
+Subject: [PATCH] drm: vc4: Remove incorrect limit from hvs_dlist debugfs
+ function
+
+The debugfs function to dump dlists aborted at 256 bytes,
+when actually the dlist memory is generally significantly
+larger but varies based on SoC.
+
+We already have the correct limit in __vc4_hvs_alloc, so
+store it for use in the debugfs dlist function.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 5 ++++-
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -341,6 +341,7 @@ struct vc4_hvs {
+ struct platform_device *pdev;
+ void __iomem *regs;
+ u32 __iomem *dlist;
++ unsigned int dlist_mem_size;
+
+ struct clk *core_clk;
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -110,6 +110,7 @@ static int vc4_hvs_debugfs_dlist(struct
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hvs *hvs = vc4->hvs;
+ struct drm_printer p = drm_seq_file_printer(m);
++ unsigned int dlist_mem_size = hvs->dlist_mem_size;
+ unsigned int next_entry_start;
+ unsigned int i, j;
+ u32 dlist_word, dispstat;
+@@ -126,7 +127,7 @@ static int vc4_hvs_debugfs_dlist(struct
+ drm_printf(&p, "HVS chan %u:\n", i);
+ next_entry_start = 0;
+
+- for (j = HVS_READ(SCALER_DISPLISTX(i)); j < 256; j++) {
++ for (j = HVS_READ(SCALER_DISPLISTX(i)); j < dlist_mem_size; j++) {
+ dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
+ drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
+ dlist_word);
+@@ -1278,6 +1279,8 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ HVS_BOOTLOADER_DLIST_END,
+ (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
+
++ hvs->dlist_mem_size = dlist_size;
++
+ /* Set up the HVS LBM memory manager. We could have some more
+ * complicated data structure that allowed reuse of LBM areas
+ * between planes when they don't overlap on the screen, but
--- /dev/null
+From 712bccec241e84e28ccb725fae87d3255d039f42 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Thu, 22 Jun 2023 14:06:40 +0100
+Subject: [PATCH] drm/vc4: hvs: Remove ABORT_ON_EMPTY flag
+
+ABORT_ON_EMPTY chooses whether the HVS abandons the current frame
+when it experiences an underflow, or attempts to continue.
+
+In theory the frame should be black from the point of underflow,
+compared to a shift of sebsequent pixels to the left.
+
+Unfortunately it seems to put the HVS is a bad state where it is not
+possible to recover simply. This typically requires a reboot
+following the 'flip done timed out message'.
+
+Discussion with Broadcom has suggested we don't use this flag.
+All their testing is done with it disabled.
+
+Additionally setting BLANK_INSERT_EN causes the HDMI to output
+blank pixels on an underflow which avoids it losing sync.
+
+After this change a 'flip done timed out' due to sdram bandwidth
+starvation or too low a clock is recoverable once the situation improves.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 1 +
+ drivers/gpu/drm/vc4/vc4_regs.h | 1 +
+ 2 files changed, 2 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1866,6 +1866,7 @@ static void vc4_hdmi_encoder_post_crtc_e
+ VC4_HD_VID_CTL_CLRRGB |
+ VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+ VC4_HD_VID_CTL_FRAME_COUNTER_RESET |
++ VC4_HD_VID_CTL_BLANK_INSERT_EN |
+ (vsync_pos ? 0 : VC4_HD_VID_CTL_VSYNC_LOW) |
+ (hsync_pos ? 0 : VC4_HD_VID_CTL_HSYNC_LOW));
+
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -799,6 +799,7 @@ enum {
+ # define VC4_HD_VID_CTL_CLRSYNC BIT(24)
+ # define VC4_HD_VID_CTL_CLRRGB BIT(23)
+ # define VC4_HD_VID_CTL_BLANKPIX BIT(18)
++# define VC4_HD_VID_CTL_BLANK_INSERT_EN BIT(16)
+
+ # define VC4_HD_CSC_CTL_ORDER_MASK VC4_MASK(7, 5)
+ # define VC4_HD_CSC_CTL_ORDER_SHIFT 5
--- /dev/null
+From 542ba979b4fa1e07ff2ad2dabbdc12e92b80ed46 Mon Sep 17 00:00:00 2001
+From: Tim Gover <tim.gover@raspberrypi.com>
+Date: Thu, 13 Jul 2023 17:47:22 +0100
+Subject: [PATCH] drm/vc4: Enable SCALER_CONTROL early in HVS init
+
+Always enable SCALER_CONTROL before attempting other HVS
+operations. It's safe to write to some parts of the HVS but
+in general it's dangerous to do this because it can cause bus
+lockups.
+
+Signed-off-by: Tim Gover <tim.gover@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1303,6 +1303,10 @@ static int vc4_hvs_hw_init(struct vc4_hv
+ struct vc4_dev *vc4 = hvs->vc4;
+ u32 dispctrl, reg;
+
++ dispctrl = HVS_READ(SCALER_DISPCTRL);
++ dispctrl |= SCALER_DISPCTRL_ENABLE;
++ HVS_WRITE(SCALER_DISPCTRL, dispctrl);
++
+ reg = HVS_READ(SCALER_DISPECTRL);
+ reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK;
+ HVS_WRITE(SCALER_DISPECTRL,
+@@ -1324,8 +1328,6 @@ static int vc4_hvs_hw_init(struct vc4_hv
+ reg | VC4_SET_FIELD(3, SCALER_DISPDITHER_DSP5_MUX));
+
+ dispctrl = HVS_READ(SCALER_DISPCTRL);
+-
+- dispctrl |= SCALER_DISPCTRL_ENABLE;
+ dispctrl |= SCALER_DISPCTRL_DISPEIRQ(0) |
+ SCALER_DISPCTRL_DISPEIRQ(1) |
+ SCALER_DISPCTRL_DISPEIRQ(2);
+@@ -1521,6 +1523,10 @@ static int vc4_hvs_bind(struct device *d
+ else
+ hvs->dlist = hvs->regs + SCALER5_DLIST_START;
+
++ ret = vc4_hvs_hw_init(hvs);
++ if (ret)
++ return ret;
++
+ /* Upload filter kernels. We only have the one for now, so we
+ * keep it around for the lifetime of the driver.
+ */
+@@ -1530,10 +1536,6 @@ static int vc4_hvs_bind(struct device *d
+ if (ret)
+ return ret;
+
+- ret = vc4_hvs_hw_init(hvs);
+- if (ret)
+- return ret;
+-
+ ret = vc4_hvs_cob_init(hvs);
+ if (ret)
+ return ret;
--- /dev/null
+From aed3dadaa6fb4c38275b264ecc0ff5ebe0408b82 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:02 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 HDMI bindings
+
+The BCM2712 HDMI controller uses a slightly different HDMI controller
+than the BCM2711, and a completely different PHY.
+
+Let's introduce a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2711-hdmi.yaml | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2711-hdmi.yaml
+@@ -14,6 +14,8 @@ properties:
+ enum:
+ - brcm,bcm2711-hdmi0
+ - brcm,bcm2711-hdmi1
++ - brcm,bcm2712-hdmi0
++ - brcm,bcm2712-hdmi1
+
+ reg:
+ items:
--- /dev/null
+From 2f5a75f9687553d6e91fcc09b233f5f6176b3681 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:14 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 HVS bindings
+
+The BCM2712 has a completely different HVS than the previous
+generations, so let's add a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-hvs.yaml | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-hvs.yaml
+@@ -13,6 +13,7 @@ properties:
+ compatible:
+ enum:
+ - brcm,bcm2711-hvs
++ - brcm,bcm2712-hvs
+ - brcm,bcm2835-hvs
+
+ reg:
+@@ -36,7 +37,9 @@ if:
+ properties:
+ compatible:
+ contains:
+- const: brcm,bcm2711-hvs
++ enum:
++ - brcm,bcm2711-hvs
++ - brcm,bcm2712-hvs
+
+ then:
+ required:
--- /dev/null
+From 9f26d5827745df2c59e5559fd59a5045a4ad7ce0 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:27 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 PixelValve bindings
+
+The BCM2712 has 3 different pixelvalves that are similar to the ones
+found in the previous generations but with slightly different
+capabilities.
+
+Express that using a new set of compatibles.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-pixelvalve0.yaml
+@@ -20,6 +20,9 @@ properties:
+ - brcm,bcm2711-pixelvalve2
+ - brcm,bcm2711-pixelvalve3
+ - brcm,bcm2711-pixelvalve4
++ - brcm,bcm2712-pixelvalve0
++ - brcm,bcm2712-pixelvalve1
++ - brcm,bcm2712-pixelvalve2
+
+ reg:
+ maxItems: 1
--- /dev/null
+From 07f90ad6a81d9ed923ee0da05541718baf49fb3c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:36 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 MOP bindings
+
+The BCM2712 has a MOP controller which is basically a new revision of
+the TXP.
+
+Express that by adding a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../devicetree/bindings/display/brcm,bcm2835-txp.yaml | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
+@@ -11,7 +11,9 @@ maintainers:
+
+ properties:
+ compatible:
+- const: brcm,bcm2835-txp
++ enum:
++ - brcm,bcm2712-mop
++ - brcm,bcm2835-txp
+
+ reg:
+ maxItems: 1
--- /dev/null
+From e4a3722d08c723f1212bfbdcb52710de8340720a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:36 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 MOPLET bindings
+
+The BCM2712 has a MOPLET controller which is basically a TXP without the
+transpose feature.
+
+Express that by adding a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-txp.yaml
+@@ -13,6 +13,7 @@ properties:
+ compatible:
+ enum:
+ - brcm,bcm2712-mop
++ - brcm,bcm2712-moplet
+ - brcm,bcm2835-txp
+
+ reg:
--- /dev/null
+From e66d4b49a027257c347fa57ce6972f08747c0917 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:36:51 +0100
+Subject: [PATCH] dt-bindings: display: Add BCM2712 KMS driver bindings
+
+The BCM2712 SoC comes with a new variation of the videocore display
+pipeline. Let's create a new compatible for it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
++++ b/Documentation/devicetree/bindings/display/brcm,bcm2835-vc4.yaml
+@@ -18,6 +18,7 @@ properties:
+ compatible:
+ enum:
+ - brcm,bcm2711-vc5
++ - brcm,bcm2712-vc6
+ - brcm,bcm2835-vc4
+ - brcm,cygnus-vc4
+
--- /dev/null
+From 847ec495822ad512dd9f1a58a85dabea01534855 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:29:52 +0100
+Subject: [PATCH] drm/vc4: drv: Support BCM2712
+
+The BCM2712 has an improved display pipeline, most notably with a
+different HVS and only HDMI and writeback outputs.
+
+Let's introduce it as a new VideoCore generation and compatible.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 5 ++++-
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -310,7 +310,9 @@ static int vc4_drm_bind(struct device *d
+
+ dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+- if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
++ if (of_device_is_compatible(dev->of_node, "brcm,bcm2712-vc6"))
++ gen = VC4_GEN_6;
++ else if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
+ gen = VC4_GEN_5;
+ else
+ gen = VC4_GEN_4;
+@@ -475,6 +477,7 @@ static int vc4_platform_drm_remove(struc
+
+ static const struct of_device_id vc4_of_match[] = {
+ { .compatible = "brcm,bcm2711-vc5", },
++ { .compatible = "brcm,bcm2712-vc6", },
+ { .compatible = "brcm,bcm2835-vc4", },
+ { .compatible = "brcm,cygnus-vc4", },
+ {},
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -84,6 +84,7 @@ struct vc4_perfmon {
+ enum vc4_gen {
+ VC4_GEN_4,
+ VC4_GEN_5,
++ VC4_GEN_6,
+ };
+
+ struct vc4_dev {
--- /dev/null
+From e84da235223d0209165183c430692dde5c69854c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:25:16 +0100
+Subject: [PATCH] drm/vc4: hvs: Support BCM2712 HVS
+
+The HVS found in the BCM2712, while having a similar role, is very
+different from the one found in the previous SoCs. Indeed, the register
+layout is fairly different, and the DLIST format is new as well.
+
+Let's introduce the needed functions to support the new HVS.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 47 ++-
+ drivers/gpu/drm/vc4/vc4_drv.c | 8 +-
+ drivers/gpu/drm/vc4/vc4_drv.h | 18 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 626 ++++++++++++++++++++++++++++---
+ drivers/gpu/drm/vc4/vc4_kms.c | 102 ++++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 641 +++++++++++++++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc4_regs.h | 181 +++++++++
+ 7 files changed, 1540 insertions(+), 83 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -82,13 +82,22 @@ static unsigned int
+ vc4_crtc_get_cob_allocation(struct vc4_dev *vc4, unsigned int channel)
+ {
+ struct vc4_hvs *hvs = vc4->hvs;
+- u32 dispbase = HVS_READ(SCALER_DISPBASEX(channel));
++ u32 dispbase, top, base;
++
+ /* Top/base are supposed to be 4-pixel aligned, but the
+ * Raspberry Pi firmware fills the low bits (which are
+ * presumably ignored).
+ */
+- u32 top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
+- u32 base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
++
++ if (vc4->gen >= VC4_GEN_6) {
++ dispbase = HVS_READ(SCALER6_DISPX_COB(channel));
++ top = VC4_GET_FIELD(dispbase, SCALER6_DISPX_COB_TOP) & ~3;
++ base = VC4_GET_FIELD(dispbase, SCALER6_DISPX_COB_BASE) & ~3;
++ } else {
++ dispbase = HVS_READ(SCALER_DISPBASEX(channel));
++ top = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_TOP) & ~3;
++ base = VC4_GET_FIELD(dispbase, SCALER_DISPBASEX_BASE) & ~3;
++ }
+
+ return top - base + 4;
+ }
+@@ -121,7 +130,10 @@ static bool vc4_crtc_get_scanout_positio
+ * Read vertical scanline which is currently composed for our
+ * pixelvalve by the HVS, and also the scaler status.
+ */
+- val = HVS_READ(SCALER_DISPSTATX(channel));
++ if (vc4->gen >= VC4_GEN_6)
++ val = HVS_READ(SCALER6_DISPX_STATUS(channel));
++ else
++ val = HVS_READ(SCALER_DISPSTATX(channel));
+
+ /* Get optional system timestamp after query. */
+ if (etime)
+@@ -130,7 +142,12 @@ static bool vc4_crtc_get_scanout_positio
+ /* preempt_enable_rt() should go right here in PREEMPT_RT patchset. */
+
+ /* Vertical position of hvs composed scanline. */
+- *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
++
++ if (vc4->gen >= VC4_GEN_6)
++ *vpos = VC4_GET_FIELD(val, SCALER6_DISPX_STATUS_YLINE);
++ else
++ *vpos = VC4_GET_FIELD(val, SCALER_DISPSTATX_LINE);
++
+ *hpos = 0;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+@@ -475,8 +492,10 @@ static void require_hvs_enabled(struct d
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hvs *hvs = vc4->hvs;
+
+- WARN_ON_ONCE((HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE) !=
+- SCALER_DISPCTRL_ENABLE);
++ if (vc4->gen >= VC4_GEN_6)
++ WARN_ON_ONCE(!(HVS_READ(SCALER6_CONTROL) & SCALER6_CONTROL_HVS_EN));
++ else
++ WARN_ON_ONCE(!(HVS_READ(SCALER_DISPCTRL) & SCALER_DISPCTRL_ENABLE));
+ }
+
+ static int vc4_crtc_disable(struct drm_crtc *crtc,
+@@ -804,14 +823,21 @@ static void vc4_crtc_handle_page_flip(st
+ struct drm_device *dev = crtc->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hvs *hvs = vc4->hvs;
++ unsigned int current_dlist;
+ u32 chan = vc4_crtc->current_hvs_channel;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ spin_lock(&vc4_crtc->irq_lock);
++
++ if (vc4->gen >= VC4_GEN_6)
++ current_dlist = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_DL(chan)),
++ SCALER6_DISPX_DL_LACT);
++ else
++ current_dlist = HVS_READ(SCALER_DISPLACTX(chan));
++
+ if (vc4_crtc->event &&
+- (vc4_crtc->current_dlist == HVS_READ(SCALER_DISPLACTX(chan)) ||
+- vc4_crtc->feeds_txp)) {
++ (vc4_crtc->current_dlist == current_dlist || vc4_crtc->feeds_txp)) {
+ drm_crtc_send_vblank_event(crtc, vc4_crtc->event);
+ vc4_crtc->event = NULL;
+ drm_crtc_vblank_put(crtc);
+@@ -822,7 +848,8 @@ static void vc4_crtc_handle_page_flip(st
+ * the CRTC and encoder already reconfigured, leading to
+ * underruns. This can be seen when reconfiguring the CRTC.
+ */
+- vc4_hvs_unmask_underrun(hvs, chan);
++ if (vc4->gen < VC4_GEN_6)
++ vc4_hvs_unmask_underrun(hvs, chan);
+ }
+ spin_unlock(&vc4_crtc->irq_lock);
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -277,6 +277,7 @@ static const struct of_device_id vc4_dma
+ { .compatible = "brcm,bcm2711-hvs" },
+ { .compatible = "brcm,bcm2835-hvs" },
+ { .compatible = "brcm,bcm2711-hvs" },
++ { .compatible = "brcm,bcm2712-hvs" },
+ { .compatible = "raspberrypi,rpi-firmware-kms" },
+ { .compatible = "brcm,bcm2835-v3d" },
+ { .compatible = "brcm,cygnus-v3d" },
+@@ -308,8 +309,6 @@ static int vc4_drm_bind(struct device *d
+ enum vc4_gen gen;
+ int ret = 0;
+
+- dev->coherent_dma_mask = DMA_BIT_MASK(32);
+-
+ if (of_device_is_compatible(dev->of_node, "brcm,bcm2712-vc6"))
+ gen = VC4_GEN_6;
+ else if (of_device_is_compatible(dev->of_node, "brcm,bcm2711-vc5"))
+@@ -322,6 +321,11 @@ static int vc4_drm_bind(struct device *d
+ else
+ driver = &vc4_drm_driver;
+
++ if (gen >= VC4_GEN_6)
++ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36));
++ else
++ dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
++
+ node = of_find_matching_node_and_match(NULL, vc4_dma_range_matches,
+ NULL);
+ if (node) {
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -345,8 +345,10 @@ struct vc4_hvs {
+ unsigned int dlist_mem_size;
+
+ struct clk *core_clk;
++ struct clk *disp_clk;
+
+ struct {
++ unsigned int desc;
+ unsigned int enabled: 1;
+ } eof_irq[HVS_NUM_CHANNELS];
+
+@@ -358,6 +360,11 @@ struct vc4_hvs {
+ struct drm_mm dlist_mm;
+ /* Memory manager for the LBM memory used by HVS scaling. */
+ struct drm_mm lbm_mm;
++
++ /* Memory manager for the UPM memory used for prefetching. */
++ struct drm_mm upm_mm;
++ struct ida upm_handles;
++
+ spinlock_t mm_lock;
+
+ struct list_head stale_dlist_entries;
+@@ -382,6 +389,8 @@ struct vc4_hvs {
+ bool vc5_hdmi_enable_4096by2160;
+ };
+
++#define HVS_UBM_WORD_SIZE 256
++
+ struct vc4_hvs_state {
+ struct drm_private_state base;
+ unsigned long core_clock_rate;
+@@ -456,6 +465,15 @@ struct vc4_plane_state {
+ /* Our allocation in LBM for temporary storage during scaling. */
+ struct drm_mm_node lbm;
+
++ /* Our allocation in UPM for prefetching. */
++ struct drm_mm_node upm[DRM_FORMAT_MAX_PLANES];
++
++ /* The Unified Pre-Fetcher Handle */
++ unsigned int upm_handle[DRM_FORMAT_MAX_PLANES];
++
++ /* Number of lines to pre-fetch */
++ unsigned int upm_buffer_lines;
++
+ /* Set when the plane has per-pixel alpha content or does not cover
+ * the entire screen. This is a hint to the CRTC that it might need
+ * to enable background color fill.
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -67,6 +67,80 @@ static const struct debugfs_reg32 vc4_hv
+ VC4_REG32(SCALER_OLEDCOEF2),
+ };
+
++static const struct debugfs_reg32 vc6_hvs_regs[] = {
++ VC4_REG32(SCALER6_VERSION),
++ VC4_REG32(SCALER6_CXM_SIZE),
++ VC4_REG32(SCALER6_LBM_SIZE),
++ VC4_REG32(SCALER6_UBM_SIZE),
++ VC4_REG32(SCALER6_COBA_SIZE),
++ VC4_REG32(SCALER6_COB_SIZE),
++ VC4_REG32(SCALER6_CONTROL),
++ VC4_REG32(SCALER6_FETCHER_STATUS),
++ VC4_REG32(SCALER6_FETCH_STATUS),
++ VC4_REG32(SCALER6_HANDLE_ERROR),
++ VC4_REG32(SCALER6_DISP0_CTRL0),
++ VC4_REG32(SCALER6_DISP0_CTRL1),
++ VC4_REG32(SCALER6_DISP0_BGND),
++ VC4_REG32(SCALER6_DISP0_LPTRS),
++ VC4_REG32(SCALER6_DISP0_COB),
++ VC4_REG32(SCALER6_DISP0_STATUS),
++ VC4_REG32(SCALER6_DISP0_DL),
++ VC4_REG32(SCALER6_DISP0_RUN),
++ VC4_REG32(SCALER6_DISP1_CTRL0),
++ VC4_REG32(SCALER6_DISP1_CTRL1),
++ VC4_REG32(SCALER6_DISP1_BGND),
++ VC4_REG32(SCALER6_DISP1_LPTRS),
++ VC4_REG32(SCALER6_DISP1_COB),
++ VC4_REG32(SCALER6_DISP1_STATUS),
++ VC4_REG32(SCALER6_DISP1_DL),
++ VC4_REG32(SCALER6_DISP1_RUN),
++ VC4_REG32(SCALER6_DISP2_CTRL0),
++ VC4_REG32(SCALER6_DISP2_CTRL1),
++ VC4_REG32(SCALER6_DISP2_BGND),
++ VC4_REG32(SCALER6_DISP2_LPTRS),
++ VC4_REG32(SCALER6_DISP2_COB),
++ VC4_REG32(SCALER6_DISP2_STATUS),
++ VC4_REG32(SCALER6_DISP2_DL),
++ VC4_REG32(SCALER6_DISP2_RUN),
++ VC4_REG32(SCALER6_EOLN),
++ VC4_REG32(SCALER6_DL_STATUS),
++ VC4_REG32(SCALER6_BFG_MISC),
++ VC4_REG32(SCALER6_QOS0),
++ VC4_REG32(SCALER6_PROF0),
++ VC4_REG32(SCALER6_QOS1),
++ VC4_REG32(SCALER6_PROF1),
++ VC4_REG32(SCALER6_QOS2),
++ VC4_REG32(SCALER6_PROF2),
++ VC4_REG32(SCALER6_PRI_MAP0),
++ VC4_REG32(SCALER6_PRI_MAP1),
++ VC4_REG32(SCALER6_HISTCTRL),
++ VC4_REG32(SCALER6_HISTBIN0),
++ VC4_REG32(SCALER6_HISTBIN1),
++ VC4_REG32(SCALER6_HISTBIN2),
++ VC4_REG32(SCALER6_HISTBIN3),
++ VC4_REG32(SCALER6_HISTBIN4),
++ VC4_REG32(SCALER6_HISTBIN5),
++ VC4_REG32(SCALER6_HISTBIN6),
++ VC4_REG32(SCALER6_HISTBIN7),
++ VC4_REG32(SCALER6_HDR_CFG_REMAP),
++ VC4_REG32(SCALER6_COL_SPACE),
++ VC4_REG32(SCALER6_HVS_ID),
++ VC4_REG32(SCALER6_CFC1),
++ VC4_REG32(SCALER6_DISP_UPM_ISO0),
++ VC4_REG32(SCALER6_DISP_UPM_ISO1),
++ VC4_REG32(SCALER6_DISP_UPM_ISO2),
++ VC4_REG32(SCALER6_DISP_LBM_ISO0),
++ VC4_REG32(SCALER6_DISP_LBM_ISO1),
++ VC4_REG32(SCALER6_DISP_LBM_ISO2),
++ VC4_REG32(SCALER6_DISP_COB_ISO0),
++ VC4_REG32(SCALER6_DISP_COB_ISO1),
++ VC4_REG32(SCALER6_DISP_COB_ISO2),
++ VC4_REG32(SCALER6_BAD_COB),
++ VC4_REG32(SCALER6_BAD_LBM),
++ VC4_REG32(SCALER6_BAD_UPM),
++ VC4_REG32(SCALER6_BAD_AXI),
++};
++
+ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
+ {
+ struct drm_device *drm = &hvs->vc4->base;
+@@ -145,6 +219,55 @@ static int vc4_hvs_debugfs_dlist(struct
+ return 0;
+ }
+
++static int vc6_hvs_debugfs_dlist(struct seq_file *m, void *data)
++{
++ struct drm_info_node *node = m->private;
++ struct drm_device *dev = node->minor->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hvs *hvs = vc4->hvs;
++ struct drm_printer p = drm_seq_file_printer(m);
++ unsigned int dlist_mem_size = hvs->dlist_mem_size;
++ unsigned int next_entry_start;
++ unsigned int i;
++
++ for (i = 0; i < SCALER_CHANNELS_COUNT; i++) {
++ unsigned int active_dlist, dispstat;
++ unsigned int j;
++
++ dispstat = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(i)),
++ SCALER6_DISPX_STATUS_MODE);
++ if (dispstat == SCALER6_DISPX_STATUS_MODE_DISABLED ||
++ dispstat == SCALER6_DISPX_STATUS_MODE_EOF) {
++ drm_printf(&p, "HVS chan %u disabled\n", i);
++ continue;
++ }
++
++ drm_printf(&p, "HVS chan %u:\n", i);
++
++ active_dlist = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_DL(i)),
++ SCALER6_DISPX_DL_LACT);
++ next_entry_start = 0;
++
++ for (j = active_dlist; j < dlist_mem_size; j++) {
++ u32 dlist_word;
++
++ dlist_word = readl((u32 __iomem *)vc4->hvs->dlist + j);
++ drm_printf(&p, "dlist: %02d: 0x%08x\n", j,
++ dlist_word);
++ if (!next_entry_start ||
++ next_entry_start == j) {
++ if (dlist_word & SCALER_CTL0_END)
++ break;
++ next_entry_start = j +
++ VC4_GET_FIELD(dlist_word,
++ SCALER_CTL0_SIZE);
++ }
++ }
++ }
++
++ return 0;
++}
++
+ static int vc5_hvs_debugfs_gamma(struct seq_file *m, void *data)
+ {
+ struct drm_info_node *node = m->private;
+@@ -435,6 +558,10 @@ static void vc4_hvs_irq_enable_eof(struc
+ SCALER5_DISPCTRL_DSPEIEOF(channel));
+ break;
+
++ case VC4_GEN_6:
++ enable_irq(hvs->eof_irq[channel].desc);
++ break;
++
+ default:
+ break;
+ }
+@@ -463,6 +590,10 @@ static void vc4_hvs_irq_clear_eof(struct
+ ~SCALER5_DISPCTRL_DSPEIEOF(channel));
+ break;
+
++ case VC4_GEN_6:
++ disable_irq_nosync(hvs->eof_irq[channel].desc);
++ break;
++
+ default:
+ break;
+ }
+@@ -622,26 +753,32 @@ static void vc4_hvs_dlist_free_work(stru
+
+ u8 vc4_hvs_get_fifo_frame_count(struct vc4_hvs *hvs, unsigned int fifo)
+ {
+- struct drm_device *drm = &hvs->vc4->base;
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
+ u8 field = 0;
+ int idx;
+
+ if (!drm_dev_enter(drm, &idx))
+ return 0;
+
+- switch (fifo) {
+- case 0:
+- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
+- SCALER_DISPSTAT1_FRCNT0);
+- break;
+- case 1:
+- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
+- SCALER_DISPSTAT1_FRCNT1);
+- break;
+- case 2:
+- field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2),
+- SCALER_DISPSTAT2_FRCNT2);
+- break;
++ if (vc4->gen >= VC4_GEN_6) {
++ field = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(fifo)),
++ SCALER6_DISPX_STATUS_FRCNT);
++ } else {
++ switch (fifo) {
++ case 0:
++ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++ SCALER_DISPSTAT1_FRCNT0);
++ break;
++ case 1:
++ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++ SCALER_DISPSTAT1_FRCNT1);
++ break;
++ case 2:
++ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2),
++ SCALER_DISPSTAT2_FRCNT2);
++ break;
++ }
+ }
+
+ drm_dev_exit(idx);
+@@ -708,6 +845,23 @@ int vc4_hvs_get_fifo_from_output(struct
+ default:
+ return -EPIPE;
+ }
++
++ case VC4_GEN_6:
++ switch (output) {
++ case 0:
++ return 0;
++
++ case 2:
++ return 2;
++
++ case 1:
++ case 3:
++ case 4:
++ return 1;
++
++ default:
++ return -EPIPE;
++ }
+ }
+
+ return -EPIPE;
+@@ -782,7 +936,41 @@ static int vc4_hvs_init_channel(struct v
+ return 0;
+ }
+
+-void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
++static int vc6_hvs_init_channel(struct vc4_hvs *hvs, struct drm_crtc *crtc,
++ struct drm_display_mode *mode, bool oneshot)
++{
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
++ struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state);
++ unsigned int chan = vc4_crtc_state->assigned_channel;
++ bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
++ u32 disp_ctrl1;
++ int idx;
++
++ if (!drm_dev_enter(drm, &idx))
++ return -ENODEV;
++
++ HVS_WRITE(SCALER6_DISPX_CTRL0(chan), SCALER6_DISPX_CTRL0_RESET);
++
++ disp_ctrl1 = HVS_READ(SCALER6_DISPX_CTRL1(chan));
++ disp_ctrl1 &= ~SCALER6_DISPX_CTRL1_INTLACE;
++ HVS_WRITE(SCALER6_DISPX_CTRL1(chan),
++ disp_ctrl1 | (interlace ? SCALER6_DISPX_CTRL1_INTLACE : 0));
++
++ HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
++ SCALER6_DISPX_CTRL0_ENB |
++ VC4_SET_FIELD(mode->hdisplay - 1,
++ SCALER6_DISPX_CTRL0_FWIDTH) |
++ (oneshot ? SCALER6_DISPX_CTRL0_ONESHOT : 0) |
++ VC4_SET_FIELD(mode->vdisplay - 1,
++ SCALER6_DISPX_CTRL0_LINES));
++
++ drm_dev_exit(idx);
++
++ return 0;
++}
++
++static void __vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
+ {
+ struct drm_device *drm = &hvs->vc4->base;
+ int idx;
+@@ -813,6 +1001,42 @@ out:
+ drm_dev_exit(idx);
+ }
+
++static void __vc6_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
++{
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
++ int idx;
++
++ if (!drm_dev_enter(drm, &idx))
++ return;
++
++ if (HVS_READ(SCALER6_DISPX_CTRL0(chan)) & SCALER6_DISPX_CTRL0_ENB)
++ goto out;
++
++ HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
++ HVS_READ(SCALER6_DISPX_CTRL0(chan)) | SCALER6_DISPX_CTRL0_RESET);
++
++ HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
++ HVS_READ(SCALER6_DISPX_CTRL0(chan)) & ~SCALER6_DISPX_CTRL0_ENB);
++
++ WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(chan)),
++ SCALER6_DISPX_STATUS_MODE) !=
++ SCALER6_DISPX_STATUS_MODE_DISABLED);
++
++out:
++ drm_dev_exit(idx);
++}
++
++void vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
++{
++ struct vc4_dev *vc4 = hvs->vc4;
++
++ if (vc4->gen >= VC4_GEN_6)
++ __vc6_hvs_stop_channel(hvs, chan);
++ else
++ __vc4_hvs_stop_channel(hvs, chan);
++}
++
+ static int vc4_hvs_gamma_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *state)
+ {
+@@ -907,8 +1131,14 @@ static void vc4_hvs_install_dlist(struct
+ return;
+
+ WARN_ON(!vc4_state->mm);
+- HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
+- vc4_state->mm->mm_node.start);
++
++ if (vc4->gen >= VC4_GEN_6)
++ HVS_WRITE(SCALER6_DISPX_LPTRS(vc4_state->assigned_channel),
++ VC4_SET_FIELD(vc4_state->mm->mm_node.start,
++ SCALER6_DISPX_LPTRS_HEADE));
++ else
++ HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel),
++ vc4_state->mm->mm_node.start);
+
+ drm_dev_exit(idx);
+ }
+@@ -965,7 +1195,11 @@ void vc4_hvs_atomic_enable(struct drm_cr
+
+ vc4_hvs_install_dlist(crtc);
+ vc4_hvs_update_dlist(crtc);
+- vc4_hvs_init_channel(vc4->hvs, crtc, mode, oneshot);
++
++ if (vc4->gen >= VC4_GEN_6)
++ vc6_hvs_init_channel(vc4->hvs, crtc, mode, oneshot);
++ else
++ vc4_hvs_init_channel(vc4->hvs, crtc, mode, oneshot);
+ }
+
+ void vc4_hvs_atomic_disable(struct drm_crtc *crtc,
+@@ -1052,13 +1286,28 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ WARN_ON(!vc4_state->mm);
+ WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm->mm_node.size);
+
+- if (enable_bg_fill)
++ if (enable_bg_fill) {
+ /* This sets a black background color fill, as is the case
+ * with other DRM drivers.
+ */
+- HVS_WRITE(SCALER_DISPBKGNDX(channel),
+- HVS_READ(SCALER_DISPBKGNDX(channel)) |
+- SCALER_DISPBKGND_FILL);
++ if (vc4->gen >= VC4_GEN_6)
++ HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
++ HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
++ SCALER6_DISPX_CTRL1_BGENB);
++ else
++ HVS_WRITE(SCALER_DISPBKGNDX(channel),
++ HVS_READ(SCALER_DISPBKGNDX(channel)) |
++ SCALER_DISPBKGND_FILL);
++ } else {
++ if (vc4->gen >= VC4_GEN_6)
++ HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
++ HVS_READ(SCALER6_DISPX_CTRL1(channel)) &
++ ~SCALER6_DISPX_CTRL1_BGENB);
++ else
++ HVS_WRITE(SCALER_DISPBKGNDX(channel),
++ HVS_READ(SCALER_DISPBKGNDX(channel)) &
++ ~SCALER_DISPBKGND_FILL);
++ }
+
+ /* Only update DISPLIST if the CRTC was already running and is not
+ * being disabled.
+@@ -1210,6 +1459,27 @@ static irqreturn_t vc4_hvs_irq_handler(i
+ return irqret;
+ }
+
++static irqreturn_t vc6_hvs_eof_irq_handler(int irq, void *data)
++{
++ struct drm_device *dev = data;
++ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hvs *hvs = vc4->hvs;
++ unsigned int i;
++
++ for (i = 0; i < HVS_NUM_CHANNELS; i++) {
++ if (!hvs->eof_irq[i].enabled)
++ continue;
++
++ if (hvs->eof_irq[i].desc != irq)
++ continue;
++
++ vc4_hvs_schedule_dlist_sweep(hvs, i);
++ return IRQ_HANDLED;
++ }
++
++ return IRQ_NONE;
++}
++
+ int vc4_hvs_debugfs_init(struct drm_minor *minor)
+ {
+ struct drm_device *drm = minor->dev;
+@@ -1232,8 +1502,10 @@ int vc4_hvs_debugfs_init(struct drm_mino
+ NULL);
+ }
+
+- ret = vc4_debugfs_add_file(minor, "hvs_dlists",
+- vc4_hvs_debugfs_dlist, NULL);
++ if (vc4->gen >= VC4_GEN_6)
++ ret = vc4_debugfs_add_file(minor, "hvs_dlists", vc6_hvs_debugfs_dlist, NULL);
++ else
++ ret = vc4_debugfs_add_file(minor, "hvs_dlists", vc4_hvs_debugfs_dlist, NULL);
+ if (ret)
+ return ret;
+
+@@ -1256,6 +1528,9 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ {
+ struct drm_device *drm = &vc4->base;
+ struct vc4_hvs *hvs;
++ unsigned int dlist_start;
++ size_t dlist_size;
++ size_t lbm_size;
+
+ hvs = drmm_kzalloc(drm, sizeof(*hvs), GFP_KERNEL);
+ if (!hvs)
+@@ -1270,14 +1545,39 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ INIT_LIST_HEAD(&hvs->stale_dlist_entries);
+ INIT_WORK(&hvs->free_dlist_work, vc4_hvs_dlist_free_work);
+
+- /* Set up the HVS display list memory manager. We never
+- * overwrite the setup from the bootloader (just 128b out of
+- * our 16K), since we don't want to scramble the screen when
+- * transitioning from the firmware's boot setup to runtime.
+- */
+- drm_mm_init(&hvs->dlist_mm,
+- HVS_BOOTLOADER_DLIST_END,
+- (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END);
++ switch (vc4->gen) {
++ case VC4_GEN_4:
++ case VC4_GEN_5:
++ /* Set up the HVS display list memory manager. We never
++ * overwrite the setup from the bootloader (just 128b
++ * out of our 16K), since we don't want to scramble the
++ * screen when transitioning from the firmware's boot
++ * setup to runtime.
++ */
++ dlist_start = HVS_BOOTLOADER_DLIST_END;
++ dlist_size = (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END;
++ break;
++
++ case VC4_GEN_6:
++ dlist_start = HVS_BOOTLOADER_DLIST_END;
++
++ /*
++ * If we are running a test, it means that we can't
++ * access a register. Use a plausible size then.
++ */
++ if (!kunit_get_current_test())
++ dlist_size = HVS_READ(SCALER6_CXM_SIZE);
++ else
++ dlist_size = 4096;
++
++ break;
++
++ default:
++ drm_err(drm, "Unknown VC4 generation: %d", vc4->gen);
++ return ERR_PTR(-ENODEV);
++ }
++
++ drm_mm_init(&hvs->dlist_mm, dlist_start, dlist_size);
+
+ hvs->dlist_mem_size = dlist_size;
+
+@@ -1286,12 +1586,46 @@ struct vc4_hvs *__vc4_hvs_alloc(struct v
+ * between planes when they don't overlap on the screen, but
+ * for now we just allocate globally.
+ */
+- if (vc4->gen == VC4_GEN_4)
++
++ switch (vc4->gen) {
++ case VC4_GEN_4:
+ /* 48k words of 2x12-bit pixels */
+- drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024);
+- else
++ lbm_size = 48 * SZ_1K;
++ break;
++
++ case VC4_GEN_5:
+ /* 60k words of 4x12-bit pixels */
+- drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024);
++ lbm_size = 60 * SZ_1K;
++ break;
++
++ case VC4_GEN_6:
++ /*
++ * If we are running a test, it means that we can't
++ * access a register. Use a plausible size then.
++ */
++ lbm_size = 1024;
++ break;
++
++ default:
++ drm_err(drm, "Unknown VC4 generation: %d", vc4->gen);
++ return ERR_PTR(-ENODEV);
++ }
++
++ drm_mm_init(&hvs->lbm_mm, 0, lbm_size);
++
++ if (vc4->gen >= VC4_GEN_6) {
++ ida_init(&hvs->upm_handles);
++
++ /*
++ * NOTE: On BCM2712, the size can also be read through
++ * the SCALER_UBM_SIZE register. We would need to do a
++ * register access though, which we can't do with kunit
++ * that also uses this function to create its mock
++ * device.
++ */
++ drm_mm_init(&hvs->upm_mm, 0, 1024 * HVS_UBM_WORD_SIZE);
++ }
++
+
+ vc4->hvs = hvs;
+
+@@ -1388,10 +1722,124 @@ static int vc4_hvs_hw_init(struct vc4_hv
+ return 0;
+ }
+
++#define CFC1_N_NL_CSC_CTRL(x) (0xa000 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C00(x) (0xa008 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C01(x) (0xa00c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C02(x) (0xa010 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C03(x) (0xa014 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C04(x) (0xa018 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C10(x) (0xa01c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C11(x) (0xa020 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C12(x) (0xa024 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C13(x) (0xa028 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C14(x) (0xa02c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C20(x) (0xa030 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C21(x) (0xa034 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C22(x) (0xa038 + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C23(x) (0xa03c + ((x) * 0x3000))
++#define CFC1_N_MA_CSC_COEFF_C24(x) (0xa040 + ((x) * 0x3000))
++
++/* 4 S2.22 multiplication factors, and 1 S9.15 addititive element for each of 3
++ * output components
++ */
++struct vc6_csc_coeff_entry {
++ u32 csc[3][5];
++};
++
++static const struct vc6_csc_coeff_entry csc_coeffs[2][3] = {
++ [DRM_COLOR_YCBCR_LIMITED_RANGE] = {
++ [DRM_COLOR_YCBCR_BT601] = {
++ .csc = {
++ { 0x004A8542, 0x0, 0x0066254A, 0x0, 0xFF908A0D },
++ { 0x004A8542, 0xFFE6ED5D, 0xFFCBF856, 0x0, 0x0043C9A3 },
++ { 0x004A8542, 0x00811A54, 0x0, 0x0, 0xFF759502 }
++ }
++ },
++ [DRM_COLOR_YCBCR_BT709] = {
++ .csc = {
++ { 0x004A8542, 0x0, 0x0072BC44, 0x0, 0xFF83F312 },
++ { 0x004A8542, 0xFFF25A22, 0xFFDDE4D0, 0x0, 0x00267064 },
++ { 0x004A8542, 0x00873197, 0x0, 0x0, 0xFF6F7DC0 }
++ }
++ },
++ [DRM_COLOR_YCBCR_BT2020] = {
++ .csc = {
++ { 0x004A8542, 0x0, 0x006B4A17, 0x0, 0xFF8B653F },
++ { 0x004A8542, 0xFFF402D9, 0xFFDDE4D0, 0x0, 0x0024C7AE },
++ { 0x004A8542, 0x008912CC, 0x0, 0x0, 0xFF6D9C8B }
++ }
++ }
++ },
++ [DRM_COLOR_YCBCR_FULL_RANGE] = {
++ [DRM_COLOR_YCBCR_BT601] = {
++ .csc = {
++ { 0x00400000, 0x0, 0x0059BA5E, 0x0, 0xFFA645A1 },
++ { 0x00400000, 0xFFE9F9AC, 0xFFD24B97, 0x0, 0x0043BABB },
++ { 0x00400000, 0x00716872, 0x0, 0x0, 0xFF8E978D }
++ }
++ },
++ [DRM_COLOR_YCBCR_BT709] = {
++ .csc = {
++ { 0x00400000, 0x0, 0x0064C985, 0x0, 0xFF9B367A },
++ { 0x00400000, 0xFFF402E1, 0xFFE20A40, 0x0, 0x0029F2DE },
++ { 0x00400000, 0x0076C226, 0x0, 0x0, 0xFF893DD9 }
++ }
++ },
++ [DRM_COLOR_YCBCR_BT2020] = {
++ .csc = {
++ { 0x00400000, 0x0, 0x005E3F14, 0x0, 0xFFA1C0EB },
++ { 0x00400000, 0xFFF577F6, 0xFFDB580F, 0x0, 0x002F2FFA },
++ { 0x00400000, 0x007868DB, 0x0, 0x0, 0xFF879724 }
++ }
++ }
++ }
++};
++
++static int vc6_hvs_hw_init(struct vc4_hvs *hvs)
++{
++ const struct vc6_csc_coeff_entry *coeffs;
++ unsigned int i;
++
++ HVS_WRITE(SCALER6_CONTROL,
++ SCALER6_CONTROL_HVS_EN |
++ VC4_SET_FIELD(8, SCALER6_CONTROL_PF_LINES) |
++ VC4_SET_FIELD(15, SCALER6_CONTROL_MAX_REQS));
++
++ /* Set HVS arbiter priority to max */
++ HVS_WRITE(SCALER6_PRI_MAP0, 0xffffffff);
++ HVS_WRITE(SCALER6_PRI_MAP1, 0xffffffff);
++
++ for (i = 0; i < 6; i++) {
++ coeffs = &csc_coeffs[i / 3][i % 3];
++
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C00(i), coeffs->csc[0][0]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C01(i), coeffs->csc[0][1]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C02(i), coeffs->csc[0][2]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C03(i), coeffs->csc[0][3]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C04(i), coeffs->csc[0][4]);
++
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C10(i), coeffs->csc[1][0]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C11(i), coeffs->csc[1][1]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C12(i), coeffs->csc[1][2]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C13(i), coeffs->csc[1][3]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C14(i), coeffs->csc[1][4]);
++
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C20(i), coeffs->csc[2][0]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C21(i), coeffs->csc[2][1]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C22(i), coeffs->csc[2][2]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C23(i), coeffs->csc[2][3]);
++ HVS_WRITE(CFC1_N_MA_CSC_COEFF_C24(i), coeffs->csc[2][4]);
++
++ HVS_WRITE(CFC1_N_NL_CSC_CTRL(i), BIT(15));
++ }
++
++ return 0;
++}
++
+ static int vc4_hvs_cob_init(struct vc4_hvs *hvs)
+ {
+ struct vc4_dev *vc4 = hvs->vc4;
+- u32 reg, top;
++ u32 reg, top, base;
+
+ /*
+ * Recompute Composite Output Buffer (COB) allocations for the
+@@ -1452,6 +1900,31 @@ static int vc4_hvs_cob_init(struct vc4_h
+ HVS_WRITE(SCALER_DISPBASE0, reg);
+ break;
+
++ case VC4_GEN_6:
++ #define VC6_COB_LINE_WIDTH 3840
++ #define VC6_COB_NUM_LINES 4
++ reg = 0;
++ top = 3840;
++
++ HVS_WRITE(SCALER6_DISP2_COB,
++ VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
++ VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
++
++ base = top + 16;
++ top += VC6_COB_LINE_WIDTH * VC6_COB_NUM_LINES;
++
++ HVS_WRITE(SCALER6_DISP1_COB,
++ VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
++ VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
++
++ base = top + 16;
++ top += VC6_COB_LINE_WIDTH * VC6_COB_NUM_LINES;
++
++ HVS_WRITE(SCALER6_DISP0_COB,
++ VC4_SET_FIELD(top, SCALER6_DISPX_COB_TOP) |
++ VC4_SET_FIELD(base, SCALER6_DISPX_COB_BASE));
++ break;
++
+ default:
+ return -EINVAL;
+ }
+@@ -1477,10 +1950,16 @@ static int vc4_hvs_bind(struct device *d
+ return PTR_ERR(hvs);
+
+ hvs->regset.base = hvs->regs;
+- hvs->regset.regs = vc4_hvs_regs;
+- hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
+
+- if (vc4->gen == VC4_GEN_5) {
++ if (vc4->gen >= VC4_GEN_6) {
++ hvs->regset.regs = vc6_hvs_regs;
++ hvs->regset.nregs = ARRAY_SIZE(vc6_hvs_regs);
++ } else {
++ hvs->regset.regs = vc4_hvs_regs;
++ hvs->regset.nregs = ARRAY_SIZE(vc4_hvs_regs);
++ }
++
++ if (vc4->gen >= VC4_GEN_5) {
+ struct rpi_firmware *firmware;
+ struct device_node *node;
+ unsigned int max_rate;
+@@ -1494,12 +1973,20 @@ static int vc4_hvs_bind(struct device *d
+ if (!firmware)
+ return -EPROBE_DEFER;
+
+- hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
++ hvs->core_clk = devm_clk_get(&pdev->dev,
++ (vc4->gen >= VC4_GEN_6) ? "core" : NULL);
+ if (IS_ERR(hvs->core_clk)) {
+ dev_err(&pdev->dev, "Couldn't get core clock\n");
+ return PTR_ERR(hvs->core_clk);
+ }
+
++ hvs->disp_clk = devm_clk_get(&pdev->dev,
++ (vc4->gen >= VC4_GEN_6) ? "disp" : NULL);
++ if (IS_ERR(hvs->disp_clk)) {
++ dev_err(&pdev->dev, "Couldn't get disp clock\n");
++ return PTR_ERR(hvs->disp_clk);
++ }
++
+ max_rate = rpi_firmware_clk_get_max_rate(firmware,
+ RPI_FIRMWARE_CORE_CLK_ID);
+ rpi_firmware_put(firmware);
+@@ -1516,14 +2003,51 @@ static int vc4_hvs_bind(struct device *d
+ dev_err(&pdev->dev, "Couldn't enable the core clock\n");
+ return ret;
+ }
++
++ ret = clk_prepare_enable(hvs->disp_clk);
++ if (ret) {
++ dev_err(&pdev->dev, "Couldn't enable the disp clock\n");
++ return ret;
++ }
+ }
+
+- if (vc4->gen == VC4_GEN_4)
+- hvs->dlist = hvs->regs + SCALER_DLIST_START;
+- else
++ if (vc4->gen >= VC4_GEN_6) {
++ unsigned int i;
++
++ for (i = 0; i < HVS_NUM_CHANNELS; i++) {
++ char irq_name[16];
++ int irq;
++
++ snprintf(irq_name, sizeof(irq_name), "ch%u-eof", i);
++
++ irq = platform_get_irq_byname(pdev, irq_name);
++ if (irq < 0) {
++ dev_err(&pdev->dev,
++ "Couldn't get %s interrupt: %d\n",
++ irq_name, irq);
++ return irq;
++ }
++
++ ret = devm_request_irq(&pdev->dev,
++ irq,
++ vc6_hvs_eof_irq_handler,
++ IRQF_NO_AUTOEN,
++ dev_name(&pdev->dev),
++ drm);
++
++ hvs->eof_irq[i].desc = irq;
++ }
++ }
++
++ if (vc4->gen >= VC4_GEN_5)
+ hvs->dlist = hvs->regs + SCALER5_DLIST_START;
++ else
++ hvs->dlist = hvs->regs + SCALER_DLIST_START;
+
+- ret = vc4_hvs_hw_init(hvs);
++ if (vc4->gen >= VC4_GEN_6)
++ ret = vc6_hvs_hw_init(hvs);
++ else
++ ret = vc4_hvs_hw_init(hvs);
+ if (ret)
+ return ret;
+
+@@ -1540,10 +2064,12 @@ static int vc4_hvs_bind(struct device *d
+ if (ret)
+ return ret;
+
+- ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+- vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
+- if (ret)
+- return ret;
++ if (vc4->gen < VC4_GEN_6) {
++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
++ vc4_hvs_irq_handler, 0, "vc4 hvs", drm);
++ if (ret)
++ return ret;
++ }
+
+ return 0;
+ }
+@@ -1568,6 +2094,7 @@ static void vc4_hvs_unbind(struct device
+ drm_mm_remove_node(node);
+ drm_mm_takedown(&vc4->hvs->lbm_mm);
+
++ clk_disable_unprepare(hvs->disp_clk);
+ clk_disable_unprepare(hvs->core_clk);
+
+ vc4->hvs = NULL;
+@@ -1591,6 +2118,7 @@ static int vc4_hvs_dev_remove(struct pla
+
+ static const struct of_device_id vc4_hvs_dt_match[] = {
+ { .compatible = "brcm,bcm2711-hvs" },
++ { .compatible = "brcm,bcm2712-hvs" },
+ { .compatible = "brcm,bcm2835-hvs" },
+ {}
+ };
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -329,17 +329,59 @@ static void vc5_hvs_pv_muxing_commit(str
+ }
+ }
+
++static void vc6_hvs_pv_muxing_commit(struct vc4_dev *vc4,
++ struct drm_atomic_state *state)
++{
++ struct vc4_hvs *hvs = vc4->hvs;
++ struct drm_crtc_state *crtc_state;
++ struct drm_crtc *crtc;
++ unsigned int i;
++
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_6);
++
++ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
++ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
++ struct vc4_encoder *vc4_encoder;
++ struct drm_encoder *encoder;
++ unsigned char mux;
++ u32 reg;
++
++ if (!vc4_state->update_muxing)
++ continue;
++
++ if (vc4_state->assigned_channel != 1)
++ continue;
++
++ encoder = vc4_get_crtc_encoder(crtc, crtc_state);
++ vc4_encoder = to_vc4_encoder(encoder);
++ switch (vc4_encoder->type) {
++ case VC4_ENCODER_TYPE_HDMI1:
++ mux = 0;
++ break;
++
++ case VC4_ENCODER_TYPE_TXP:
++ mux = 2;
++ break;
++
++ default:
++ break;
++ }
++
++ reg = HVS_READ(SCALER6_CONTROL);
++ HVS_WRITE(SCALER6_CONTROL,
++ (reg & ~SCALER6_CONTROL_DSP1_TARGET_MASK) |
++ VC4_SET_FIELD(mux, SCALER6_CONTROL_DSP1_TARGET));
++ }
++}
++
+ static void vc4_atomic_commit_tail(struct drm_atomic_state *state)
+ {
+ struct drm_device *dev = state->dev;
+ struct vc4_dev *vc4 = to_vc4_dev(dev);
+ struct vc4_hvs *hvs = vc4->hvs;
+- struct drm_crtc_state *new_crtc_state;
+ struct vc4_hvs_state *new_hvs_state;
+- struct drm_crtc *crtc;
+ struct vc4_hvs_state *old_hvs_state;
+ unsigned int channel;
+- int i;
+
+ old_hvs_state = vc4_hvs_get_old_global_state(state);
+ if (WARN_ON(IS_ERR(old_hvs_state)))
+@@ -349,14 +391,23 @@ static void vc4_atomic_commit_tail(struc
+ if (WARN_ON(IS_ERR(new_hvs_state)))
+ return;
+
+- for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
+- struct vc4_crtc_state *vc4_crtc_state;
++ if (vc4->gen < VC4_GEN_6) {
++ struct drm_crtc_state *new_crtc_state;
++ struct drm_crtc *crtc;
++ int i;
++
++ for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) {
++ struct vc4_crtc_state *vc4_crtc_state;
+
+- if (!new_crtc_state->commit || vc4->firmware_kms)
+- continue;
++ if (vc4->firmware_kms)
++ continue;
+
+- vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
+- vc4_hvs_mask_underrun(hvs, vc4_crtc_state->assigned_channel);
++ if (!new_crtc_state->commit)
++ continue;
++
++ vc4_crtc_state = to_vc4_crtc_state(new_crtc_state);
++ vc4_hvs_mask_underrun(hvs, vc4_crtc_state->assigned_channel);
++ }
+ }
+
+ for (channel = 0; channel < HVS_NUM_CHANNELS; channel++) {
+@@ -378,7 +429,7 @@ static void vc4_atomic_commit_tail(struc
+ old_hvs_state->fifo_state[channel].pending_commit = NULL;
+ }
+
+- if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
++ if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
+ unsigned long state_rate = max(old_hvs_state->core_clock_rate,
+ new_hvs_state->core_clock_rate);
+ unsigned long core_rate = clamp_t(unsigned long, state_rate,
+@@ -391,17 +442,32 @@ static void vc4_atomic_commit_tail(struc
+ * modeset.
+ */
+ WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate));
++ WARN_ON(clk_set_min_rate(hvs->disp_clk, core_rate));
+ }
+
+ drm_atomic_helper_commit_modeset_disables(dev, state);
+
+- vc4_ctm_commit(vc4, state);
++ if (vc4->gen <= VC4_GEN_5)
++ vc4_ctm_commit(vc4, state);
+
+ if (!vc4->firmware_kms) {
+- if (vc4->gen == VC4_GEN_5)
+- vc5_hvs_pv_muxing_commit(vc4, state);
+- else
++ switch (vc4->gen) {
++ case VC4_GEN_4:
+ vc4_hvs_pv_muxing_commit(vc4, state);
++ break;
++
++ case VC4_GEN_5:
++ vc5_hvs_pv_muxing_commit(vc4, state);
++ break;
++
++ case VC4_GEN_6:
++ vc6_hvs_pv_muxing_commit(vc4, state);
++ break;
++
++ default:
++ drm_err(dev, "Unknown VC4 generation: %d", vc4->gen);
++ break;
++ }
+ }
+
+ drm_atomic_helper_commit_planes(dev, state,
+@@ -417,7 +483,7 @@ static void vc4_atomic_commit_tail(struc
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+- if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
++ if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
+ unsigned long core_rate = min_t(unsigned long,
+ hvs->max_core_rate,
+ new_hvs_state->core_clock_rate);
+@@ -429,6 +495,7 @@ static void vc4_atomic_commit_tail(struc
+ * requirements.
+ */
+ WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate));
++ WARN_ON(clk_set_min_rate(hvs->disp_clk, core_rate));
+
+ drm_dbg(dev, "Core clock actual rate: %lu Hz\n",
+ clk_get_rate(hvs->core_clk));
+@@ -1081,7 +1148,10 @@ int vc4_kms_load(struct drm_device *dev)
+ return ret;
+ }
+
+- if (vc4->gen == VC4_GEN_5) {
++ if (vc4->gen >= VC4_GEN_6) {
++ dev->mode_config.max_width = 8192;
++ dev->mode_config.max_height = 8192;
++ } else if (vc4->gen >= VC4_GEN_5) {
+ dev->mode_config.max_width = 7680;
+ dev->mode_config.max_height = 7680;
+ } else {
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -279,6 +279,7 @@ static bool plane_enabled(struct drm_pla
+ static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+ {
+ struct vc4_plane_state *vc4_state;
++ unsigned int i;
+
+ if (WARN_ON(!plane->state))
+ return NULL;
+@@ -288,6 +289,11 @@ static struct drm_plane_state *vc4_plane
+ return NULL;
+
+ memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
++ memset(&vc4_state->upm, 0, sizeof(vc4_state->upm));
++
++ for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++)
++ vc4_state->upm_handle[i] = 0;
++
+ vc4_state->dlist_initialized = 0;
+
+ __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
+@@ -310,14 +316,30 @@ static void vc4_plane_destroy_state(stru
+ struct drm_plane_state *state)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
++ struct vc4_hvs *hvs = vc4->hvs;
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++ unsigned int i;
+
+ if (drm_mm_node_allocated(&vc4_state->lbm)) {
+ unsigned long irqflags;
+
+- spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
++ spin_lock_irqsave(&hvs->mm_lock, irqflags);
+ drm_mm_remove_node(&vc4_state->lbm);
+- spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
++ spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
++ }
++
++ for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++) {
++ unsigned long irqflags;
++
++ if (!drm_mm_node_allocated(&vc4_state->upm[i]))
++ continue;
++
++ spin_lock_irqsave(&hvs->mm_lock, irqflags);
++ drm_mm_remove_node(&vc4_state->upm[i]);
++ spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
++
++ if (vc4_state->upm_handle[i] > 0)
++ ida_free(&hvs->upm_handles, vc4_state->upm_handle[i]);
+ }
+
+ kfree(vc4_state->dlist);
+@@ -543,6 +565,11 @@ static void vc4_write_tpz(struct vc4_pla
+ recip = ~0 / scale;
+
+ vc4_dlist_write(vc4_state,
++ /*
++ * The BCM2712 is lacking BIT(31) compared to
++ * the previous generations, but we don't use
++ * it.
++ */
+ VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
+ VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
+ vc4_dlist_write(vc4_state,
+@@ -590,10 +617,15 @@ static void vc4_write_ppf(struct vc4_pla
+ vc4_dlist_write(vc4_state,
+ SCALER_PPF_AGC |
+ VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
++ /*
++ * The register layout documentation is slightly
++ * different to setup the phase in the BCM2712,
++ * but they seem equivalent.
++ */
+ VC4_SET_FIELD(phase, SCALER_PPF_IPHASE));
+ }
+
+-static u32 vc4_lbm_size(struct drm_plane_state *state)
++static u32 __vc4_lbm_size(struct drm_plane_state *state)
+ {
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+ struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
+@@ -641,6 +673,131 @@ static u32 vc4_lbm_size(struct drm_plane
+ return lbm;
+ }
+
++static unsigned int vc4_lbm_words_per_component(const struct drm_plane_state *state,
++ unsigned int channel)
++{
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++
++ switch (vc4_state->y_scaling[channel]) {
++ case VC4_SCALING_PPF:
++ return 4;
++
++ case VC4_SCALING_TPZ:
++ return 2;
++
++ default:
++ return 0;
++ }
++}
++
++static unsigned int vc4_lbm_components(const struct drm_plane_state *state,
++ unsigned int channel)
++{
++ const struct drm_format_info *info = state->fb->format;
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++
++ if (vc4_state->y_scaling[channel] == VC4_SCALING_NONE)
++ return 0;
++
++ if (info->is_yuv)
++ return channel ? 2 : 1;
++
++ if (info->has_alpha)
++ return 4;
++
++ return 3;
++}
++
++static unsigned int vc4_lbm_channel_size(const struct drm_plane_state *state,
++ unsigned int channel)
++{
++ const struct drm_format_info *info = state->fb->format;
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++ unsigned int channels_scaled = 0;
++ unsigned int components, words, wpc;
++ unsigned int width, lines;
++ unsigned int i;
++
++ /* LBM is meant to use the smaller of source or dest width, but there
++ * is a issue with UV scaling that the size required for the second
++ * channel is based on the source width only.
++ */
++ if (info->hsub > 1 && channel == 1)
++ width = state->src_w >> 16;
++ else
++ width = min(state->src_w >> 16, state->crtc_w);
++ width = round_up(width / info->hsub, 4);
++
++ wpc = vc4_lbm_words_per_component(state, channel);
++ if (!wpc)
++ return 0;
++
++ components = vc4_lbm_components(state, channel);
++ if (!components)
++ return 0;
++
++ if (state->alpha != DRM_BLEND_ALPHA_OPAQUE)
++ components -= 1;
++
++ words = width * wpc * components;
++
++ lines = DIV_ROUND_UP(words, 128 / info->hsub);
++
++ for (i = 0; i < 2; i++)
++ if (vc4_state->y_scaling[channel] != VC4_SCALING_NONE)
++ channels_scaled++;
++
++ if (channels_scaled == 1)
++ lines = lines / 2;
++
++ return lines;
++}
++
++static unsigned int __vc6_lbm_size(const struct drm_plane_state *state)
++{
++ const struct drm_format_info *info = state->fb->format;
++
++ if (info->hsub > 1)
++ return max(vc4_lbm_channel_size(state, 0),
++ vc4_lbm_channel_size(state, 1));
++ else
++ return vc4_lbm_channel_size(state, 0);
++}
++
++u32 vc4_lbm_size(struct drm_plane_state *state)
++{
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++ struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
++
++ /* LBM is not needed when there's no vertical scaling. */
++ if (vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
++ vc4_state->y_scaling[1] == VC4_SCALING_NONE)
++ return 0;
++
++ if (vc4->gen >= VC4_GEN_6)
++ return __vc6_lbm_size(state);
++ else
++ return __vc4_lbm_size(state);
++}
++
++static size_t vc6_upm_size(const struct drm_plane_state *state,
++ unsigned int plane)
++{
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++ unsigned int stride = state->fb->pitches[plane];
++
++ /*
++ * TODO: This only works for raster formats, and is sub-optimal
++ * for buffers with a stride aligned on 32 bytes.
++ */
++ unsigned int words_per_line = (stride + 62) / 32;
++ unsigned int fetch_region_size = words_per_line * 32;
++ unsigned int buffer_lines = 2 << vc4_state->upm_buffer_lines;
++ unsigned int buffer_size = fetch_region_size * buffer_lines;
++
++ return ALIGN(buffer_size, HVS_UBM_WORD_SIZE);
++}
++
+ static void vc4_write_scaling_parameters(struct drm_plane_state *state,
+ int channel)
+ {
+@@ -744,6 +901,10 @@ static int vc4_plane_allocate_lbm(struct
+ if (!lbm_size)
+ return 0;
+
++ /*
++ * NOTE: BCM2712 doesn't need to be aligned, since the size
++ * returned by vc4_lbm_size() is in words already.
++ */
+ if (vc4->gen == VC4_GEN_5)
+ lbm_size = ALIGN(lbm_size, 64);
+ else if (vc4->gen == VC4_GEN_4)
+@@ -781,6 +942,57 @@ static int vc4_plane_allocate_lbm(struct
+ return 0;
+ }
+
++static int vc6_plane_allocate_upm(struct drm_plane_state *state)
++{
++ const struct drm_format_info *info = state->fb->format;
++ struct drm_device *drm = state->plane->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
++ struct vc4_hvs *hvs = vc4->hvs;
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++ unsigned int i;
++ int ret;
++
++ WARN_ON_ONCE(vc4->gen < VC4_GEN_6);
++
++ vc4_state->upm_buffer_lines = SCALER6_PTR0_UPM_BUFF_SIZE_2_LINES;
++
++ for (i = 0; i < info->num_planes; i++) {
++ unsigned long irqflags;
++ size_t upm_size;
++
++ upm_size = vc6_upm_size(state, i);
++ if (!upm_size)
++ return -EINVAL;
++
++ spin_lock_irqsave(&hvs->mm_lock, irqflags);
++ ret = drm_mm_insert_node_generic(&hvs->upm_mm,
++ &vc4_state->upm[i],
++ upm_size, HVS_UBM_WORD_SIZE,
++ 0, 0);
++ spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
++ if (ret) {
++ drm_err(drm, "Failed to allocate UPM entry: %d\n", ret);
++ return ret;
++ }
++
++ ret = ida_alloc_range(&hvs->upm_handles, 1, 32, GFP_KERNEL);
++ if (ret < 0)
++ return ret;
++
++ vc4_state->upm_handle[i] = ret;
++
++ vc4_state->dlist[vc4_state->ptr0_offset[i]] |=
++ VC4_SET_FIELD(vc4_state->upm[i].start / HVS_UBM_WORD_SIZE,
++ SCALER6_PTR0_UPM_BASE) |
++ VC4_SET_FIELD(vc4_state->upm_handle[i] - 1,
++ SCALER6_PTR0_UPM_HANDLE) |
++ VC4_SET_FIELD(vc4_state->upm_buffer_lines,
++ SCALER6_PTR0_UPM_BUFF_SIZE);
++ }
++
++ return 0;
++}
++
+ /*
+ * The colorspace conversion matrices are held in 3 entries in the dlist.
+ * Create an array of them, with entries for each full and limited mode, and
+@@ -1355,6 +1567,413 @@ static int vc4_plane_mode_set(struct drm
+ return 0;
+ }
+
++static u32 vc6_plane_get_csc_mode(struct vc4_plane_state *vc4_state)
++{
++ struct drm_plane_state *state = &vc4_state->base;
++ u32 ret = 0;
++
++ if (vc4_state->is_yuv) {
++ enum drm_color_encoding color_encoding = state->color_encoding;
++ enum drm_color_range color_range = state->color_range;
++
++ ret |= SCALER6_CTL2_CSC_ENABLE;
++
++ /* CSC pre-loaded with:
++ * 0 = BT601 limited range
++ * 1 = BT709 limited range
++ * 2 = BT2020 limited range
++ * 3 = BT601 full range
++ * 4 = BT709 full range
++ * 5 = BT2020 full range
++ */
++ if (color_encoding > DRM_COLOR_YCBCR_BT2020)
++ color_encoding = DRM_COLOR_YCBCR_BT601;
++ if (color_range > DRM_COLOR_YCBCR_FULL_RANGE)
++ color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
++
++ ret |= VC4_SET_FIELD(color_encoding + (color_range * 3),
++ SCALER6_CTL2_BRCM_CFC_CONTROL);
++ }
++
++ return ret;
++}
++
++static int vc6_plane_mode_set(struct drm_plane *plane,
++ struct drm_plane_state *state)
++{
++ struct drm_device *drm = plane->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
++ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
++ struct drm_framebuffer *fb = state->fb;
++ const struct hvs_format *format = vc4_get_hvs_format(fb->format->format);
++ u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier);
++ int num_planes = fb->format->num_planes;
++ u32 h_subsample = fb->format->hsub;
++ u32 v_subsample = fb->format->vsub;
++ bool mix_plane_alpha;
++ bool covers_screen;
++ u32 scl0, scl1, pitch0;
++ u32 tiling, src_x, src_y;
++ u32 width, height;
++ u32 hvs_format = format->hvs;
++ u32 offsets[3] = { 0 };
++ unsigned int rotation;
++ int ret, i;
++
++ if (vc4_state->dlist_initialized)
++ return 0;
++
++ ret = vc4_plane_setup_clipping_and_scaling(state);
++ if (ret)
++ return ret;
++
++ width = vc4_state->src_w[0] >> 16;
++ height = vc4_state->src_h[0] >> 16;
++
++ /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
++ * and 4:4:4, scl1 should be set to scl0 so both channels of
++ * the scaler do the same thing. For YUV, the Y plane needs
++ * to be put in channel 1 and Cb/Cr in channel 0, so we swap
++ * the scl fields here.
++ */
++ if (num_planes == 1) {
++ scl0 = vc4_get_scl_field(state, 0);
++ scl1 = scl0;
++ } else {
++ scl0 = vc4_get_scl_field(state, 1);
++ scl1 = vc4_get_scl_field(state, 0);
++ }
++
++ rotation = drm_rotation_simplify(state->rotation,
++ DRM_MODE_ROTATE_0 |
++ DRM_MODE_REFLECT_X |
++ DRM_MODE_REFLECT_Y);
++
++ /* We must point to the last line when Y reflection is enabled. */
++ src_y = vc4_state->src_y >> 16;
++ if (rotation & DRM_MODE_REFLECT_Y)
++ src_y += height - 1;
++
++ src_x = vc4_state->src_x >> 16;
++
++ switch (base_format_mod) {
++ case DRM_FORMAT_MOD_LINEAR:
++ tiling = SCALER6_CTL0_ADDR_MODE_LINEAR;
++
++ /* Adjust the base pointer to the first pixel to be scanned
++ * out.
++ */
++ for (i = 0; i < num_planes; i++) {
++ offsets[i] += src_y / (i ? v_subsample : 1) * fb->pitches[i];
++ offsets[i] += src_x / (i ? h_subsample : 1) * fb->format->cpp[i];
++ }
++
++ break;
++
++ case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ case DRM_FORMAT_MOD_BROADCOM_SAND256: {
++ uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
++ u32 components_per_word;
++ u32 starting_offset;
++ u32 fetch_count;
++
++ if (param > SCALER_TILE_HEIGHT_MASK) {
++ DRM_DEBUG_KMS("SAND height too large (%d)\n",
++ param);
++ return -EINVAL;
++ }
++
++ if (fb->format->format == DRM_FORMAT_P030) {
++ hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT;
++ tiling = SCALER6_CTL0_ADDR_MODE_128B;
++ } else {
++ hvs_format = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE;
++
++ switch (base_format_mod) {
++ case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ tiling = SCALER6_CTL0_ADDR_MODE_128B;
++ break;
++ case DRM_FORMAT_MOD_BROADCOM_SAND256:
++ tiling = SCALER6_CTL0_ADDR_MODE_256B;
++ break;
++ default:
++ return -EINVAL;
++ }
++ }
++
++ /* Adjust the base pointer to the first pixel to be scanned
++ * out.
++ *
++ * For P030, y_ptr [31:4] is the 128bit word for the start pixel
++ * y_ptr [3:0] is the pixel (0-11) contained within that 128bit
++ * word that should be taken as the first pixel.
++ * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the
++ * element within the 128bit word, eg for pixel 3 the value
++ * should be 6.
++ */
++ for (i = 0; i < num_planes; i++) {
++ u32 tile_w, tile, x_off, pix_per_tile;
++
++ if (fb->format->format == DRM_FORMAT_P030) {
++ /*
++ * Spec says: bits [31:4] of the given address
++ * should point to the 128-bit word containing
++ * the desired starting pixel, and bits[3:0]
++ * should be between 0 and 11, indicating which
++ * of the 12-pixels in that 128-bit word is the
++ * first pixel to be used
++ */
++ u32 remaining_pixels = src_x % 96;
++ u32 aligned = remaining_pixels / 12;
++ u32 last_bits = remaining_pixels % 12;
++
++ x_off = aligned * 16 + last_bits;
++ tile_w = 128;
++ pix_per_tile = 96;
++ } else {
++ switch (base_format_mod) {
++ case DRM_FORMAT_MOD_BROADCOM_SAND128:
++ tile_w = 128;
++ break;
++ case DRM_FORMAT_MOD_BROADCOM_SAND256:
++ tile_w = 256;
++ break;
++ default:
++ return -EINVAL;
++ }
++ pix_per_tile = tile_w / fb->format->cpp[0];
++ x_off = (src_x % pix_per_tile) /
++ (i ? h_subsample : 1) *
++ fb->format->cpp[i];
++ }
++
++ tile = src_x / pix_per_tile;
++
++ offsets[i] += param * tile_w * tile;
++ offsets[i] += src_y / (i ? v_subsample : 1) * tile_w;
++ offsets[i] += x_off & ~(i ? 1 : 0);
++ }
++
++ components_per_word = fb->format->format == DRM_FORMAT_P030 ? 24 : 32;
++ starting_offset = src_x % components_per_word;
++ fetch_count = (width + starting_offset + components_per_word - 1) /
++ components_per_word;
++
++ pitch0 = VC4_SET_FIELD(param, SCALER6_PTR2_PITCH) |
++ VC4_SET_FIELD(fetch_count - 1, SCALER6_PTR2_FETCH_COUNT);
++ break;
++ }
++
++ default:
++ DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
++ (long long)fb->modifier);
++ return -EINVAL;
++ }
++
++ /* fetch an extra pixel if we don't actually line up with the left edge. */
++ if ((vc4_state->src_x & 0xffff) && vc4_state->src_x < (state->fb->width << 16))
++ width++;
++
++ /* same for the right side */
++ if (((vc4_state->src_x + vc4_state->src_w[0]) & 0xffff) &&
++ vc4_state->src_x + vc4_state->src_w[0] < (state->fb->width << 16))
++ width++;
++
++ /* now for the top */
++ if ((vc4_state->src_y & 0xffff) && vc4_state->src_y < (state->fb->height << 16))
++ height++;
++
++ /* and the bottom */
++ if (((vc4_state->src_y + vc4_state->src_h[0]) & 0xffff) &&
++ vc4_state->src_y + vc4_state->src_h[0] < (state->fb->height << 16))
++ height++;
++
++ /* for YUV444 hardware wants double the width, otherwise it doesn't
++ * fetch full width of chroma
++ */
++ if (format->drm == DRM_FORMAT_YUV444 || format->drm == DRM_FORMAT_YVU444)
++ width <<= 1;
++
++ /* Don't waste cycles mixing with plane alpha if the set alpha
++ * is opaque or there is no per-pixel alpha information.
++ * In any case we use the alpha property value as the fixed alpha.
++ */
++ mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
++ fb->format->has_alpha;
++
++ /* Control Word 0: Scaling Configuration & Element Validity*/
++ vc4_dlist_write(vc4_state,
++ SCALER6_CTL0_VALID |
++ VC4_SET_FIELD(tiling, SCALER6_CTL0_ADDR_MODE) |
++ VC4_SET_FIELD(0, SCALER6_CTL0_ALPHA_MASK) |
++ (vc4_state->is_unity ? SCALER6_CTL0_UNITY : 0) |
++ VC4_SET_FIELD(format->pixel_order_hvs5, SCALER6_CTL0_ORDERRGBA) |
++ VC4_SET_FIELD(scl1, SCALER6_CTL0_SCL1_MODE) |
++ VC4_SET_FIELD(scl0, SCALER6_CTL0_SCL0_MODE) |
++ VC4_SET_FIELD(hvs_format, SCALER6_CTL0_PIXEL_FORMAT));
++
++ /* Position Word 0: Image Position */
++ vc4_state->pos0_offset = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(vc4_state->crtc_y, SCALER6_POS0_START_Y) |
++ (rotation & DRM_MODE_REFLECT_X ? SCALER6_POS0_HFLIP : 0) |
++ VC4_SET_FIELD(vc4_state->crtc_x, SCALER6_POS0_START_X));
++
++ /* Control Word 2: Alpha Value & CSC */
++ vc4_dlist_write(vc4_state,
++ vc6_plane_get_csc_mode(vc4_state) |
++ vc4_hvs5_get_alpha_blend_mode(state) |
++ (mix_plane_alpha ? SCALER6_CTL2_ALPHA_MIX : 0) |
++ VC4_SET_FIELD(state->alpha >> 4, SCALER5_CTL2_ALPHA));
++
++ /* Position Word 1: Scaled Image Dimensions */
++ if (!vc4_state->is_unity)
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(vc4_state->crtc_h - 1,
++ SCALER6_POS1_SCL_LINES) |
++ VC4_SET_FIELD(vc4_state->crtc_w - 1,
++ SCALER6_POS1_SCL_WIDTH));
++
++ /* Position Word 2: Source Image Size */
++ vc4_state->pos2_offset = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(height - 1,
++ SCALER6_POS2_SRC_LINES) |
++ VC4_SET_FIELD(width - 1,
++ SCALER6_POS2_SRC_WIDTH));
++
++ /* Position Word 3: Context */
++ vc4_dlist_write(vc4_state, 0xc0c0c0c0);
++
++ /*
++ * TODO: This only covers Raster Scan Order planes
++ */
++ for (i = 0; i < num_planes; i++) {
++ dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
++
++ paddr += offsets[i];
++
++ /* Pointer Word 0 */
++ vc4_state->ptr0_offset[i] = vc4_state->dlist_count;
++ vc4_dlist_write(vc4_state,
++ (rotation & DRM_MODE_REFLECT_Y ? SCALER6_PTR0_VFLIP : 0) |
++ /*
++ * The UPM buffer will be allocated in
++ * vc6_plane_allocate_upm().
++ */
++ VC4_SET_FIELD(upper_32_bits(paddr) & 0xf,
++ SCALER6_PTR0_UPPER_ADDR));
++
++ /* Pointer Word 1 */
++ vc4_dlist_write(vc4_state, lower_32_bits(paddr));
++
++ /* Pointer Word 2 */
++ if (base_format_mod != DRM_FORMAT_MOD_BROADCOM_SAND128 &&
++ base_format_mod != DRM_FORMAT_MOD_BROADCOM_SAND256) {
++ vc4_dlist_write(vc4_state,
++ VC4_SET_FIELD(fb->pitches[i],
++ SCALER6_PTR2_PITCH));
++ } else {
++ vc4_dlist_write(vc4_state, pitch0);
++ }
++ }
++
++ /*
++ * Palette Word 0
++ * TODO: We're not using the palette mode
++ */
++
++ /*
++ * Trans Word 0
++ * TODO: It's only relevant if we set the trans_rgb bit in the
++ * control word 0, and we don't at the moment.
++ */
++
++ vc4_state->lbm_offset = 0;
++
++ if (!vc4_state->is_unity || fb->format->is_yuv) {
++ /*
++ * Reserve a slot for the LBM Base Address. The real value will
++ * be set when calling vc4_plane_allocate_lbm().
++ */
++ if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
++ vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
++ vc4_state->lbm_offset = vc4_state->dlist_count;
++ vc4_dlist_counter_increment(vc4_state);
++ }
++
++ if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
++ vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
++ vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
++ vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
++ if (num_planes > 1)
++ /*
++ * Emit Cb/Cr as channel 0 and Y as channel
++ * 1. This matches how we set up scl0/scl1
++ * above.
++ */
++ vc4_write_scaling_parameters(state, 1);
++
++ vc4_write_scaling_parameters(state, 0);
++ }
++
++ /*
++ * If any PPF setup was done, then all the kernel
++ * pointers get uploaded.
++ */
++ if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
++ vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
++ vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
++ vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
++ u32 kernel =
++ VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
++ SCALER_PPF_KERNEL_OFFSET);
++
++ /* HPPF plane 0 */
++ vc4_dlist_write(vc4_state, kernel);
++ /* VPPF plane 0 */
++ vc4_dlist_write(vc4_state, kernel);
++ /* HPPF plane 1 */
++ vc4_dlist_write(vc4_state, kernel);
++ /* VPPF plane 1 */
++ vc4_dlist_write(vc4_state, kernel);
++ }
++ }
++
++ vc4_dlist_write(vc4_state, SCALER6_CTL0_END);
++
++ vc4_state->dlist[0] |=
++ VC4_SET_FIELD(vc4_state->dlist_count, SCALER6_CTL0_NEXT);
++
++ /* crtc_* are already clipped coordinates. */
++ covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 &&
++ vc4_state->crtc_w == state->crtc->mode.hdisplay &&
++ vc4_state->crtc_h == state->crtc->mode.vdisplay;
++
++ /*
++ * Background fill might be necessary when the plane has per-pixel
++ * alpha content or a non-opaque plane alpha and could blend from the
++ * background or does not cover the entire screen.
++ */
++ vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen ||
++ state->alpha != DRM_BLEND_ALPHA_OPAQUE;
++
++ /*
++ * Flag the dlist as initialized to avoid checking it twice in case
++ * the async update check already called vc4_plane_mode_set() and
++ * decided to fallback to sync update because async update was not
++ * possible.
++ */
++ vc4_state->dlist_initialized = 1;
++
++ vc4_plane_calc_load(state);
++
++ drm_dbg_driver(drm, "[PLANE:%d:%s] Computed DLIST size: %u\n",
++ plane->base.id, plane->name, vc4_state->dlist_count);
++
++ return 0;
++}
++
+ /* If a modeset involves changing the setup of a plane, the atomic
+ * infrastructure will call this to validate a proposed plane setup.
+ * However, if a plane isn't getting updated, this (and the
+@@ -1365,6 +1984,7 @@ static int vc4_plane_mode_set(struct drm
+ static int vc4_plane_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *state)
+ {
++ struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+ plane);
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(new_plane_state);
+@@ -1375,7 +1995,10 @@ static int vc4_plane_atomic_check(struct
+ if (!plane_enabled(new_plane_state))
+ return 0;
+
+- ret = vc4_plane_mode_set(plane, new_plane_state);
++ if (vc4->gen >= VC4_GEN_6)
++ ret = vc6_plane_mode_set(plane, new_plane_state);
++ else
++ ret = vc4_plane_mode_set(plane, new_plane_state);
+ if (ret)
+ return ret;
+
+@@ -1383,6 +2006,12 @@ static int vc4_plane_atomic_check(struct
+ if (ret)
+ return ret;
+
++ if (vc4->gen >= VC4_GEN_6) {
++ ret = vc6_plane_allocate_upm(new_plane_state);
++ if (ret)
++ return ret;
++ }
++
+ return 0;
+ }
+
+@@ -1716,7 +2345,7 @@ struct drm_plane *vc4_plane_init(struct
+ };
+
+ for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
+- if (!hvs_formats[i].hvs5_only || vc4->gen == VC4_GEN_5) {
++ if (!hvs_formats[i].hvs5_only || vc4->gen >= VC4_GEN_5) {
+ formats[num_formats] = hvs_formats[i].drm;
+ num_formats++;
+ }
+@@ -1731,7 +2360,7 @@ struct drm_plane *vc4_plane_init(struct
+ return ERR_CAST(vc4_plane);
+ plane = &vc4_plane->base;
+
+- if (vc4->gen == VC4_GEN_5)
++ if (vc4->gen >= VC4_GEN_5)
+ drm_plane_helper_add(plane, &vc5_plane_helper_funcs);
+ else
+ drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -536,6 +536,130 @@
+
+ #define SCALER5_DLIST_START 0x00004000
+
++#define SCALER6_VERSION 0x00000000
++#define SCALER6_CXM_SIZE 0x00000004
++#define SCALER6_LBM_SIZE 0x00000008
++#define SCALER6_UBM_SIZE 0x0000000c
++#define SCALER6_COBA_SIZE 0x00000010
++#define SCALER6_COB_SIZE 0x00000014
++
++#define SCALER6_CONTROL 0x00000020
++# define SCALER6_CONTROL_HVS_EN BIT(31)
++# define SCALER6_CONTROL_PF_LINES_MASK VC4_MASK(22, 18)
++# define SCALER6_CONTROL_ABORT_ON_EMPTY BIT(16)
++# define SCALER6_CONTROL_DSP1_TARGET_MASK VC4_MASK(13, 12)
++# define SCALER6_CONTROL_MAX_REQS_MASK VC4_MASK(7, 4)
++
++#define SCALER6_FETCHER_STATUS 0x00000024
++#define SCALER6_FETCH_STATUS 0x00000028
++#define SCALER6_HANDLE_ERROR 0x0000002c
++
++#define SCALER6_DISP0_CTRL0 0x00000030
++#define SCALER6_DISPX_CTRL0(x) \
++ (SCALER6_DISP0_CTRL0 + ((x) * (SCALER6_DISP1_CTRL0 - SCALER6_DISP0_CTRL0)))
++# define SCALER6_DISPX_CTRL0_ENB BIT(31)
++# define SCALER6_DISPX_CTRL0_RESET BIT(30)
++# define SCALER6_DISPX_CTRL0_FWIDTH_MASK VC4_MASK(28, 16)
++# define SCALER6_DISPX_CTRL0_ONESHOT BIT(15)
++# define SCALER6_DISPX_CTRL0_ONECTX_MASK VC4_MASK(14, 13)
++# define SCALER6_DISPX_CTRL0_LINES_MASK VC4_MASK(12, 0)
++
++#define SCALER6_DISP0_CTRL1 0x00000034
++#define SCALER6_DISPX_CTRL1(x) \
++ (SCALER6_DISP0_CTRL1 + ((x) * (SCALER6_DISP1_CTRL1 - SCALER6_DISP0_CTRL1)))
++# define SCALER6_DISPX_CTRL1_BGENB BIT(8)
++# define SCALER6_DISPX_CTRL1_INTLACE BIT(0)
++
++#define SCALER6_DISP0_BGND 0x00000038
++#define SCALER6_DISPX_BGND(x) \
++ (SCALER6_DISP0_BGND + ((x) * (SCALER6_DISP1_BGND - SCALER6_DISP0_BGND)))
++
++#define SCALER6_DISP0_LPTRS 0x0000003c
++#define SCALER6_DISPX_LPTRS(x) \
++ (SCALER6_DISP0_LPTRS + ((x) * (SCALER6_DISP1_LPTRS - SCALER6_DISP0_LPTRS)))
++# define SCALER6_DISPX_LPTRS_HEADE_MASK VC4_MASK(11, 0)
++
++#define SCALER6_DISP0_COB 0x00000040
++#define SCALER6_DISPX_COB(x) \
++ (SCALER6_DISP0_COB + ((x) * (SCALER6_DISP1_COB - SCALER6_DISP0_COB)))
++# define SCALER6_DISPX_COB_TOP_MASK VC4_MASK(31, 16)
++# define SCALER6_DISPX_COB_BASE_MASK VC4_MASK(15, 0)
++
++#define SCALER6_DISP0_STATUS 0x00000044
++
++#define SCALER6_DISPX_STATUS(x) \
++ (SCALER6_DISP0_STATUS + ((x) * (SCALER6_DISP1_STATUS - SCALER6_DISP0_STATUS)))
++# define SCALER6_DISPX_STATUS_EMPTY BIT(22)
++# define SCALER6_DISPX_STATUS_FRCNT_MASK VC4_MASK(21, 16)
++# define SCALER6_DISPX_STATUS_OFIELD BIT(15)
++# define SCALER6_DISPX_STATUS_MODE_MASK VC4_MASK(14, 13)
++# define SCALER6_DISPX_STATUS_MODE_DISABLED 0
++# define SCALER6_DISPX_STATUS_MODE_INIT 1
++# define SCALER6_DISPX_STATUS_MODE_RUN 2
++# define SCALER6_DISPX_STATUS_MODE_EOF 3
++# define SCALER6_DISPX_STATUS_YLINE_MASK VC4_MASK(12, 0)
++
++#define SCALER6_DISP0_DL 0x00000048
++
++#define SCALER6_DISPX_DL(x) \
++ (SCALER6_DISP0_DL + ((x) * (SCALER6_DISP1_DL - SCALER6_DISP0_DL)))
++# define SCALER6_DISPX_DL_LACT_MASK VC4_MASK(11, 0)
++
++#define SCALER6_DISP0_RUN 0x0000004c
++#define SCALER6_DISP1_CTRL0 0x00000050
++#define SCALER6_DISP1_CTRL1 0x00000054
++#define SCALER6_DISP1_BGND 0x00000058
++#define SCALER6_DISP1_LPTRS 0x0000005c
++#define SCALER6_DISP1_COB 0x00000060
++#define SCALER6_DISP1_STATUS 0x00000064
++#define SCALER6_DISP1_DL 0x00000068
++#define SCALER6_DISP1_RUN 0x0000006c
++#define SCALER6_DISP2_CTRL0 0x00000070
++#define SCALER6_DISP2_CTRL1 0x00000074
++#define SCALER6_DISP2_BGND 0x00000078
++#define SCALER6_DISP2_LPTRS 0x0000007c
++#define SCALER6_DISP2_COB 0x00000080
++#define SCALER6_DISP2_STATUS 0x00000084
++#define SCALER6_DISP2_DL 0x00000088
++#define SCALER6_DISP2_RUN 0x0000008c
++#define SCALER6_EOLN 0x00000090
++#define SCALER6_DL_STATUS 0x00000094
++#define SCALER6_BFG_MISC 0x0000009c
++#define SCALER6_QOS0 0x000000a0
++#define SCALER6_PROF0 0x000000a4
++#define SCALER6_QOS1 0x000000a8
++#define SCALER6_PROF1 0x000000ac
++#define SCALER6_QOS2 0x000000b0
++#define SCALER6_PROF2 0x000000b4
++#define SCALER6_PRI_MAP0 0x000000b8
++#define SCALER6_PRI_MAP1 0x000000bc
++#define SCALER6_HISTCTRL 0x000000c0
++#define SCALER6_HISTBIN0 0x000000c4
++#define SCALER6_HISTBIN1 0x000000c8
++#define SCALER6_HISTBIN2 0x000000cc
++#define SCALER6_HISTBIN3 0x000000d0
++#define SCALER6_HISTBIN4 0x000000d4
++#define SCALER6_HISTBIN5 0x000000d8
++#define SCALER6_HISTBIN6 0x000000dc
++#define SCALER6_HISTBIN7 0x000000e0
++#define SCALER6_HDR_CFG_REMAP 0x000000f4
++#define SCALER6_COL_SPACE 0x000000f8
++#define SCALER6_HVS_ID 0x000000fc
++#define SCALER6_CFC1 0x00000100
++#define SCALER6_DISP_UPM_ISO0 0x00000200
++#define SCALER6_DISP_UPM_ISO1 0x00000204
++#define SCALER6_DISP_UPM_ISO2 0x00000208
++#define SCALER6_DISP_LBM_ISO0 0x0000020c
++#define SCALER6_DISP_LBM_ISO1 0x00000210
++#define SCALER6_DISP_LBM_ISO2 0x00000214
++#define SCALER6_DISP_COB_ISO0 0x00000218
++#define SCALER6_DISP_COB_ISO1 0x0000021c
++#define SCALER6_DISP_COB_ISO2 0x00000220
++#define SCALER6_BAD_COB 0x00000224
++#define SCALER6_BAD_LBM 0x00000228
++#define SCALER6_BAD_UPM 0x0000022c
++#define SCALER6_BAD_AXI 0x00000230
++
+ # define VC4_HDMI_SW_RESET_FORMAT_DETECT BIT(1)
+ # define VC4_HDMI_SW_RESET_HDMI BIT(0)
+
+@@ -1131,4 +1255,61 @@ enum hvs_pixel_format {
+ #define SCALER_PITCH0_TILE_WIDTH_R_MASK VC4_MASK(6, 0)
+ #define SCALER_PITCH0_TILE_WIDTH_R_SHIFT 0
+
++#define SCALER6_CTL0_END BIT(31)
++#define SCALER6_CTL0_VALID BIT(30)
++#define SCALER6_CTL0_NEXT_MASK VC4_MASK(29, 24)
++#define SCALER6_CTL0_RGB_TRANS BIT(23)
++#define SCALER6_CTL0_ADDR_MODE_MASK VC4_MASK(22, 20)
++#define SCALER6_CTL0_ADDR_MODE_LINEAR 0
++#define SCALER6_CTL0_ADDR_MODE_128B 1
++#define SCALER6_CTL0_ADDR_MODE_256B 2
++#define SCALER6_CTL0_ADDR_MODE_MAP8 3
++#define SCALER6_CTL0_ADDR_MODE_UIF 4
++
++#define SCALER6_CTL0_ALPHA_MASK_MASK VC4_MASK(19, 18)
++#define SCALER6_CTL0_UNITY BIT(15)
++#define SCALER6_CTL0_ORDERRGBA_MASK VC4_MASK(14, 13)
++#define SCALER6_CTL0_SCL1_MODE_MASK VC4_MASK(10, 8)
++#define SCALER6_CTL0_SCL0_MODE_MASK VC4_MASK(7, 5)
++#define SCALER6_CTL0_PIXEL_FORMAT_MASK VC4_MASK(4, 0)
++
++#define SCALER6_POS0_START_Y_MASK VC4_MASK(28, 16)
++#define SCALER6_POS0_HFLIP BIT(15)
++#define SCALER6_POS0_START_X_MASK VC4_MASK(12, 0)
++
++#define SCALER6_CTL2_ALPHA_MODE_MASK VC4_MASK(31, 30)
++#define SCALER6_CTL2_ALPHA_PREMULT BIT(29)
++#define SCALER6_CTL2_ALPHA_MIX BIT(28)
++#define SCALER6_CTL2_BFG BIT(26)
++#define SCALER6_CTL2_CSC_ENABLE BIT(25)
++#define SCALER6_CTL2_BRCM_CFC_CONTROL_MASK VC4_MASK(18, 16)
++#define SCALER6_CTL2_ALPHA_MASK VC4_MASK(15, 4)
++
++#define SCALER6_POS1_SCL_LINES_MASK VC4_MASK(28, 16)
++#define SCALER6_POS1_SCL_WIDTH_MASK VC4_MASK(12, 0)
++
++#define SCALER6_POS2_SRC_LINES_MASK VC4_MASK(28, 16)
++#define SCALER6_POS2_SRC_WIDTH_MASK VC4_MASK(12, 0)
++
++#define SCALER6_PTR0_VFLIP BIT(31)
++#define SCALER6_PTR0_UPM_BASE_MASK VC4_MASK(28, 16)
++#define SCALER6_PTR0_UPM_HANDLE_MASK VC4_MASK(14, 10)
++#define SCALER6_PTR0_UPM_BUFF_SIZE_MASK VC4_MASK(9, 8)
++#define SCALER6_PTR0_UPM_BUFF_SIZE_16_LINES 3
++#define SCALER6_PTR0_UPM_BUFF_SIZE_8_LINES 2
++#define SCALER6_PTR0_UPM_BUFF_SIZE_4_LINES 1
++#define SCALER6_PTR0_UPM_BUFF_SIZE_2_LINES 0
++#define SCALER6_PTR0_UPPER_ADDR_MASK VC4_MASK(7, 0)
++
++#define SCALER6_PTR2_ALPHA_BPP_MASK VC4_MASK(31, 31)
++#define SCALER6_PTR2_ALPHA_BPP_1BPP 1
++#define SCALER6_PTR2_ALPHA_BPP_8BPP 0
++#define SCALER6_PTR2_ALPHA_ORDER_MASK VC4_MASK(30, 30)
++#define SCALER6_PTR2_ALPHA_ORDER_MSB_TO_LSB 1
++#define SCALER6_PTR2_ALPHA_ORDER_LSB_TO_MSB 0
++#define SCALER6_PTR2_ALPHA_OFFS_MASK VC4_MASK(29, 27)
++#define SCALER6_PTR2_LSKIP_MASK VC4_MASK(26, 24)
++#define SCALER6_PTR2_PITCH_MASK VC4_MASK(16, 0)
++#define SCALER6_PTR2_FETCH_COUNT_MASK VC4_MASK(26, 16)
++
+ #endif /* VC4_REGS_H */
--- /dev/null
+From 000f1b7d4dc5b515c755ee25db301e26bded00e1 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:33:23 +0100
+Subject: [PATCH] drm/vc4: crtc: Add support for BCM2712 PixelValves
+
+The PixelValves found on the BCM2712 are similar to the ones found in
+the previous generation.
+
+Compared to BCM2711, the pixelvalves only drive one HDMI controller each
+and HDMI1 PixelValve has a FIFO long enough to support 4k at 60Hz.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 53 ++++++++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
+ drivers/gpu/drm/vc4/vc4_regs.h | 5 ++++
+ 3 files changed, 58 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -239,6 +239,11 @@ static u32 vc4_get_fifo_full_level(struc
+ const struct vc4_crtc_data *crtc_data = vc4_crtc_to_vc4_crtc_data(vc4_crtc);
+ const struct vc4_pv_data *pv_data = vc4_crtc_to_vc4_pv_data(vc4_crtc);
+ struct vc4_dev *vc4 = to_vc4_dev(vc4_crtc->base.dev);
++
++ /*
++ * NOTE: Could we use register 0x68 (PV_HW_CFG1) to get the FIFO
++ * size?
++ */
+ u32 fifo_len_bytes = pv_data->fifo_depth;
+
+ /*
+@@ -393,6 +398,12 @@ static void vc4_crtc_config_pv(struct dr
+
+ vc4_crtc_pixelvalve_reset(crtc);
+
++ /*
++ * NOTE: The BCM2712 has a H_OTE (Horizontal Odd Timing Enable)
++ * bit that, when set, will allow to specify the timings in
++ * pixels instead of cycles, thus allowing to specify odd
++ * timings.
++ */
+ CRTC_WRITE(PV_HORZA,
+ VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
+ PV_HORZA_HBP) |
+@@ -462,11 +473,17 @@ static void vc4_crtc_config_pv(struct dr
+ if (is_dsi)
+ CRTC_WRITE(PV_HACT_ACT, mode->hdisplay * pixel_rep);
+
+- if (vc4->gen == VC4_GEN_5)
++ if (vc4->gen >= VC4_GEN_5)
+ CRTC_WRITE(PV_MUX_CFG,
+ VC4_SET_FIELD(PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP,
+ PV_MUX_CFG_RGB_PIXEL_MUX_MODE));
+
++ if (vc4->gen >= VC4_GEN_6)
++ CRTC_WRITE(PV_PIPE_INIT_CTRL,
++ VC4_SET_FIELD(1, PV_PIPE_INIT_CTRL_PV_INIT_WIDTH) |
++ VC4_SET_FIELD(1, PV_PIPE_INIT_CTRL_PV_INIT_IDLE) |
++ PV_PIPE_INIT_CTRL_PV_INIT_EN);
++
+ CRTC_WRITE(PV_CONTROL, PV_CONTROL_FIFO_CLR |
+ vc4_crtc_get_fifo_full_level_bits(vc4_crtc, format) |
+ VC4_SET_FIELD(format, PV_CONTROL_FORMAT) |
+@@ -565,7 +582,11 @@ int vc4_crtc_disable_at_boot(struct drm_
+ if (!(of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
+ "brcm,bcm2711-pixelvalve2") ||
+ of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
+- "brcm,bcm2711-pixelvalve4")))
++ "brcm,bcm2711-pixelvalve4") ||
++ of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
++ "brcm,bcm2712-pixelvalve0") ||
++ of_device_is_compatible(vc4_crtc->pdev->dev.of_node,
++ "brcm,bcm2712-pixelvalve1")))
+ return 0;
+
+ if (!(CRTC_READ(PV_CONTROL) & PV_CONTROL_EN))
+@@ -1304,6 +1325,32 @@ const struct vc4_pv_data bcm2711_pv4_dat
+ },
+ };
+
++const struct vc4_pv_data bcm2712_pv0_data = {
++ .base = {
++ .debugfs_name = "crtc0_regs",
++ .hvs_available_channels = BIT(0),
++ .hvs_output = 0,
++ },
++ .fifo_depth = 64,
++ .pixels_per_clock = 2,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_HDMI0,
++ },
++};
++
++const struct vc4_pv_data bcm2712_pv1_data = {
++ .base = {
++ .debugfs_name = "crtc1_regs",
++ .hvs_available_channels = BIT(1),
++ .hvs_output = 1,
++ },
++ .fifo_depth = 64,
++ .pixels_per_clock = 2,
++ .encoder_types = {
++ [0] = VC4_ENCODER_TYPE_HDMI1,
++ },
++};
++
+ static const struct of_device_id vc4_crtc_dt_match[] = {
+ { .compatible = "brcm,bcm2835-pixelvalve0", .data = &bcm2835_pv0_data },
+ { .compatible = "brcm,bcm2835-pixelvalve1", .data = &bcm2835_pv1_data },
+@@ -1313,6 +1360,8 @@ static const struct of_device_id vc4_crt
+ { .compatible = "brcm,bcm2711-pixelvalve2", .data = &bcm2711_pv2_data },
+ { .compatible = "brcm,bcm2711-pixelvalve3", .data = &bcm2711_pv3_data },
+ { .compatible = "brcm,bcm2711-pixelvalve4", .data = &bcm2711_pv4_data },
++ { .compatible = "brcm,bcm2712-pixelvalve0", .data = &bcm2712_pv0_data },
++ { .compatible = "brcm,bcm2712-pixelvalve1", .data = &bcm2712_pv1_data },
+ {}
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -583,6 +583,8 @@ extern const struct vc4_pv_data bcm2711_
+ extern const struct vc4_pv_data bcm2711_pv2_data;
+ extern const struct vc4_pv_data bcm2711_pv3_data;
+ extern const struct vc4_pv_data bcm2711_pv4_data;
++extern const struct vc4_pv_data bcm2712_pv0_data;
++extern const struct vc4_pv_data bcm2712_pv1_data;
+
+ struct vc5_gamma_entry {
+ u32 x_c_terms;
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -215,6 +215,11 @@
+ # define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_SHIFT 2
+ # define PV_MUX_CFG_RGB_PIXEL_MUX_MODE_NO_SWAP 8
+
++#define PV_PIPE_INIT_CTRL 0x94
++# define PV_PIPE_INIT_CTRL_PV_INIT_WIDTH_MASK VC4_MASK(11, 8)
++# define PV_PIPE_INIT_CTRL_PV_INIT_IDLE_MASK VC4_MASK(7, 4)
++# define PV_PIPE_INIT_CTRL_PV_INIT_EN BIT(0)
++
+ #define SCALER_CHANNELS_COUNT 3
+
+ #define SCALER_DISPCTRL 0x00000000
--- /dev/null
+From 1bb54596ae2a9a36f4aa9f8f2ba941320f463811 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 15:34:30 +0100
+Subject: [PATCH] drm/vc4: hdmi: Add support for BCM2712 HDMI controllers
+
+The HDMI controllers found in the BCM2712 are largely the ones found in
+the BCM2711 with a different PHY.
+
+There's some difference with how timings are split between registers,
+and HDMI1 is now able to run at 4k/60Hz.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 82 +++-
+ drivers/gpu/drm/vc4/vc4_hdmi.h | 4 +
+ drivers/gpu/drm/vc4/vc4_hdmi_phy.c | 640 ++++++++++++++++++++++++++++
+ drivers/gpu/drm/vc4/vc4_hdmi_regs.h | 217 ++++++++++
+ 4 files changed, 937 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -1127,6 +1127,7 @@ static void vc4_hdmi_encoder_post_crtc_d
+ {
+ struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
+ struct drm_device *drm = vc4_hdmi->connector.dev;
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ unsigned long flags;
+ int idx;
+
+@@ -1143,14 +1144,25 @@ static void vc4_hdmi_encoder_post_crtc_d
+
+ HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) | VC4_HD_VID_CTL_CLRRGB);
+
++ if (vc4->gen >= VC4_GEN_6)
++ HDMI_WRITE(HDMI_VID_CTL, HDMI_READ(HDMI_VID_CTL) |
++ VC4_HD_VID_CTL_BLANKPIX);
++
+ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+
+ mdelay(1);
+
+- spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+- HDMI_WRITE(HDMI_VID_CTL,
+- HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
+- spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++ /*
++ * TODO: This should work on BCM2712, but doesn't for some
++ * reason and result in a system lockup.
++ */
++ if (vc4->gen < VC4_GEN_6) {
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++ HDMI_WRITE(HDMI_VID_CTL,
++ HDMI_READ(HDMI_VID_CTL) &
++ ~VC4_HD_VID_CTL_ENABLE);
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++ }
+
+ vc4_hdmi_disable_scrambling(encoder);
+
+@@ -1757,7 +1769,6 @@ static void vc4_hdmi_encoder_pre_crtc_co
+ goto err_put_runtime_pm;
+ }
+
+-
+ vc4_hdmi_cec_update_clk_div(vc4_hdmi);
+
+ if (tmds_char_rate > 297000000)
+@@ -1862,6 +1873,7 @@ static void vc4_hdmi_encoder_post_crtc_e
+ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
+
+ HDMI_WRITE(HDMI_VID_CTL,
++ HDMI_READ(HDMI_VID_CTL) |
+ VC4_HD_VID_CTL_ENABLE |
+ VC4_HD_VID_CTL_CLRRGB |
+ VC4_HD_VID_CTL_UNDERFLOW_ENABLE |
+@@ -3796,7 +3808,9 @@ static int vc4_hdmi_bind(struct device *
+ return ret;
+
+ if ((of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi0") ||
+- of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1")) &&
++ of_device_is_compatible(dev->of_node, "brcm,bcm2711-hdmi1") ||
++ of_device_is_compatible(dev->of_node, "brcm,bcm2712-hdmi0") ||
++ of_device_is_compatible(dev->of_node, "brcm,bcm2712-hdmi1")) &&
+ HDMI_READ(HDMI_VID_CTL) & VC4_HD_VID_CTL_ENABLE) {
+ clk_prepare_enable(vc4_hdmi->pixel_clock);
+ clk_prepare_enable(vc4_hdmi->hsm_clock);
+@@ -3931,10 +3945,66 @@ static const struct vc4_hdmi_variant bcm
+ .hp_detect = vc5_hdmi_hp_detect,
+ };
+
++static const struct vc4_hdmi_variant bcm2712_hdmi0_variant = {
++ .encoder_type = VC4_ENCODER_TYPE_HDMI0,
++ .debugfs_name = "hdmi0_regs",
++ .card_name = "vc4-hdmi-0",
++ .max_pixel_clock = 600000000,
++ .registers = vc6_hdmi_hdmi0_fields,
++ .num_registers = ARRAY_SIZE(vc6_hdmi_hdmi0_fields),
++ .phy_lane_mapping = {
++ PHY_LANE_0,
++ PHY_LANE_1,
++ PHY_LANE_2,
++ PHY_LANE_CK,
++ },
++ .unsupported_odd_h_timings = true,
++ .external_irq_controller = true,
++
++ .init_resources = vc5_hdmi_init_resources,
++ .csc_setup = vc5_hdmi_csc_setup,
++ .reset = vc5_hdmi_reset,
++ .set_timings = vc5_hdmi_set_timings,
++ .phy_init = vc6_hdmi_phy_init,
++ .phy_disable = vc6_hdmi_phy_disable,
++ .channel_map = vc5_hdmi_channel_map,
++ .supports_hdr = true,
++ .hp_detect = vc5_hdmi_hp_detect,
++};
++
++static const struct vc4_hdmi_variant bcm2712_hdmi1_variant = {
++ .encoder_type = VC4_ENCODER_TYPE_HDMI1,
++ .debugfs_name = "hdmi1_regs",
++ .card_name = "vc4-hdmi-1",
++ .max_pixel_clock = 600000000,
++ .registers = vc6_hdmi_hdmi1_fields,
++ .num_registers = ARRAY_SIZE(vc6_hdmi_hdmi1_fields),
++ .phy_lane_mapping = {
++ PHY_LANE_0,
++ PHY_LANE_1,
++ PHY_LANE_2,
++ PHY_LANE_CK,
++ },
++ .unsupported_odd_h_timings = true,
++ .external_irq_controller = true,
++
++ .init_resources = vc5_hdmi_init_resources,
++ .csc_setup = vc5_hdmi_csc_setup,
++ .reset = vc5_hdmi_reset,
++ .set_timings = vc5_hdmi_set_timings,
++ .phy_init = vc6_hdmi_phy_init,
++ .phy_disable = vc6_hdmi_phy_disable,
++ .channel_map = vc5_hdmi_channel_map,
++ .supports_hdr = true,
++ .hp_detect = vc5_hdmi_hp_detect,
++};
++
+ static const struct of_device_id vc4_hdmi_dt_match[] = {
+ { .compatible = "brcm,bcm2835-hdmi", .data = &bcm2835_variant },
+ { .compatible = "brcm,bcm2711-hdmi0", .data = &bcm2711_hdmi0_variant },
+ { .compatible = "brcm,bcm2711-hdmi1", .data = &bcm2711_hdmi1_variant },
++ { .compatible = "brcm,bcm2712-hdmi0", .data = &bcm2712_hdmi0_variant },
++ { .compatible = "brcm,bcm2712-hdmi1", .data = &bcm2712_hdmi1_variant },
+ {}
+ };
+
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
+@@ -284,4 +284,8 @@ void vc5_hdmi_phy_disable(struct vc4_hdm
+ void vc5_hdmi_phy_rng_enable(struct vc4_hdmi *vc4_hdmi);
+ void vc5_hdmi_phy_rng_disable(struct vc4_hdmi *vc4_hdmi);
+
++void vc6_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
++ struct vc4_hdmi_connector_state *vc4_conn_state);
++void vc6_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi);
++
+ #endif /* _VC4_HDMI_H_ */
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_phy.c
+@@ -125,6 +125,48 @@
+ #define VC4_HDMI_RM_FORMAT_SHIFT_SHIFT 24
+ #define VC4_HDMI_RM_FORMAT_SHIFT_MASK VC4_MASK(25, 24)
+
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BG_PWRUP BIT(8)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_LDO_PWRUP BIT(7)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BIAS_PWRUP BIT(6)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_RNDGEN_PWRUP BIT(4)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_CK_PWRUP BIT(3)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_2_PWRUP BIT(2)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_1_PWRUP BIT(1)
++#define VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_0_PWRUP BIT(0)
++
++#define VC6_HDMI_TX_PHY_PLL_REFCLK_REFCLK_SEL_CMOS BIT(13)
++#define VC6_HDMI_TX_PHY_PLL_REFCLK_REFFRQ_MASK VC4_MASK(9, 0)
++
++#define VC6_HDMI_TX_PHY_PLL_POST_KDIV_CLK0_SEL_MASK VC4_MASK(3, 2)
++#define VC6_HDMI_TX_PHY_PLL_POST_KDIV_KDIV_MASK VC4_MASK(1, 0)
++
++#define VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV_EN BIT(10)
++#define VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV_MASK VC4_MASK(9, 0)
++
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL_MASK VC4_MASK(31, 28)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE_MASK VC4_MASK(27, 27)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL_MASK VC4_MASK(26, 26)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN_MASK VC4_MASK(25, 25)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL_MASK VC4_MASK(24, 23)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN_MASK VC4_MASK(22, 22)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL_MASK VC4_MASK(21, 21)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN_MASK VC4_MASK(20, 20)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL_MASK VC4_MASK(19, 18)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN_MASK VC4_MASK(17, 17)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN_MASK VC4_MASK(16, 16)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL_MASK VC4_MASK(15, 12)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN_MASK VC4_MASK(11, 11)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT_MASK VC4_MASK(10, 8)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT_MASK VC4_MASK(7, 5)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING_MASK VC4_MASK(4, 3)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING_MASK VC4_MASK(2, 1)
++#define VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN_MASK VC4_MASK(0, 0)
++
++#define VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_PLLPOST_RESETB BIT(1)
++#define VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_RESETB BIT(0)
++
++#define VC6_HDMI_TX_PHY_PLL_POWERUP_CTL_PLL_PWRUP BIT(0)
++
+ #define OSCILLATOR_FREQUENCY 54000000
+
+ void vc4_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
+@@ -558,3 +600,601 @@ void vc5_hdmi_phy_rng_disable(struct vc4
+ VC4_HDMI_TX_PHY_POWERDOWN_CTL_RNDGEN_PWRDN);
+ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
+ }
++
++#define VC6_VCO_MIN_FREQ (8ULL * 1000 * 1000 * 1000)
++#define VC6_VCO_MAX_FREQ (12ULL * 1000 * 1000 * 1000)
++
++static unsigned long long
++vc6_phy_get_vco_freq(unsigned long long tmds_rate, unsigned int *vco_div)
++{
++ unsigned int min_div;
++ unsigned int max_div;
++ unsigned int div;
++
++ div = 0;
++ while (tmds_rate * div * 10 < VC6_VCO_MIN_FREQ)
++ div++;
++ min_div = div;
++
++ while (tmds_rate * (div + 1) * 10 < VC6_VCO_MAX_FREQ)
++ div++;
++ max_div = div;
++
++ div = min_div + (max_div - min_div) / 2;
++
++ *vco_div = div;
++ return tmds_rate * div * 10;
++}
++
++struct vc6_phy_lane_settings {
++ unsigned int ext_current_ctl:4;
++ unsigned int ffe_enable:1;
++ unsigned int slew_rate_ctl:1;
++ unsigned int ffe_post_tap_en:1;
++ unsigned int ldmos_bias_ctl:2;
++ unsigned int com_mode_ldmos_en:1;
++ unsigned int edge_sel:1;
++ unsigned int ext_current_src_hs_en:1;
++ unsigned int term_ctl:2;
++ unsigned int ext_current_src_en:1;
++ unsigned int int_current_src_en:1;
++ unsigned int int_current_ctl:4;
++ unsigned int int_current_src_hs_en:1;
++ unsigned int main_tap_current_select:3;
++ unsigned int post_tap_current_select:3;
++ unsigned int slew_ctl_slow_loading:2;
++ unsigned int slew_ctl_slow_driving:2;
++ unsigned int ffe_pre_tap_en:1;
++};
++
++struct vc6_phy_settings {
++ unsigned long long min_rate;
++ unsigned long long max_rate;
++ struct vc6_phy_lane_settings channel[3];
++ struct vc6_phy_lane_settings clock;
++};
++
++static const struct vc6_phy_settings vc6_hdmi_phy_settings[] = {
++ {
++ 0, 222000000,
++ {
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ },
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ },
++ {
++ 222000001, 297000000,
++ {
++ {
++ /* 200mA and 180mA ?! */
++ .ext_current_ctl = 12,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 100 Ohm */
++ .term_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++ },
++ {
++ /* 200mA and 180mA ?! */
++ .ext_current_ctl = 12,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 100 Ohm */
++ .term_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++ },
++ {
++ /* 200mA and 180mA ?! */
++ .ext_current_ctl = 12,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 100 Ohm */
++ .term_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++ },
++ },
++ {
++ /* 200mA and 180mA ?! */
++ .ext_current_ctl = 12,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 100 Ohm */
++ .term_ctl = 1,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++
++ /* Internal Current Source Half Swing Enable*/
++ .int_current_src_hs_en = 1,
++ },
++ },
++ {
++ 297000001, 597000044,
++ {
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* Normal Slew Rate Control */
++ .slew_rate_ctl = 1,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 50 Ohms */
++ .term_ctl = 3,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* Normal Slew Rate Control */
++ .slew_rate_ctl = 1,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 50 Ohms */
++ .term_ctl = 3,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* Normal Slew Rate Control */
++ .slew_rate_ctl = 1,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* 50 Ohms */
++ .term_ctl = 3,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ },
++ {
++ /* 200mA */
++ .ext_current_ctl = 8,
++
++ /* Normal Slew Rate Control */
++ .slew_rate_ctl = 1,
++
++ /* 0.85V */
++ .ldmos_bias_ctl = 1,
++
++ /* External Current Source Half Swing Enable*/
++ .ext_current_src_hs_en = 1,
++
++ /* 50 Ohms */
++ .term_ctl = 3,
++
++ /* Enable External Current Source */
++ .ext_current_src_en = 1,
++
++ /* Enable Internal Current Source */
++ .int_current_src_en = 1,
++
++ /* 200mA */
++ .int_current_ctl = 8,
++
++ /* Internal Current Source Half Swing Enable*/
++ .int_current_src_hs_en = 1,
++
++ /* 17.6 mA */
++ .main_tap_current_select = 7,
++ },
++ },
++};
++
++static const struct vc6_phy_settings *
++vc6_phy_get_settings(unsigned long long tmds_rate)
++{
++ unsigned int count = ARRAY_SIZE(vc6_hdmi_phy_settings);
++ unsigned int i;
++
++ for (i = 0; i < count; i++) {
++ const struct vc6_phy_settings *s = &vc6_hdmi_phy_settings[i];
++
++ if (tmds_rate >= s->min_rate && tmds_rate <= s->max_rate)
++ return s;
++ }
++
++ /*
++ * If the pixel clock exceeds our max setting, try the max
++ * setting anyway.
++ */
++ return &vc6_hdmi_phy_settings[count - 1];
++}
++
++static const struct vc6_phy_lane_settings *
++vc6_phy_get_channel_settings(enum vc4_hdmi_phy_channel chan,
++ unsigned long long tmds_rate)
++{
++ const struct vc6_phy_settings *settings = vc6_phy_get_settings(tmds_rate);
++
++ if (chan == PHY_LANE_CK)
++ return &settings->clock;
++
++ return &settings->channel[chan];
++}
++
++static void vc6_hdmi_reset_phy(struct vc4_hdmi *vc4_hdmi)
++{
++ lockdep_assert_held(&vc4_hdmi->hw_lock);
++
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0);
++ HDMI_WRITE(HDMI_TX_PHY_POWERUP_CTL, 0);
++}
++
++void vc6_hdmi_phy_init(struct vc4_hdmi *vc4_hdmi,
++ struct vc4_hdmi_connector_state *conn_state)
++{
++ const struct vc6_phy_lane_settings *chan0_settings;
++ const struct vc6_phy_lane_settings *chan1_settings;
++ const struct vc6_phy_lane_settings *chan2_settings;
++ const struct vc6_phy_lane_settings *clock_settings;
++ const struct vc4_hdmi_variant *variant = vc4_hdmi->variant;
++ unsigned long long pixel_freq = conn_state->tmds_char_rate;
++ unsigned long long vco_freq;
++ unsigned char word_sel;
++ unsigned long flags;
++ unsigned int vco_div;
++
++ vco_freq = vc6_phy_get_vco_freq(pixel_freq, &vco_div);
++
++ spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
++
++ vc6_hdmi_reset_phy(vc4_hdmi);
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_0, 0x810c6000);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_1, 0x00b8c451);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_2, 0x46402e31);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_3, 0x00b8c005);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_4, 0x42410261);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_5, 0xcc021001);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_6, 0xc8301c80);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_7, 0xb0804444);
++ HDMI_WRITE(HDMI_TX_PHY_PLL_MISC_8, 0xf80f8000);
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_REFCLK,
++ VC6_HDMI_TX_PHY_PLL_REFCLK_REFCLK_SEL_CMOS |
++ VC4_SET_FIELD(54, VC6_HDMI_TX_PHY_PLL_REFCLK_REFFRQ));
++
++ HDMI_WRITE(HDMI_TX_PHY_RESET_CTL, 0x7f);
++
++ HDMI_WRITE(HDMI_RM_OFFSET,
++ VC4_HDMI_RM_OFFSET_ONLY |
++ VC4_SET_FIELD(phy_get_rm_offset(vco_freq),
++ VC4_HDMI_RM_OFFSET_OFFSET));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_VCOCLK_DIV,
++ VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV_EN |
++ VC4_SET_FIELD(vco_div,
++ VC6_HDMI_TX_PHY_PLL_VCOCLK_DIV_VCODIV));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_CFG,
++ VC4_SET_FIELD(0, VC4_HDMI_TX_PHY_PLL_CFG_PDIV));
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_POST_KDIV,
++ VC4_SET_FIELD(2, VC6_HDMI_TX_PHY_PLL_POST_KDIV_CLK0_SEL) |
++ VC4_SET_FIELD(1, VC6_HDMI_TX_PHY_PLL_POST_KDIV_KDIV));
++
++ chan0_settings =
++ vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_0],
++ pixel_freq);
++ HDMI_WRITE(HDMI_TX_PHY_CTL_0,
++ VC4_SET_FIELD(chan0_settings->ext_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++ VC4_SET_FIELD(chan0_settings->ffe_enable,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++ VC4_SET_FIELD(chan0_settings->slew_rate_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++ VC4_SET_FIELD(chan0_settings->ffe_post_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++ VC4_SET_FIELD(chan0_settings->ldmos_bias_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++ VC4_SET_FIELD(chan0_settings->com_mode_ldmos_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++ VC4_SET_FIELD(chan0_settings->edge_sel,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++ VC4_SET_FIELD(chan0_settings->ext_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(chan0_settings->term_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++ VC4_SET_FIELD(chan0_settings->ext_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(chan0_settings->int_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(chan0_settings->int_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++ VC4_SET_FIELD(chan0_settings->int_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(chan0_settings->main_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(chan0_settings->post_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(chan0_settings->slew_ctl_slow_loading,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++ VC4_SET_FIELD(chan0_settings->slew_ctl_slow_driving,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++ VC4_SET_FIELD(chan0_settings->ffe_pre_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++ chan1_settings =
++ vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_1],
++ pixel_freq);
++ HDMI_WRITE(HDMI_TX_PHY_CTL_1,
++ VC4_SET_FIELD(chan1_settings->ext_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++ VC4_SET_FIELD(chan1_settings->ffe_enable,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++ VC4_SET_FIELD(chan1_settings->slew_rate_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++ VC4_SET_FIELD(chan1_settings->ffe_post_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++ VC4_SET_FIELD(chan1_settings->ldmos_bias_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++ VC4_SET_FIELD(chan1_settings->com_mode_ldmos_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++ VC4_SET_FIELD(chan1_settings->edge_sel,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++ VC4_SET_FIELD(chan1_settings->ext_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(chan1_settings->term_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++ VC4_SET_FIELD(chan1_settings->ext_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(chan1_settings->int_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(chan1_settings->int_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++ VC4_SET_FIELD(chan1_settings->int_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(chan1_settings->main_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(chan1_settings->post_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(chan1_settings->slew_ctl_slow_loading,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++ VC4_SET_FIELD(chan1_settings->slew_ctl_slow_driving,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++ VC4_SET_FIELD(chan1_settings->ffe_pre_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++ chan2_settings =
++ vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_2],
++ pixel_freq);
++ HDMI_WRITE(HDMI_TX_PHY_CTL_2,
++ VC4_SET_FIELD(chan2_settings->ext_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++ VC4_SET_FIELD(chan2_settings->ffe_enable,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++ VC4_SET_FIELD(chan2_settings->slew_rate_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++ VC4_SET_FIELD(chan2_settings->ffe_post_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++ VC4_SET_FIELD(chan2_settings->ldmos_bias_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++ VC4_SET_FIELD(chan2_settings->com_mode_ldmos_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++ VC4_SET_FIELD(chan2_settings->edge_sel,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++ VC4_SET_FIELD(chan2_settings->ext_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(chan2_settings->term_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++ VC4_SET_FIELD(chan2_settings->ext_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(chan2_settings->int_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(chan2_settings->int_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++ VC4_SET_FIELD(chan2_settings->int_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(chan2_settings->main_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(chan2_settings->post_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(chan2_settings->slew_ctl_slow_loading,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++ VC4_SET_FIELD(chan2_settings->slew_ctl_slow_driving,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++ VC4_SET_FIELD(chan2_settings->ffe_pre_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++ clock_settings =
++ vc6_phy_get_channel_settings(variant->phy_lane_mapping[PHY_LANE_CK],
++ pixel_freq);
++ HDMI_WRITE(HDMI_TX_PHY_CTL_CK,
++ VC4_SET_FIELD(clock_settings->ext_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_CTL) |
++ VC4_SET_FIELD(clock_settings->ffe_enable,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_ENABLE) |
++ VC4_SET_FIELD(clock_settings->slew_rate_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_RATE_CTL) |
++ VC4_SET_FIELD(clock_settings->ffe_post_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_POST_TAP_EN) |
++ VC4_SET_FIELD(clock_settings->ldmos_bias_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_LDMOS_BIAS_CTL) |
++ VC4_SET_FIELD(clock_settings->com_mode_ldmos_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_COM_MODE_LDMOS_EN) |
++ VC4_SET_FIELD(clock_settings->edge_sel,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EDGE_SEL) |
++ VC4_SET_FIELD(clock_settings->ext_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(clock_settings->term_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_TERM_CTL) |
++ VC4_SET_FIELD(clock_settings->ext_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_EXT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(clock_settings->int_current_src_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_EN) |
++ VC4_SET_FIELD(clock_settings->int_current_ctl,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_CTL) |
++ VC4_SET_FIELD(clock_settings->int_current_src_hs_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_INT_CURRENT_SRC_HS_EN) |
++ VC4_SET_FIELD(clock_settings->main_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_MAIN_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(clock_settings->post_tap_current_select,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_POST_TAP_CURRENT_SELECT) |
++ VC4_SET_FIELD(clock_settings->slew_ctl_slow_loading,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_LOADING) |
++ VC4_SET_FIELD(clock_settings->slew_ctl_slow_driving,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_SLEW_CTL_SLOW_DRIVING) |
++ VC4_SET_FIELD(clock_settings->ffe_pre_tap_en,
++ VC6_HDMI_TX_PHY_HDMI_CTRL_CHX_FFE_PRE_TAP_EN));
++
++ if (pixel_freq >= 340000000)
++ word_sel = 3;
++ else
++ word_sel = 0;
++ HDMI_WRITE(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, word_sel);
++
++ HDMI_WRITE(HDMI_TX_PHY_POWERUP_CTL,
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BG_PWRUP |
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_LDO_PWRUP |
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_BIAS_PWRUP |
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_CK_PWRUP |
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_2_PWRUP |
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_1_PWRUP |
++ VC6_HDMI_TX_PHY_HDMI_POWERUP_CTL_TX_0_PWRUP);
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_POWERUP_CTL,
++ VC6_HDMI_TX_PHY_PLL_POWERUP_CTL_PLL_PWRUP);
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_RESET_CTL,
++ HDMI_READ(HDMI_TX_PHY_PLL_RESET_CTL) &
++ ~VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_RESETB);
++
++ HDMI_WRITE(HDMI_TX_PHY_PLL_RESET_CTL,
++ HDMI_READ(HDMI_TX_PHY_PLL_RESET_CTL) |
++ VC6_HDMI_TX_PHY_PLL_RESET_CTL_PLL_RESETB);
++
++ spin_unlock_irqrestore(&vc4_hdmi->hw_lock, flags);
++}
++
++void vc6_hdmi_phy_disable(struct vc4_hdmi *vc4_hdmi)
++{
++}
+--- a/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_hdmi_regs.h
+@@ -111,13 +111,30 @@ enum vc4_hdmi_field {
+ HDMI_TX_PHY_CTL_1,
+ HDMI_TX_PHY_CTL_2,
+ HDMI_TX_PHY_CTL_3,
++ HDMI_TX_PHY_CTL_CK,
+ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_1,
+ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_2,
+ HDMI_TX_PHY_PLL_CALIBRATION_CONFIG_4,
+ HDMI_TX_PHY_PLL_CFG,
++ HDMI_TX_PHY_PLL_CFG_PDIV,
+ HDMI_TX_PHY_PLL_CTL_0,
+ HDMI_TX_PHY_PLL_CTL_1,
++ HDMI_TX_PHY_PLL_MISC_0,
++ HDMI_TX_PHY_PLL_MISC_1,
++ HDMI_TX_PHY_PLL_MISC_2,
++ HDMI_TX_PHY_PLL_MISC_3,
++ HDMI_TX_PHY_PLL_MISC_4,
++ HDMI_TX_PHY_PLL_MISC_5,
++ HDMI_TX_PHY_PLL_MISC_6,
++ HDMI_TX_PHY_PLL_MISC_7,
++ HDMI_TX_PHY_PLL_MISC_8,
++ HDMI_TX_PHY_PLL_POST_KDIV,
++ HDMI_TX_PHY_PLL_POWERUP_CTL,
++ HDMI_TX_PHY_PLL_REFCLK,
++ HDMI_TX_PHY_PLL_RESET_CTL,
++ HDMI_TX_PHY_PLL_VCOCLK_DIV,
+ HDMI_TX_PHY_POWERDOWN_CTL,
++ HDMI_TX_PHY_POWERUP_CTL,
+ HDMI_TX_PHY_RESET_CTL,
+ HDMI_TX_PHY_TMDS_CLK_WORD_SEL,
+ HDMI_VEC_INTERFACE_CFG,
+@@ -383,6 +400,206 @@ static const struct vc4_hdmi_register __
+
+ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
+ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
++ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
++
++ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
++
++ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++
++ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
++ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
++ VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
++ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
++ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
++ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
++ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++ VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c),
++};
++
++static const struct vc4_hdmi_register __maybe_unused vc6_hdmi_hdmi0_fields[] = {
++ VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
++ VC4_HD_REG(HDMI_MAI_CTL, 0x0010),
++ VC4_HD_REG(HDMI_MAI_THR, 0x0014),
++ VC4_HD_REG(HDMI_MAI_FMT, 0x0018),
++ VC4_HD_REG(HDMI_MAI_DATA, 0x001c),
++ VC4_HD_REG(HDMI_MAI_SMP, 0x0020),
++ VC4_HD_REG(HDMI_VID_CTL, 0x0044),
++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0060),
++
++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x07c),
++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0c0),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0c4),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0cc),
++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0d0),
++ VC4_HDMI_REG(HDMI_CTS_0, 0x0d4),
++ VC4_HDMI_REG(HDMI_CTS_1, 0x0d8),
++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e8),
++ VC4_HDMI_REG(HDMI_HORZA, 0x0ec),
++ VC4_HDMI_REG(HDMI_HORZB, 0x0f0),
++ VC4_HDMI_REG(HDMI_VERTA0, 0x0f4),
++ VC4_HDMI_REG(HDMI_VERTB0, 0x0f8),
++ VC4_HDMI_REG(HDMI_VERTA1, 0x100),
++ VC4_HDMI_REG(HDMI_VERTB1, 0x104),
++ VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x114),
++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0a4),
++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a8),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_1, 0x148),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_2, 0x14c),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_3, 0x150),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_4, 0x158),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_5, 0x15c),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_6, 0x160),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_7, 0x164),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_8, 0x168),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_9, 0x16c),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_10, 0x170),
++ VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x18c),
++ VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x194),
++ VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x198),
++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1c8),
++ VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1e4),
++
++ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0f0),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f4),
++
++ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
++ VC5_PHY_REG(HDMI_TX_PHY_POWERUP_CTL, 0x004),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_CK, 0x014),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_REFCLK, 0x01c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_POST_KDIV, 0x028),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_VCOCLK_DIV, 0x02c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x044),
++ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x054),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_0, 0x060),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_1, 0x064),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_2, 0x068),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_3, 0x06c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_4, 0x070),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_5, 0x074),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_6, 0x078),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_7, 0x07c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_8, 0x080),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_RESET_CTL, 0x190),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_POWERUP_CTL, 0x194),
++
++ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
++ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
++ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
++
++ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
++
++ VC5_CEC_REG(HDMI_CEC_CNTRL_1, 0x010),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_2, 0x014),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_3, 0x018),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_4, 0x01c),
++ VC5_CEC_REG(HDMI_CEC_CNTRL_5, 0x020),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_1, 0x028),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_2, 0x02c),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_3, 0x030),
++ VC5_CEC_REG(HDMI_CEC_TX_DATA_4, 0x034),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_1, 0x038),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_2, 0x03c),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_3, 0x040),
++ VC5_CEC_REG(HDMI_CEC_RX_DATA_4, 0x044),
++
++ VC5_CSC_REG(HDMI_CSC_CTL, 0x000),
++ VC5_CSC_REG(HDMI_CSC_12_11, 0x004),
++ VC5_CSC_REG(HDMI_CSC_14_13, 0x008),
++ VC5_CSC_REG(HDMI_CSC_22_21, 0x00c),
++ VC5_CSC_REG(HDMI_CSC_24_23, 0x010),
++ VC5_CSC_REG(HDMI_CSC_32_31, 0x014),
++ VC5_CSC_REG(HDMI_CSC_34_33, 0x018),
++ VC5_CSC_REG(HDMI_CSC_CHANNEL_CTL, 0x02c),
++};
++
++static const struct vc4_hdmi_register __maybe_unused vc6_hdmi_hdmi1_fields[] = {
++ VC4_HD_REG(HDMI_DVP_CTL, 0x0000),
++ VC4_HD_REG(HDMI_MAI_CTL, 0x0030),
++ VC4_HD_REG(HDMI_MAI_THR, 0x0034),
++ VC4_HD_REG(HDMI_MAI_FMT, 0x0038),
++ VC4_HD_REG(HDMI_MAI_DATA, 0x003c),
++ VC4_HD_REG(HDMI_MAI_SMP, 0x0040),
++ VC4_HD_REG(HDMI_VID_CTL, 0x0048),
++ VC4_HD_REG(HDMI_FRAME_COUNT, 0x0064),
++
++ VC4_HDMI_REG(HDMI_FIFO_CTL, 0x07c),
++ VC4_HDMI_REG(HDMI_AUDIO_PACKET_CONFIG, 0x0c0),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_CONFIG, 0x0c4),
++ VC4_HDMI_REG(HDMI_RAM_PACKET_STATUS, 0x0cc),
++ VC4_HDMI_REG(HDMI_CRP_CFG, 0x0d0),
++ VC4_HDMI_REG(HDMI_CTS_0, 0x0d4),
++ VC4_HDMI_REG(HDMI_CTS_1, 0x0d8),
++ VC4_HDMI_REG(HDMI_SCHEDULER_CONTROL, 0x0e8),
++ VC4_HDMI_REG(HDMI_HORZA, 0x0ec),
++ VC4_HDMI_REG(HDMI_HORZB, 0x0f0),
++ VC4_HDMI_REG(HDMI_VERTA0, 0x0f4),
++ VC4_HDMI_REG(HDMI_VERTB0, 0x0f8),
++ VC4_HDMI_REG(HDMI_VERTA1, 0x100),
++ VC4_HDMI_REG(HDMI_VERTB1, 0x104),
++ VC4_HDMI_REG(HDMI_MISC_CONTROL, 0x114),
++ VC4_HDMI_REG(HDMI_MAI_CHANNEL_MAP, 0x0a4),
++ VC4_HDMI_REG(HDMI_MAI_CONFIG, 0x0a8),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_1, 0x148),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_2, 0x14c),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_3, 0x150),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_4, 0x158),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_5, 0x15c),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_6, 0x160),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_7, 0x164),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_8, 0x168),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_9, 0x16c),
++ VC4_HDMI_REG(HDMI_FORMAT_DET_10, 0x170),
++ VC4_HDMI_REG(HDMI_DEEP_COLOR_CONFIG_1, 0x18c),
++ VC4_HDMI_REG(HDMI_GCP_CONFIG, 0x194),
++ VC4_HDMI_REG(HDMI_GCP_WORD_1, 0x198),
++ VC4_HDMI_REG(HDMI_HOTPLUG, 0x1c8),
++ VC4_HDMI_REG(HDMI_SCRAMBLER_CTL, 0x1e4),
++
++ VC5_DVP_REG(HDMI_CLOCK_STOP, 0x0bc),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_CFG, 0x0f0),
++ VC5_DVP_REG(HDMI_VEC_INTERFACE_XBAR, 0x0f4),
++
++ VC5_PHY_REG(HDMI_TX_PHY_RESET_CTL, 0x000),
++ VC5_PHY_REG(HDMI_TX_PHY_POWERUP_CTL, 0x004),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_0, 0x008),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_1, 0x00c),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_2, 0x010),
++ VC5_PHY_REG(HDMI_TX_PHY_CTL_CK, 0x014),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_REFCLK, 0x01c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_POST_KDIV, 0x028),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_VCOCLK_DIV, 0x02c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_CFG, 0x044),
++ VC5_PHY_REG(HDMI_TX_PHY_TMDS_CLK_WORD_SEL, 0x054),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_0, 0x060),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_1, 0x064),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_2, 0x068),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_3, 0x06c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_4, 0x070),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_5, 0x074),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_6, 0x078),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_7, 0x07c),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_MISC_8, 0x080),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_RESET_CTL, 0x190),
++ VC5_PHY_REG(HDMI_TX_PHY_PLL_POWERUP_CTL, 0x194),
++
++ VC5_RM_REG(HDMI_RM_CONTROL, 0x000),
++ VC5_RM_REG(HDMI_RM_OFFSET, 0x018),
+ VC5_RM_REG(HDMI_RM_FORMAT, 0x01c),
+
+ VC5_RAM_REG(HDMI_RAM_PACKET_START, 0x000),
--- /dev/null
+From a68e8ffc3314612b0d00d491c8cdd61ae1d9af4e Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 25 Apr 2023 10:12:32 +0200
+Subject: [PATCH] drm/vc4: txp: Introduce structure to deal with revision
+ differences
+
+The BCM2712 will have several TXP with small differences. Let's add a
+structure tied to the compatible to deal with those differences.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 4 ++--
+ drivers/gpu/drm/vc4/vc4_drv.h | 6 +++++-
+ drivers/gpu/drm/vc4/vc4_txp.c | 23 ++++++++++++++++-------
+ 3 files changed, 23 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -51,7 +51,7 @@ struct vc4_mock_desc {
+
+ static const struct vc4_mock_desc vc4_mock =
+ VC4_MOCK_DESC(
+- VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
++ VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+@@ -77,7 +77,7 @@ static const struct vc4_mock_desc vc4_mo
+
+ static const struct vc4_mock_desc vc5_mock =
+ VC4_MOCK_DESC(
+- VC4_MOCK_CRTC_DESC(&vc4_txp_crtc_data,
++ VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -561,7 +561,11 @@ struct vc4_crtc_data {
+ int hvs_output;
+ };
+
+-extern const struct vc4_crtc_data vc4_txp_crtc_data;
++struct vc4_txp_data {
++ struct vc4_crtc_data base;
++};
++
++extern const struct vc4_txp_data vc4_txp_data;
+
+ struct vc4_pv_data {
+ struct vc4_crtc_data base;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -159,6 +159,7 @@
+
+ struct vc4_txp {
+ struct vc4_crtc base;
++ const struct vc4_txp_data *data;
+
+ struct platform_device *pdev;
+
+@@ -488,17 +489,20 @@ static irqreturn_t vc4_txp_interrupt(int
+ return IRQ_HANDLED;
+ }
+
+-const struct vc4_crtc_data vc4_txp_crtc_data = {
+- .name = "txp",
+- .debugfs_name = "txp_regs",
+- .hvs_available_channels = BIT(2),
+- .hvs_output = 2,
++const struct vc4_txp_data vc4_txp_data = {
++ .base = {
++ .name = "txp",
++ .debugfs_name = "txp_regs",
++ .hvs_available_channels = BIT(2),
++ .hvs_output = 2,
++ },
+ };
+
+ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
+ {
+ struct platform_device *pdev = to_platform_device(dev);
+ struct drm_device *drm = dev_get_drvdata(master);
++ const struct vc4_txp_data *txp_data;
+ struct vc4_encoder *vc4_encoder;
+ struct drm_encoder *encoder;
+ struct vc4_crtc *vc4_crtc;
+@@ -513,6 +517,11 @@ static int vc4_txp_bind(struct device *d
+ if (!txp)
+ return -ENOMEM;
+
++ txp_data = of_device_get_match_data(dev);
++ if (!txp_data)
++ return -ENODEV;
++
++ txp->data = txp_data;
+ txp->pdev = pdev;
+ txp->regs = vc4_ioremap_regs(pdev, 0);
+ if (IS_ERR(txp->regs))
+@@ -523,7 +532,7 @@ static int vc4_txp_bind(struct device *d
+ vc4_crtc->regset.regs = txp_regs;
+ vc4_crtc->regset.nregs = ARRAY_SIZE(txp_regs);
+
+- ret = vc4_crtc_init(drm, pdev, vc4_crtc, &vc4_txp_crtc_data,
++ ret = vc4_crtc_init(drm, pdev, vc4_crtc, &txp_data->base,
+ &vc4_txp_crtc_funcs, &vc4_txp_crtc_helper_funcs, true);
+ if (ret)
+ return ret;
+@@ -584,7 +593,7 @@ static int vc4_txp_remove(struct platfor
+ }
+
+ static const struct of_device_id vc4_txp_dt_match[] = {
+- { .compatible = "brcm,bcm2835-txp" },
++ { .compatible = "brcm,bcm2835-txp", .data = &vc4_txp_data },
+ { /* sentinel */ },
+ };
+
--- /dev/null
+From 7d345345b70f00bf4c673a68da7d1cf0faf5cc47 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 25 Apr 2023 10:21:53 +0200
+Subject: [PATCH] drm/vc4: txp: Rename TXP data structure
+
+The TXP data structure has a name too generic for the multiple variants
+we'll have to support. Let's rename it to mention the SoC it applies to.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 4 ++--
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 +-
+ drivers/gpu/drm/vc4/vc4_txp.c | 4 ++--
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -51,7 +51,7 @@ struct vc4_mock_desc {
+
+ static const struct vc4_mock_desc vc4_mock =
+ VC4_MOCK_DESC(
+- VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
++ VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+@@ -77,7 +77,7 @@ static const struct vc4_mock_desc vc4_mo
+
+ static const struct vc4_mock_desc vc5_mock =
+ VC4_MOCK_DESC(
+- VC4_MOCK_CRTC_DESC(&vc4_txp_data.base,
++ VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -565,7 +565,7 @@ struct vc4_txp_data {
+ struct vc4_crtc_data base;
+ };
+
+-extern const struct vc4_txp_data vc4_txp_data;
++extern const struct vc4_txp_data bcm2835_txp_data;
+
+ struct vc4_pv_data {
+ struct vc4_crtc_data base;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -489,7 +489,7 @@ static irqreturn_t vc4_txp_interrupt(int
+ return IRQ_HANDLED;
+ }
+
+-const struct vc4_txp_data vc4_txp_data = {
++const struct vc4_txp_data bcm2835_txp_data = {
+ .base = {
+ .name = "txp",
+ .debugfs_name = "txp_regs",
+@@ -593,7 +593,7 @@ static int vc4_txp_remove(struct platfor
+ }
+
+ static const struct of_device_id vc4_txp_dt_match[] = {
+- { .compatible = "brcm,bcm2835-txp", .data = &vc4_txp_data },
++ { .compatible = "brcm,bcm2835-txp", .data = &bcm2835_txp_data },
+ { /* sentinel */ },
+ };
+
--- /dev/null
+From e8dbad6d506b6fac992fdf74a7e3a66a38e554c3 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 09:30:33 +0200
+Subject: [PATCH] drm/vc4: txp: Add byte enable toggle bit
+
+The MOPLET doesn't have the BYTE_ENABLE field to set, but the TXP and
+MOP do, so let's add a boolean to control whether or not we need to set
+it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 6 +++++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -563,6 +563,7 @@ struct vc4_crtc_data {
+
+ struct vc4_txp_data {
+ struct vc4_crtc_data base;
++ unsigned int has_byte_enable:1;
+ };
+
+ extern const struct vc4_txp_data bcm2835_txp_data;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -291,6 +291,7 @@ static void vc4_txp_connector_atomic_com
+ struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state,
+ conn);
+ struct vc4_txp *txp = connector_to_vc4_txp(conn);
++ const struct vc4_txp_data *txp_data = txp->data;
+ struct drm_gem_dma_object *gem;
+ struct drm_display_mode *mode;
+ struct drm_framebuffer *fb;
+@@ -313,9 +314,11 @@ static void vc4_txp_connector_atomic_com
+ return;
+
+ ctrl = TXP_GO | TXP_EI |
+- VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE) |
+ VC4_SET_FIELD(txp_fmts[i], TXP_FORMAT);
+
++ if (txp_data->has_byte_enable)
++ ctrl |= VC4_SET_FIELD(0xf, TXP_BYTE_ENABLE);
++
+ if (fb->format->has_alpha)
+ ctrl |= TXP_ALPHA_ENABLE;
+ else
+@@ -496,6 +499,7 @@ const struct vc4_txp_data bcm2835_txp_da
+ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
+ },
++ .has_byte_enable = true,
+ };
+
+ static int vc4_txp_bind(struct device *dev, struct device *master, void *data)
--- /dev/null
+From a51e4acdab01540e1006e43f38e5befb40002de0 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 09:47:54 +0200
+Subject: [PATCH] drm/vc4: txp: Add horizontal and vertical size offset toggle
+ bit
+
+The new writeback controllers that can be found on the BCM2712 require
+to have their horizontal and vertical size reduced by one.
+
+Let's tie that behaviour to the compatible so we can support both the
+new and old controllers.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 14 ++++++++++++--
+ 2 files changed, 13 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -564,6 +564,7 @@ struct vc4_crtc_data {
+ struct vc4_txp_data {
+ struct vc4_crtc_data base;
+ unsigned int has_byte_enable:1;
++ unsigned int size_minus_one:1;
+ };
+
+ extern const struct vc4_txp_data bcm2835_txp_data;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -295,6 +295,8 @@ static void vc4_txp_connector_atomic_com
+ struct drm_gem_dma_object *gem;
+ struct drm_display_mode *mode;
+ struct drm_framebuffer *fb;
++ unsigned int hdisplay;
++ unsigned int vdisplay;
+ u32 ctrl;
+ int idx;
+ int i;
+@@ -334,9 +336,17 @@ static void vc4_txp_connector_atomic_com
+ gem = drm_fb_dma_get_gem_obj(fb, 0);
+ TXP_WRITE(TXP_DST_PTR, gem->dma_addr + fb->offsets[0]);
+ TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
++
++ hdisplay = mode->hdisplay ?: 1;
++ vdisplay = mode->vdisplay ?: 1;
++ if (txp_data->size_minus_one) {
++ hdisplay -= 1;
++ vdisplay -= 1;
++ }
++
+ TXP_WRITE(TXP_DIM,
+- VC4_SET_FIELD(mode->hdisplay, TXP_WIDTH) |
+- VC4_SET_FIELD(mode->vdisplay, TXP_HEIGHT));
++ VC4_SET_FIELD(hdisplay, TXP_WIDTH) |
++ VC4_SET_FIELD(vdisplay, TXP_HEIGHT));
+
+ TXP_WRITE(TXP_DST_CTRL, ctrl);
+
--- /dev/null
+From ddb9aa80692ed5d35e4ee4688c36789620f78c5c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 17:47:11 +0200
+Subject: [PATCH] drm/vc4: txp: Handle 40-bits DMA Addresses
+
+The BCM2712 MOP and MOPLET can handle addresses larger than 32bits
+through an extra register. We can easily support it and make it
+conditional based on the compatible through a boolean in our variant
+structure.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 10 +++++++++-
+ 2 files changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -565,6 +565,7 @@ struct vc4_txp_data {
+ struct vc4_crtc_data base;
+ unsigned int has_byte_enable:1;
+ unsigned int size_minus_one:1;
++ unsigned int supports_40bit_addresses:1;
+ };
+
+ extern const struct vc4_txp_data bcm2835_txp_data;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -145,6 +145,8 @@
+ /* Number of lines received and committed to memory. */
+ #define TXP_PROGRESS 0x10
+
++#define TXP_DST_PTR_HIGH 0x1c
++
+ #define TXP_READ(offset) \
+ ({ \
+ kunit_fail_current_test("Accessing a register in a unit test!\n"); \
+@@ -297,6 +299,7 @@ static void vc4_txp_connector_atomic_com
+ struct drm_framebuffer *fb;
+ unsigned int hdisplay;
+ unsigned int vdisplay;
++ dma_addr_t addr;
+ u32 ctrl;
+ int idx;
+ int i;
+@@ -334,7 +337,12 @@ static void vc4_txp_connector_atomic_com
+ return;
+
+ gem = drm_fb_dma_get_gem_obj(fb, 0);
+- TXP_WRITE(TXP_DST_PTR, gem->dma_addr + fb->offsets[0]);
++ addr = gem->dma_addr + fb->offsets[0];
++ TXP_WRITE(TXP_DST_PTR, lower_32_bits(addr));
++
++ if (txp_data->supports_40bit_addresses)
++ TXP_WRITE(TXP_DST_PTR_HIGH, upper_32_bits(addr) & 0xff);
++
+ TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
+
+ hdisplay = mode->hdisplay ?: 1;
--- /dev/null
+From 2e8f4fa23af4bb794e9b2284a53aa40bbfdd3cbb Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 11:26:10 +0200
+Subject: [PATCH] drm/vc4: txp: Move the encoder type in the variant structure
+
+We'll have multiple TXP instances in the BCM2712, so we can't use a
+single encoder type anymore. Let's tie the encoder type to the
+compatible.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 3 ++-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -563,6 +563,7 @@ struct vc4_crtc_data {
+
+ struct vc4_txp_data {
+ struct vc4_crtc_data base;
++ enum vc4_encoder_type encoder_type;
+ unsigned int has_byte_enable:1;
+ unsigned int size_minus_one:1;
+ unsigned int supports_40bit_addresses:1;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -517,6 +517,7 @@ const struct vc4_txp_data bcm2835_txp_da
+ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
+ },
++ .encoder_type = VC4_ENCODER_TYPE_TXP,
+ .has_byte_enable = true,
+ };
+
+@@ -560,7 +561,7 @@ static int vc4_txp_bind(struct device *d
+ return ret;
+
+ vc4_encoder = &txp->encoder;
+- txp->encoder.type = VC4_ENCODER_TYPE_TXP;
++ txp->encoder.type = txp_data->encoder_type;
+
+ encoder = &vc4_encoder->base;
+ encoder->possible_crtcs = drm_crtc_mask(&vc4_crtc->base);
--- /dev/null
+From 68a00ca7b1d7809ac7be736c02238c142e629127 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 11:49:28 +0200
+Subject: [PATCH] drm/vc4: txp: Add a new TXP encoder type
+
+Starting with BCM2712, we'll have a two TXP. Let's follow the HDMI
+example and add two encoder types for TXP: TXP0 and TXP1.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 4 +-
+ .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 106 +++++++++---------
+ drivers/gpu/drm/vc4/vc4_drv.h | 3 +-
+ drivers/gpu/drm/vc4/vc4_kms.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_txp.c | 2 +-
+ 5 files changed, 59 insertions(+), 58 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -52,7 +52,7 @@ struct vc4_mock_desc {
+ static const struct vc4_mock_desc vc4_mock =
+ VC4_MOCK_DESC(
+ VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+- VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
++ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP0,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2835_pv0_data,
+@@ -78,7 +78,7 @@ static const struct vc4_mock_desc vc4_mo
+ static const struct vc4_mock_desc vc5_mock =
+ VC4_MOCK_DESC(
+ VC4_MOCK_CRTC_DESC(&bcm2835_txp_data.base,
+- VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP,
++ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP0,
+ DRM_MODE_ENCODER_VIRTUAL,
+ DRM_MODE_CONNECTOR_WRITEBACK)),
+ VC4_MOCK_PIXELVALVE_DESC(&bcm2711_pv0_data,
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -91,7 +91,7 @@ static const struct encoder_constraint v
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 1),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
+- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 2),
++ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP0, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 2),
+ };
+
+@@ -99,7 +99,7 @@ static const struct encoder_constraint v
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DPI, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI0, 0),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_VEC, 1),
+- ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP, 0, 2),
++ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP0, 0, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_DSI1, 0, 1, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0, 1, 2),
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2),
+@@ -208,7 +208,7 @@ static const struct pv_muxing_param vc4_
+ VC4_PV_MUXING_TEST("1 output: DSI1",
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("1 output: TXP",
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("2 outputs: DSI0, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0),
+@@ -220,7 +220,7 @@ static const struct pv_muxing_param vc4_
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: DSI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("2 outputs: DPI, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0),
+@@ -232,19 +232,19 @@ static const struct pv_muxing_param vc4_
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: DPI, TXP",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("2 outputs: HDMI0, DSI1",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
+ VC4_ENCODER_TYPE_HDMI0,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("2 outputs: VEC, DSI1",
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("2 outputs: VEC, TXP",
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+@@ -252,7 +252,7 @@ static const struct pv_muxing_param vc4_
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, HDMI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+@@ -260,7 +260,7 @@ static const struct pv_muxing_param vc4_
+ VC4_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+@@ -268,7 +268,7 @@ static const struct pv_muxing_param vc4_
+ VC4_PV_MUXING_TEST("3 outputs: DPI, HDMI0, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+@@ -276,7 +276,7 @@ static const struct pv_muxing_param vc4_
+ VC4_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ };
+
+ KUNIT_ARRAY_PARAM(vc4_test_pv_muxing,
+@@ -288,7 +288,7 @@ static const struct pv_muxing_param vc4_
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_DSI0),
+ VC4_PV_MUXING_TEST("TXP/DSI1 Conflict",
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC4_PV_MUXING_TEST("HDMI0/VEC Conflict",
+ VC4_ENCODER_TYPE_HDMI0,
+@@ -297,22 +297,22 @@ static const struct pv_muxing_param vc4_
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DPI, HDMI0, DSI1, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_DSI1,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC4_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+ VC4_ENCODER_TYPE_DSI1,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ };
+
+ KUNIT_ARRAY_PARAM(vc4_test_pv_muxing_invalid,
+@@ -343,7 +343,7 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, TXP",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("2 outputs: DPI, VEC",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC),
+@@ -361,7 +361,7 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("2 outputs: DSI0, VEC",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC),
+@@ -373,7 +373,7 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: DSI1, TXP",
+ VC4_ENCODER_TYPE_DSI1,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("2 outputs: DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+@@ -385,7 +385,7 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI0, TXP",
+ VC4_ENCODER_TYPE_HDMI0,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+@@ -394,14 +394,14 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("2 outputs: HDMI1, TXP",
+ VC4_ENCODER_TYPE_HDMI1,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("2 outputs: TXP, VEC",
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_VEC),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, TXP",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, VEC, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+@@ -416,15 +416,15 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DPI, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+@@ -441,7 +441,7 @@ static const struct pv_muxing_param vc5_
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, TXP",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP),
++ VC4_ENCODER_TYPE_TXP0),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, VEC, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+@@ -456,15 +456,15 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("3 outputs: DSI0, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+@@ -491,17 +491,17 @@ static const struct pv_muxing_param vc5_
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+@@ -520,17 +520,17 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, DSI1, HDMI0, HDMI1",
+@@ -541,19 +541,19 @@ static const struct pv_muxing_param vc5_
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, DSI1, HDMI0, HDMI1",
+@@ -564,24 +564,24 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+@@ -600,17 +600,17 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, DSI1, HDMI0, HDMI1",
+@@ -621,19 +621,19 @@ static const struct pv_muxing_param vc5_
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, DSI1, HDMI0, HDMI1",
+@@ -644,27 +644,27 @@ static const struct pv_muxing_param vc5_
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: VEC, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DPI, VEC, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DPI,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+ VC5_PV_MUXING_TEST("More than 3 outputs: DSI0, VEC, TXP, DSI1, HDMI0, HDMI1",
+ VC4_ENCODER_TYPE_DSI0,
+ VC4_ENCODER_TYPE_VEC,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_HDMI0,
+ VC4_ENCODER_TYPE_HDMI1),
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -511,7 +511,8 @@ enum vc4_encoder_type {
+ VC4_ENCODER_TYPE_DSI1,
+ VC4_ENCODER_TYPE_SMI,
+ VC4_ENCODER_TYPE_DPI,
+- VC4_ENCODER_TYPE_TXP,
++ VC4_ENCODER_TYPE_TXP0,
++ VC4_ENCODER_TYPE_TXP1,
+ };
+
+ struct vc4_encoder {
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -359,7 +359,7 @@ static void vc6_hvs_pv_muxing_commit(str
+ mux = 0;
+ break;
+
+- case VC4_ENCODER_TYPE_TXP:
++ case VC4_ENCODER_TYPE_TXP0:
+ mux = 2;
+ break;
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -517,7 +517,7 @@ const struct vc4_txp_data bcm2835_txp_da
+ .hvs_available_channels = BIT(2),
+ .hvs_output = 2,
+ },
+- .encoder_type = VC4_ENCODER_TYPE_TXP,
++ .encoder_type = VC4_ENCODER_TYPE_TXP0,
+ .has_byte_enable = true,
+ };
+
--- /dev/null
+From 831c6e8c68a66678e8329a382823dc83c483dcc8 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Thu, 27 Apr 2023 09:30:49 +0200
+Subject: [PATCH] drm/vc4: txp: Add support for BCM2712 MOP
+
+The BCM2712 has an evolution of what used to be called TXP in the
+earlier SoCs, but is now called MOP.
+
+There's a few differences still, so we can add a new compatible to deal
+with them easily.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 18 +++++++++++++++++-
+ 1 file changed, 17 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -388,6 +388,7 @@ static const struct drm_connector_funcs
+ static void vc4_txp_encoder_disable(struct drm_encoder *encoder)
+ {
+ struct drm_device *drm = encoder->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct vc4_txp *txp = encoder_to_vc4_txp(encoder);
+ int idx;
+
+@@ -406,7 +407,8 @@ static void vc4_txp_encoder_disable(stru
+ WARN_ON(TXP_READ(TXP_DST_CTRL) & TXP_BUSY);
+ }
+
+- TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN);
++ if (vc4->gen < VC4_GEN_6)
++ TXP_WRITE(TXP_DST_CTRL, TXP_POWERDOWN);
+
+ drm_dev_exit(idx);
+ }
+@@ -510,6 +512,19 @@ static irqreturn_t vc4_txp_interrupt(int
+ return IRQ_HANDLED;
+ }
+
++const struct vc4_txp_data bcm2712_mop_data = {
++ .base = {
++ .name = "mop",
++ .debugfs_name = "mop_regs",
++ .hvs_available_channels = BIT(2),
++ .hvs_output = 2,
++ },
++ .encoder_type = VC4_ENCODER_TYPE_TXP0,
++ .has_byte_enable = true,
++ .size_minus_one = true,
++ .supports_40bit_addresses = true,
++};
++
+ const struct vc4_txp_data bcm2835_txp_data = {
+ .base = {
+ .name = "txp",
+@@ -616,6 +631,7 @@ static int vc4_txp_remove(struct platfor
+ }
+
+ static const struct of_device_id vc4_txp_dt_match[] = {
++ { .compatible = "brcm,bcm2712-mop", .data = &bcm2712_mop_data },
+ { .compatible = "brcm,bcm2835-txp", .data = &bcm2835_txp_data },
+ { /* sentinel */ },
+ };
--- /dev/null
+From b239fc6a68fea2b073c4cb48884fbb697014ac2b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Mon, 20 Feb 2023 17:16:01 +0100
+Subject: [PATCH] drm/vc4: txp: Add BCM2712 MOPLET support
+
+The BCM2712 features a simpler TXP called MOPLET. Let's add support for
+it.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_txp.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -525,6 +525,18 @@ const struct vc4_txp_data bcm2712_mop_da
+ .supports_40bit_addresses = true,
+ };
+
++const struct vc4_txp_data bcm2712_moplet_data = {
++ .base = {
++ .name = "moplet",
++ .debugfs_name = "moplet_regs",
++ .hvs_available_channels = BIT(1),
++ .hvs_output = 4,
++ },
++ .encoder_type = VC4_ENCODER_TYPE_TXP1,
++ .size_minus_one = true,
++ .supports_40bit_addresses = true,
++};
++
+ const struct vc4_txp_data bcm2835_txp_data = {
+ .base = {
+ .name = "txp",
+@@ -632,6 +644,7 @@ static int vc4_txp_remove(struct platfor
+
+ static const struct of_device_id vc4_txp_dt_match[] = {
+ { .compatible = "brcm,bcm2712-mop", .data = &bcm2712_mop_data },
++ { .compatible = "brcm,bcm2712-moplet", .data = &bcm2712_moplet_data },
+ { .compatible = "brcm,bcm2835-txp", .data = &bcm2835_txp_data },
+ { /* sentinel */ },
+ };
--- /dev/null
+From bb05ccd66342643b1cd9a0a48cec3ebdc3eed511 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Tue, 21 Feb 2023 14:38:32 +0100
+Subject: [PATCH] drm/vc4: Add additional warn_on
+
+Some code path in vc4 are conditional to a generation and cannot be
+executed on others. Let's put a WARN_ON if that ever happens.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 32 ++++++++++++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_kms.c | 6 ++++++
+ drivers/gpu/drm/vc4/vc4_plane.c | 19 +++++++++++++++++++
+ 3 files changed, 55 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -417,12 +417,15 @@ static int vc4_hvs_upload_linear_kernel(
+ static void vc4_hvs_lut_load(struct vc4_hvs *hvs,
+ struct vc4_crtc *vc4_crtc)
+ {
+- struct drm_device *drm = &hvs->vc4->base;
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
+ struct drm_crtc *crtc = &vc4_crtc->base;
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state);
+ int idx;
+ u32 i;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+@@ -758,6 +761,8 @@ u8 vc4_hvs_get_fifo_frame_count(struct v
+ u8 field = 0;
+ int idx;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+ if (!drm_dev_enter(drm, &idx))
+ return 0;
+
+@@ -791,6 +796,8 @@ int vc4_hvs_get_fifo_from_output(struct
+ u32 reg;
+ int ret;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+ switch (vc4->gen) {
+ case VC4_GEN_4:
+ return output;
+@@ -880,6 +887,8 @@ static int vc4_hvs_init_channel(struct v
+ u32 dispctrl;
+ int idx;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+ if (!drm_dev_enter(drm, &idx))
+ return -ENODEV;
+
+@@ -947,6 +956,8 @@ static int vc6_hvs_init_channel(struct v
+ u32 disp_ctrl1;
+ int idx;
+
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_6);
++
+ if (!drm_dev_enter(drm, &idx))
+ return -ENODEV;
+
+@@ -972,9 +983,12 @@ static int vc6_hvs_init_channel(struct v
+
+ static void __vc4_hvs_stop_channel(struct vc4_hvs *hvs, unsigned int chan)
+ {
+- struct drm_device *drm = &hvs->vc4->base;
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
+ int idx;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+@@ -1007,6 +1021,8 @@ static void __vc6_hvs_stop_channel(struc
+ struct drm_device *drm = &vc4->base;
+ int idx;
+
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_6);
++
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+@@ -1234,6 +1250,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ bool found = false;
+ int idx;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+ if (!drm_dev_enter(dev, &idx)) {
+ vc4_crtc_send_vblank(crtc);
+ return;
+@@ -1324,6 +1342,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ if (crtc->state->color_mgmt_changed) {
+ u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(channel));
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+ if (crtc->state->gamma_lut) {
+ if (vc4->gen == VC4_GEN_4) {
+ vc4_hvs_update_gamma_lut(hvs, vc4_crtc);
+@@ -1363,6 +1383,8 @@ void vc4_hvs_mask_underrun(struct vc4_hv
+ u32 dispctrl;
+ int idx;
+
++ WARN_ON(vc4->gen > VC4_GEN_5);
++
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+@@ -1383,6 +1405,8 @@ void vc4_hvs_unmask_underrun(struct vc4_
+ u32 dispctrl;
+ int idx;
+
++ WARN_ON(vc4->gen > VC4_GEN_5);
++
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+@@ -1417,6 +1441,8 @@ static irqreturn_t vc4_hvs_irq_handler(i
+ u32 status;
+ u32 dspeislur;
+
++ WARN_ON(vc4->gen > VC4_GEN_5);
++
+ /*
+ * NOTE: We don't need to protect the register access using
+ * drm_dev_enter() there because the interrupt handler lifetime
+@@ -1466,6 +1492,8 @@ static irqreturn_t vc6_hvs_eof_irq_handl
+ struct vc4_hvs *hvs = vc4->hvs;
+ unsigned int i;
+
++ WARN_ON(vc4->gen < VC4_GEN_6);
++
+ for (i = 0; i < HVS_NUM_CHANNELS; i++) {
+ if (!hvs->eof_irq[i].enabled)
+ continue;
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -147,6 +147,8 @@ vc4_ctm_commit(struct vc4_dev *vc4, stru
+ if (vc4->firmware_kms)
+ return;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_5);
++
+ if (ctm_state->fifo) {
+ HVS_WRITE(SCALER_OLEDCOEF2,
+ VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]),
+@@ -222,6 +224,8 @@ static void vc4_hvs_pv_muxing_commit(str
+ struct drm_crtc *crtc;
+ unsigned int i;
+
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_4);
++
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+@@ -265,6 +269,8 @@ static void vc5_hvs_pv_muxing_commit(str
+ unsigned int i;
+ u32 reg;
+
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_5);
++
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state);
+ struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -555,8 +555,11 @@ static int vc4_plane_setup_clipping_and_
+
+ static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
+ {
++ struct vc4_dev *vc4 = to_vc4_dev(vc4_state->base.plane->dev);
+ u32 scale, recip;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+ scale = src / dst;
+
+ /* The specs note that while the reciprocal would be defined
+@@ -581,10 +584,13 @@ static void vc4_write_tpz(struct vc4_pla
+
+ static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst, u32 xy, int channel, int chroma_offset)
+ {
++ struct vc4_dev *vc4 = to_vc4_dev(vc4_state->base.plane->dev);
+ u32 scale = src / dst;
+ s32 offset, offset2;
+ s32 phase;
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+ /* Start the phase at 1/2 pixel from the 1st pixel at src_x.
+ 1/4 pixel for YUV, plus the offset for chroma siting */
+ if (channel) {
+@@ -801,8 +807,11 @@ static size_t vc6_upm_size(const struct
+ static void vc4_write_scaling_parameters(struct drm_plane_state *state,
+ int channel)
+ {
++ struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
+ /* Ch0 H-PPF Word 0: Scaling Parameters */
+ if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
+ vc4_write_ppf(vc4_state,
+@@ -1040,6 +1049,11 @@ static const u32 colorspace_coeffs[2][DR
+
+ static u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state)
+ {
++ struct drm_device *dev = state->state->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(dev);
++
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_4);
++
+ if (!state->fb->format->has_alpha)
+ return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED,
+ SCALER_POS2_ALPHA_MODE);
+@@ -1061,6 +1075,11 @@ static u32 vc4_hvs4_get_alpha_blend_mode
+
+ static u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state)
+ {
++ struct drm_device *dev = state->state->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(dev);
++
++ WARN_ON_ONCE(vc4->gen != VC4_GEN_5 && vc4->gen != VC4_GEN_6);
++
+ if (!state->fb->format->has_alpha)
+ return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
+ SCALER5_CTL2_ALPHA_MODE);
--- /dev/null
+From 336917ca87807b8a4bb08855b4dcb0477289c765 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:35:16 +0100
+Subject: [PATCH] drm/vc4: tests: Switch generation mockup to a switch
+
+Testing whether the VideoCore generation we want to mock is vc5 or vc4
+worked so far, but will be difficult to extend to support BCM2712 (VC6).
+
+Convert to a switch.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 18 ++++++++++++++++--
+ 1 file changed, 16 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -155,13 +155,27 @@ static int __build_mock(struct kunit *te
+
+ static struct vc4_dev *__mock_device(struct kunit *test, enum vc4_gen gen)
+ {
++ const struct vc4_mock_desc *desc;
++ const struct drm_driver *drv;
+ struct drm_device *drm;
+- const struct drm_driver *drv = (gen == VC4_GEN_5) ? &vc5_drm_driver : &vc4_drm_driver;
+- const struct vc4_mock_desc *desc = (gen == VC4_GEN_5) ? &vc5_mock : &vc4_mock;
+ struct vc4_dev *vc4;
+ struct device *dev;
+ int ret;
+
++ switch (gen) {
++ case VC4_GEN_4:
++ drv = &vc4_drm_driver;
++ desc = &vc4_mock;
++ break;
++ case VC4_GEN_5:
++ drv = &vc5_drm_driver;
++ desc = &vc5_mock;
++ break;
++
++ default:
++ return NULL;
++ }
++
+ dev = drm_kunit_helper_alloc_device(test);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev);
+
--- /dev/null
+From 70e906d3c688491e181446afa27bea32ce241d6a Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 09:58:15 +0100
+Subject: [PATCH] drm/vc4: tests: Drop drm parameter for
+ vc4_find_crtc_for_encoder
+
+The DRM device pointer and the DRM encoder pointer are redundant, since
+the latter is attached to the former and we can just follow the
+drm_encoder->dev pointer.
+
+Let's remove the drm_device pointer argument.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h | 2 +-
+ drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 4 ++--
+ drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 2 +-
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -7,9 +7,9 @@
+
+ static inline
+ struct drm_crtc *vc4_find_crtc_for_encoder(struct kunit *test,
+- struct drm_device *drm,
+ struct drm_encoder *encoder)
+ {
++ struct drm_device *drm = encoder->dev;
+ struct drm_crtc *crtc;
+
+ KUNIT_ASSERT_EQ(test, hweight32(encoder->possible_crtcs), 1);
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
+@@ -77,7 +77,7 @@ int vc4_mock_atomic_add_output(struct ku
+ encoder = vc4_find_encoder_by_type(drm, type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+
+- crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
++ crtc = vc4_find_crtc_for_encoder(test, encoder);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ output = container_of(encoder, struct vc4_dummy_output, encoder.base);
+@@ -115,7 +115,7 @@ int vc4_mock_atomic_del_output(struct ku
+ encoder = vc4_find_encoder_by_type(drm, type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+
+- crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
++ crtc = vc4_find_crtc_for_encoder(test, encoder);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -132,7 +132,7 @@ get_vc4_crtc_state_for_encoder(struct ku
+ encoder = vc4_find_encoder_by_type(drm, type);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, encoder);
+
+- crtc = vc4_find_crtc_for_encoder(test, drm, encoder);
++ crtc = vc4_find_crtc_for_encoder(test, encoder);
+ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
+
+ new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
--- /dev/null
+From 14e97c5765579eaab3c8372701750ffa30e4c7da Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 24 Mar 2023 10:02:59 +0100
+Subject: [PATCH] drm/vc4: tests: Return the allocated output
+
+Some tests will need to retrieve the output that was just allocated by
+vc4_mock_atomic_add_output().
+
+Instead of making them look them up in the DRM device, we can simply
+make vc4_mock_atomic_add_output() return an error pointer that holds the
+allocated output instead of the error code.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h | 7 ++--
+ drivers/gpu/drm/vc4/tests/vc4_mock_output.c | 9 +++--
+ .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 37 +++++++++++--------
+ 3 files changed, 30 insertions(+), 23 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -53,9 +53,10 @@ struct vc4_dummy_output *vc4_dummy_outpu
+ struct vc4_dev *vc4_mock_device(struct kunit *test);
+ struct vc4_dev *vc5_mock_device(struct kunit *test);
+
+-int vc4_mock_atomic_add_output(struct kunit *test,
+- struct drm_atomic_state *state,
+- enum vc4_encoder_type type);
++struct vc4_dummy_output *
++vc4_mock_atomic_add_output(struct kunit *test,
++ struct drm_atomic_state *state,
++ enum vc4_encoder_type type);
+ int vc4_mock_atomic_del_output(struct kunit *test,
+ struct drm_atomic_state *state,
+ enum vc4_encoder_type type);
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_output.c
+@@ -61,9 +61,10 @@ static const struct drm_display_mode def
+ DRM_SIMPLE_MODE(640, 480, 64, 48)
+ };
+
+-int vc4_mock_atomic_add_output(struct kunit *test,
+- struct drm_atomic_state *state,
+- enum vc4_encoder_type type)
++struct vc4_dummy_output *
++vc4_mock_atomic_add_output(struct kunit *test,
++ struct drm_atomic_state *state,
++ enum vc4_encoder_type type)
+ {
+ struct drm_device *drm = state->dev;
+ struct drm_connector_state *conn_state;
+@@ -96,7 +97,7 @@ int vc4_mock_atomic_add_output(struct ku
+
+ crtc_state->active = true;
+
+- return 0;
++ return output;
+ }
+
+ int vc4_mock_atomic_del_output(struct kunit *test,
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -683,10 +683,11 @@ static void drm_vc4_test_pv_muxing(struc
+ int ret;
+
+ for (i = 0; i < params->nencoders; i++) {
++ struct vc4_dummy_output *output;
+ enum vc4_encoder_type enc_type = params->encoders[i];
+
+- ret = vc4_mock_atomic_add_output(test, state, enc_type);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, enc_type);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+ }
+
+ ret = drm_atomic_check_only(state);
+@@ -712,10 +713,11 @@ static void drm_vc4_test_pv_muxing_inval
+ int ret;
+
+ for (i = 0; i < params->nencoders; i++) {
++ struct vc4_dummy_output *output;
+ enum vc4_encoder_type enc_type = params->encoders[i];
+
+- ret = vc4_mock_atomic_add_output(test, state, enc_type);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, enc_type);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+ }
+
+ ret = drm_atomic_check_only(state);
+@@ -804,6 +806,7 @@ static void drm_test_vc5_pv_muxing_bugs_
+ {
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
++ struct vc4_dummy_output *output;
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct vc4_hvs_state *new_hvs_state;
+ unsigned int hdmi0_channel;
+@@ -823,8 +826,8 @@ static void drm_test_vc5_pv_muxing_bugs_
+
+ state->acquire_ctx = &ctx;
+
+- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -850,8 +853,8 @@ static void drm_test_vc5_pv_muxing_bugs_
+
+ state->acquire_ctx = &ctx;
+
+- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -880,6 +883,7 @@ static void drm_test_vc5_pv_muxing_bugs_
+ {
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
++ struct vc4_dummy_output *output;
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct vc4_hvs_state *new_hvs_state;
+ unsigned int old_hdmi0_channel;
+@@ -899,11 +903,11 @@ static void drm_test_vc5_pv_muxing_bugs_
+
+ state->acquire_ctx = &ctx;
+
+- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+
+- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -971,6 +975,7 @@ drm_test_vc5_pv_muxing_bugs_subsequent_c
+ {
+ struct drm_modeset_acquire_ctx ctx;
+ struct drm_atomic_state *state;
++ struct vc4_dummy_output *output;
+ struct vc4_crtc_state *new_vc4_crtc_state;
+ struct drm_device *drm;
+ struct vc4_dev *vc4;
+@@ -987,8 +992,8 @@ drm_test_vc5_pv_muxing_bugs_subsequent_c
+
+ state->acquire_ctx = &ctx;
+
+- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+@@ -1003,8 +1008,8 @@ drm_test_vc5_pv_muxing_bugs_subsequent_c
+
+ state->acquire_ctx = &ctx;
+
+- ret = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
+- KUNIT_ASSERT_EQ(test, ret, 0);
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI1);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
+
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
--- /dev/null
+From 04bec005b049604f862765b35ebd71c2a69b9e7c Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 17 Feb 2023 13:38:10 +0100
+Subject: [PATCH] drm/vc4: tests: Add BCM2712 mock driver
+
+The BCM2712 has a simpler pipeline that can only output to a writeback
+connector and two HDMI controllers.
+
+Let's allow our kunit tests to create a mock of that pipeline.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.c | 29 ++++++++++++++++++++++++++++
+ drivers/gpu/drm/vc4/tests/vc4_mock.h | 1 +
+ drivers/gpu/drm/vc4/vc4_drv.h | 2 ++
+ 3 files changed, 32 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.c
+@@ -106,6 +106,26 @@ static const struct vc4_mock_desc vc5_mo
+ DRM_MODE_CONNECTOR_HDMIA)),
+ );
+
++static const struct vc4_mock_desc vc6_mock =
++ VC4_MOCK_DESC(
++ VC4_MOCK_CRTC_DESC(&bcm2712_mop_data.base,
++ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP0,
++ DRM_MODE_ENCODER_VIRTUAL,
++ DRM_MODE_CONNECTOR_WRITEBACK)),
++ VC4_MOCK_CRTC_DESC(&bcm2712_moplet_data.base,
++ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_TXP1,
++ DRM_MODE_ENCODER_VIRTUAL,
++ DRM_MODE_CONNECTOR_WRITEBACK)),
++ VC4_MOCK_PIXELVALVE_DESC(&bcm2712_pv0_data,
++ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI0,
++ DRM_MODE_ENCODER_TMDS,
++ DRM_MODE_CONNECTOR_HDMIA)),
++ VC4_MOCK_PIXELVALVE_DESC(&bcm2712_pv1_data,
++ VC4_MOCK_OUTPUT_DESC(VC4_ENCODER_TYPE_HDMI1,
++ DRM_MODE_ENCODER_TMDS,
++ DRM_MODE_CONNECTOR_HDMIA)),
++);
++
+ static int __build_one_pipe(struct kunit *test, struct drm_device *drm,
+ const struct vc4_mock_pipe_desc *pipe)
+ {
+@@ -171,6 +191,10 @@ static struct vc4_dev *__mock_device(str
+ drv = &vc5_drm_driver;
+ desc = &vc5_mock;
+ break;
++ case VC4_GEN_6:
++ drv = &vc5_drm_driver;
++ desc = &vc6_mock;
++ break;
+
+ default:
+ return NULL;
+@@ -212,3 +236,8 @@ struct vc4_dev *vc5_mock_device(struct k
+ {
+ return __mock_device(test, VC4_GEN_5);
+ }
++
++struct vc4_dev *vc6_mock_device(struct kunit *test)
++{
++ return __mock_device(test, VC4_GEN_6);
++}
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -52,6 +52,7 @@ struct vc4_dummy_output *vc4_dummy_outpu
+
+ struct vc4_dev *vc4_mock_device(struct kunit *test);
+ struct vc4_dev *vc5_mock_device(struct kunit *test);
++struct vc4_dev *vc6_mock_device(struct kunit *test);
+
+ struct vc4_dummy_output *
+ vc4_mock_atomic_add_output(struct kunit *test,
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -570,6 +570,8 @@ struct vc4_txp_data {
+ unsigned int supports_40bit_addresses:1;
+ };
+
++extern const struct vc4_txp_data bcm2712_mop_data;
++extern const struct vc4_txp_data bcm2712_moplet_data;
+ extern const struct vc4_txp_data bcm2835_txp_data;
+
+ struct vc4_pv_data {
--- /dev/null
+From 77c14764ae164b7969e65437a87aca25b30d8b80 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:14:22 +0200
+Subject: [PATCH] drm/vc4: tests: Add tests for BCM2712 PixelValve Muxing
+
+The BCM2712 has a simpler pipeline than the BCM2711, and thus the muxing
+requirements are different. Create some tests to make sure we get proper
+muxing decisions.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ .../gpu/drm/vc4/tests/vc4_test_pv_muxing.c | 81 +++++++++++++++++++
+ 1 file changed, 81 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_pv_muxing.c
+@@ -105,6 +105,13 @@ static const struct encoder_constraint v
+ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 0, 1, 2),
+ };
+
++static const struct encoder_constraint vc6_encoder_constraints[] = {
++ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI0, 0),
++ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_HDMI1, 1),
++ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP1, 1),
++ ENCODER_CONSTRAINT(VC4_ENCODER_TYPE_TXP0, 2),
++};
++
+ static bool check_vc4_encoder_constraints(enum vc4_encoder_type type, unsigned int channel)
+ {
+ return __check_encoder_constraints(vc4_encoder_constraints,
+@@ -119,6 +126,13 @@ static bool check_vc5_encoder_constraint
+ type, channel);
+ }
+
++static bool check_vc6_encoder_constraints(enum vc4_encoder_type type, unsigned int channel)
++{
++ return __check_encoder_constraints(vc6_encoder_constraints,
++ ARRAY_SIZE(vc6_encoder_constraints),
++ type, channel);
++}
++
+ static struct vc4_crtc_state *
+ get_vc4_crtc_state_for_encoder(struct kunit *test,
+ const struct drm_atomic_state *state,
+@@ -196,6 +210,9 @@ static void vc4_test_pv_muxing_desc(cons
+ #define VC5_PV_MUXING_TEST(_name, ...) \
+ PV_MUXING_TEST(_name, vc5_mock_device, check_vc5_encoder_constraints, __VA_ARGS__)
+
++#define VC6_PV_MUXING_TEST(_name, ...) \
++ PV_MUXING_TEST(_name, vc6_mock_device, check_vc6_encoder_constraints, __VA_ARGS__)
++
+ static const struct pv_muxing_param vc4_test_pv_muxing_params[] = {
+ VC4_PV_MUXING_TEST("1 output: DSI0",
+ VC4_ENCODER_TYPE_DSI0),
+@@ -674,6 +691,54 @@ KUNIT_ARRAY_PARAM(vc5_test_pv_muxing_inv
+ vc5_test_pv_muxing_invalid_params,
+ vc4_test_pv_muxing_desc);
+
++static const struct pv_muxing_param vc6_test_pv_muxing_params[] = {
++ VC6_PV_MUXING_TEST("1 output: HDMI0",
++ VC4_ENCODER_TYPE_HDMI0),
++ VC6_PV_MUXING_TEST("1 output: HDMI1",
++ VC4_ENCODER_TYPE_HDMI1),
++ VC6_PV_MUXING_TEST("1 output: MOPLET",
++ VC4_ENCODER_TYPE_TXP1),
++ VC6_PV_MUXING_TEST("1 output: MOP",
++ VC4_ENCODER_TYPE_TXP0),
++ VC6_PV_MUXING_TEST("2 outputs: HDMI0, HDMI1",
++ VC4_ENCODER_TYPE_HDMI0,
++ VC4_ENCODER_TYPE_HDMI1),
++ VC6_PV_MUXING_TEST("2 outputs: HDMI0, MOPLET",
++ VC4_ENCODER_TYPE_HDMI0,
++ VC4_ENCODER_TYPE_TXP1),
++ VC6_PV_MUXING_TEST("2 outputs: HDMI0, MOP",
++ VC4_ENCODER_TYPE_HDMI0,
++ VC4_ENCODER_TYPE_TXP0),
++ VC6_PV_MUXING_TEST("2 outputs: HDMI1, MOP",
++ VC4_ENCODER_TYPE_HDMI1,
++ VC4_ENCODER_TYPE_TXP0),
++ VC6_PV_MUXING_TEST("2 outputs: MOPLET, MOP",
++ VC4_ENCODER_TYPE_TXP1,
++ VC4_ENCODER_TYPE_TXP0),
++ VC6_PV_MUXING_TEST("3 outputs: HDMI0, HDMI1, MOP",
++ VC4_ENCODER_TYPE_HDMI0,
++ VC4_ENCODER_TYPE_HDMI1,
++ VC4_ENCODER_TYPE_TXP0),
++ VC6_PV_MUXING_TEST("3 outputs: HDMI0, MOPLET, MOP",
++ VC4_ENCODER_TYPE_HDMI0,
++ VC4_ENCODER_TYPE_TXP1,
++ VC4_ENCODER_TYPE_TXP0),
++};
++
++KUNIT_ARRAY_PARAM(vc6_test_pv_muxing,
++ vc6_test_pv_muxing_params,
++ vc4_test_pv_muxing_desc);
++
++static const struct pv_muxing_param vc6_test_pv_muxing_invalid_params[] = {
++ VC6_PV_MUXING_TEST("HDMI1/MOPLET Conflict",
++ VC4_ENCODER_TYPE_HDMI1,
++ VC4_ENCODER_TYPE_TXP1),
++};
++
++KUNIT_ARRAY_PARAM(vc6_test_pv_muxing_invalid,
++ vc6_test_pv_muxing_invalid_params,
++ vc4_test_pv_muxing_desc);
++
+ static void drm_vc4_test_pv_muxing(struct kunit *test)
+ {
+ const struct pv_muxing_param *params = test->param_value;
+@@ -797,6 +862,21 @@ static struct kunit_suite vc5_pv_muxing_
+ .test_cases = vc5_pv_muxing_tests,
+ };
+
++static struct kunit_case vc6_pv_muxing_tests[] = {
++ KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing,
++ vc6_test_pv_muxing_gen_params),
++ KUNIT_CASE_PARAM(drm_vc4_test_pv_muxing_invalid,
++ vc6_test_pv_muxing_invalid_gen_params),
++ {}
++};
++
++static struct kunit_suite vc6_pv_muxing_test_suite = {
++ .name = "vc6-pv-muxing-combinations",
++ .init = vc4_pv_muxing_test_init,
++ .exit = vc4_pv_muxing_test_exit,
++ .test_cases = vc6_pv_muxing_tests,
++};
++
+ /* See
+ * https://lore.kernel.org/all/3e113525-aa89-b1e2-56b7-ca55bd41d057@samsung.com/
+ * and
+@@ -1040,5 +1120,6 @@ static struct kunit_suite vc5_pv_muxing_
+ kunit_test_suites(
+ &vc4_pv_muxing_test_suite,
+ &vc5_pv_muxing_test_suite,
++ &vc6_pv_muxing_test_suite,
+ &vc5_pv_muxing_bugs_test_suite
+ );
--- /dev/null
+From 3d849ab48cecab55862a4f2742f11937d66ba54b Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:21:34 +0200
+Subject: [PATCH] drm/vc4: fkms: Rename plane related functions
+
+The name collide with the Full KMS functions that are going to be made
+public.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -670,8 +670,8 @@ static int vc4_plane_to_mb(struct drm_pl
+ return 0;
+ }
+
+-static int vc4_plane_atomic_check(struct drm_plane *plane,
+- struct drm_atomic_state *state)
++static int vc4_fkms_plane_atomic_check(struct drm_plane *plane,
++ struct drm_atomic_state *state)
+ {
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+ plane);
+@@ -728,7 +728,7 @@ static int vc4_plane_atomic_async_check(
+ }
+
+ /* Called during init to allocate the plane's atomic state. */
+-static void vc4_plane_reset(struct drm_plane *plane)
++static void vc4_fkms_plane_reset(struct drm_plane *plane)
+ {
+ struct vc4_plane_state *vc4_state;
+
+@@ -788,7 +788,7 @@ static bool vc4_fkms_format_mod_supporte
+ }
+ }
+
+-static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
++static struct drm_plane_state *vc4_fkms_plane_duplicate_state(struct drm_plane *plane)
+ {
+ struct vc4_plane_state *vc4_state;
+
+@@ -809,8 +809,8 @@ static const struct drm_plane_funcs vc4_
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = vc4_plane_destroy,
+ .set_property = NULL,
+- .reset = vc4_plane_reset,
+- .atomic_duplicate_state = vc4_plane_duplicate_state,
++ .reset = vc4_fkms_plane_reset,
++ .atomic_duplicate_state = vc4_fkms_plane_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+ .format_mod_supported = vc4_fkms_format_mod_supported,
+ };
+@@ -818,7 +818,7 @@ static const struct drm_plane_funcs vc4_
+ static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
+ .prepare_fb = drm_gem_plane_helper_prepare_fb,
+ .cleanup_fb = NULL,
+- .atomic_check = vc4_plane_atomic_check,
++ .atomic_check = vc4_fkms_plane_atomic_check,
+ .atomic_update = vc4_plane_atomic_update,
+ .atomic_disable = vc4_plane_atomic_disable,
+ .atomic_async_check = vc4_plane_atomic_async_check,
--- /dev/null
+From 14fe42ff341741d60ba338c401855dee7fb68754 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:24:37 +0200
+Subject: [PATCH] drm/vc4: tests: Use custom plane state for mock
+
+The current mock planes were just using the regular drm_plane_state,
+while the driver expect struct vc4_plane_state that subclasses
+drm_plane_state.
+
+Hook the proper implementations of reset, duplicate_state, destroy and
+atomic_check to create vc4_plane_state.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 7 ++++---
+ drivers/gpu/drm/vc4/vc4_drv.h | 6 ++++++
+ drivers/gpu/drm/vc4/vc4_plane.c | 12 ++++++------
+ 3 files changed, 16 insertions(+), 9 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+@@ -10,12 +10,13 @@
+ #include "vc4_mock.h"
+
+ static const struct drm_plane_helper_funcs vc4_dummy_plane_helper_funcs = {
++ .atomic_check = vc4_plane_atomic_check,
+ };
+
+ static const struct drm_plane_funcs vc4_dummy_plane_funcs = {
+- .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
+- .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
+- .reset = drm_atomic_helper_plane_reset,
++ .atomic_destroy_state = vc4_plane_destroy_state,
++ .atomic_duplicate_state = vc4_plane_duplicate_state,
++ .reset = vc4_plane_reset,
+ };
+
+ static const uint32_t vc4_dummy_plane_formats[] = {
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -1145,6 +1145,12 @@ int vc4_kms_load(struct drm_device *dev)
+ struct drm_plane *vc4_plane_init(struct drm_device *dev,
+ enum drm_plane_type type,
+ uint32_t possible_crtcs);
++void vc4_plane_reset(struct drm_plane *plane);
++void vc4_plane_destroy_state(struct drm_plane *plane,
++ struct drm_plane_state *state);
++struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane);
++int vc4_plane_atomic_check(struct drm_plane *plane,
++ struct drm_atomic_state *state);
+ int vc4_plane_create_additional_planes(struct drm_device *dev);
+ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist);
+ u32 vc4_plane_dlist_size(const struct drm_plane_state *state);
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -276,7 +276,7 @@ static bool plane_enabled(struct drm_pla
+ return state->fb && !WARN_ON(!state->crtc);
+ }
+
+-static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
++struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
+ {
+ struct vc4_plane_state *vc4_state;
+ unsigned int i;
+@@ -312,8 +312,8 @@ static struct drm_plane_state *vc4_plane
+ return &vc4_state->base;
+ }
+
+-static void vc4_plane_destroy_state(struct drm_plane *plane,
+- struct drm_plane_state *state)
++void vc4_plane_destroy_state(struct drm_plane *plane,
++ struct drm_plane_state *state)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+ struct vc4_hvs *hvs = vc4->hvs;
+@@ -348,7 +348,7 @@ static void vc4_plane_destroy_state(stru
+ }
+
+ /* Called during init to allocate the plane's atomic state. */
+-static void vc4_plane_reset(struct drm_plane *plane)
++void vc4_plane_reset(struct drm_plane *plane)
+ {
+ struct vc4_plane_state *vc4_state;
+
+@@ -2000,8 +2000,8 @@ static int vc6_plane_mode_set(struct drm
+ * compute the dlist here and have all active plane dlists get updated
+ * in the CRTC's flush.
+ */
+-static int vc4_plane_atomic_check(struct drm_plane *plane,
+- struct drm_atomic_state *state)
++int vc4_plane_atomic_check(struct drm_plane *plane,
++ struct drm_atomic_state *state)
+ {
+ struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
--- /dev/null
+From 3320e449e40eeb49b601dcbbd4bd72b8cb8f3054 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 11:26:58 +0200
+Subject: [PATCH] drm/vc4: tests: Add function to lookup a plane for a CRTC
+
+Some tests will need to find a plane to run a test on for a given CRTC.
+Let's create a small helper to do that.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -21,6 +21,20 @@ struct drm_crtc *vc4_find_crtc_for_encod
+ return NULL;
+ }
+
++static inline
++struct drm_plane *vc4_mock_find_plane_for_crtc(struct kunit *test,
++ struct drm_crtc *crtc)
++{
++ struct drm_device *drm = crtc->dev;
++ struct drm_plane *plane;
++
++ drm_for_each_plane(plane, drm)
++ if (plane->possible_crtcs & drm_crtc_mask(crtc))
++ return plane;
++
++ return NULL;
++}
++
+ struct vc4_dummy_plane {
+ struct vc4_plane plane;
+ };
--- /dev/null
+From a2f912c44b98acb6c10c977db105e199011c09b5 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 12:57:53 +0200
+Subject: [PATCH] drm/vc4: tests: Add helper to add a new plane to a state
+
+We'll start to add some tests for the plane state logic, so let's create
+a helper to add a plane to an existing atomic state.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock.h | 4 ++++
+ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 22 ++++++++++++++++++++++
+ 2 files changed, 26 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock.h
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock.h
+@@ -42,6 +42,10 @@ struct vc4_dummy_plane {
+ struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
+ struct drm_device *drm,
+ enum drm_plane_type type);
++struct drm_plane *
++vc4_mock_atomic_add_plane(struct kunit *test,
++ struct drm_atomic_state *state,
++ struct drm_crtc *crtc);
+
+ struct vc4_dummy_crtc {
+ struct vc4_crtc crtc;
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0
+
+ #include <drm/drm_atomic_state_helper.h>
++#include <drm/drm_atomic_uapi.h>
+ #include <drm/drm_fourcc.h>
+ #include <drm/drm_modeset_helper_vtables.h>
+ #include <drm/drm_plane.h>
+@@ -46,3 +47,24 @@ struct vc4_dummy_plane *vc4_dummy_plane(
+
+ return dummy_plane;
+ }
++
++struct drm_plane *
++vc4_mock_atomic_add_plane(struct kunit *test,
++ struct drm_atomic_state *state,
++ struct drm_crtc *crtc)
++{
++ struct drm_plane_state *plane_state;
++ struct drm_plane *plane;
++ int ret;
++
++ plane = vc4_mock_find_plane_for_crtc(test, crtc);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
++
++ plane_state = drm_atomic_get_plane_state(state, plane);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_state);
++
++ ret = drm_atomic_set_crtc_for_plane(plane_state, crtc);
++ KUNIT_EXPECT_EQ(test, ret, 0);
++
++ return plane;
++}
--- /dev/null
+From 418d2e0e652c3870c29dd2e462f052a88fa027da Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 12:59:05 +0200
+Subject: [PATCH] drm/vc4: tests: Support a few more plane formats
+
+We'll start testing our planes code in situations where we will use more
+than XRGB8888, so let's add a few common pixel formats.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/tests/vc4_mock_plane.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_mock_plane.c
+@@ -21,7 +21,10 @@ static const struct drm_plane_funcs vc4_
+ };
+
+ static const uint32_t vc4_dummy_plane_formats[] = {
++ DRM_FORMAT_ARGB8888,
+ DRM_FORMAT_XRGB8888,
++ DRM_FORMAT_YUV420,
++ DRM_FORMAT_YUV422,
+ };
+
+ struct vc4_dummy_plane *vc4_dummy_plane(struct kunit *test,
--- /dev/null
+From 39ae36d19bf6e59e3091f8f0ec6f4eac59aaf6f2 Mon Sep 17 00:00:00 2001
+From: Maxime Ripard <maxime@cerno.tech>
+Date: Fri, 14 Apr 2023 13:43:32 +0200
+Subject: [PATCH] drm/vc4: tests: Introduce a test for LBM buffer size
+
+The BCM2712 comes with a different LBM size computation than the
+previous generations, so let's add the few examples provided as kunit
+tests to make sure we always satisfy those requirements.
+
+Signed-off-by: Maxime Ripard <maxime@cerno.tech>
+---
+ drivers/gpu/drm/vc4/Makefile | 3 +-
+ drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c | 327 ++++++++++++++++++
+ 2 files changed, 329 insertions(+), 1 deletion(-)
+ create mode 100644 drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
+
+--- a/drivers/gpu/drm/vc4/Makefile
++++ b/drivers/gpu/drm/vc4/Makefile
+@@ -31,7 +31,8 @@ vc4-$(CONFIG_DRM_VC4_KUNIT_TEST) += \
+ tests/vc4_mock_crtc.o \
+ tests/vc4_mock_output.o \
+ tests/vc4_mock_plane.o \
+- tests/vc4_test_pv_muxing.o
++ tests/vc4_test_pv_muxing.o \
++ tests/vc4_test_lbm_size.o
+
+ vc4-$(CONFIG_DEBUG_FS) += vc4_debugfs.o
+
+--- /dev/null
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
+@@ -0,0 +1,327 @@
++// SPDX-License-Identifier: GPL-2.0
++
++#include <drm/drm_atomic_helper.h>
++#include <drm/drm_atomic_uapi.h>
++#include <drm/drm_drv.h>
++#include <drm/drm_fourcc.h>
++#include <drm/drm_framebuffer.h>
++#include <drm/drm_plane.h>
++#include <drm/drm_kunit_helpers.h>
++
++#include "../../drm_crtc_internal.h"
++#include "../../drm_internal.h"
++
++#include <kunit/test.h>
++
++#include "../vc4_drv.h"
++
++#include "vc4_mock.h"
++
++u32 vc4_lbm_size(struct drm_plane_state *state);
++
++struct vc4_lbm_size_priv {
++ struct vc4_dev *vc4;
++ struct drm_file *file;
++ struct drm_modeset_acquire_ctx ctx;
++ struct drm_atomic_state *state;
++};
++
++struct vc4_lbm_size_param {
++ unsigned int src_w, src_h;
++ unsigned int crtc_w, crtc_h;
++ bool forced_alpha;
++ u32 fourcc;
++ enum vc4_scaling_mode expected_x_scaling[2];
++ enum vc4_scaling_mode expected_y_scaling[2];
++ unsigned int expected_lbm_size;
++};
++
++static const struct vc4_lbm_size_param vc4_test_lbm_size_params[] = {
++ {
++ .src_w = 256,
++ .crtc_w = 256,
++ .src_h = 256,
++ .crtc_h = 512,
++ .fourcc = DRM_FORMAT_ARGB8888,
++ .expected_x_scaling = { VC4_SCALING_NONE, },
++ .expected_y_scaling = { VC4_SCALING_PPF, },
++ .expected_lbm_size = 32,
++ },
++ {
++ .src_w = 256,
++ .crtc_w = 179,
++ .src_h = 256,
++ .crtc_h = 512,
++ .fourcc = DRM_FORMAT_ARGB8888,
++ .expected_x_scaling = { VC4_SCALING_PPF, },
++ .expected_y_scaling = { VC4_SCALING_PPF, },
++ .expected_lbm_size = 23,
++ },
++ {
++ .src_w = 256,
++ .crtc_w = 256,
++ .src_h = 256,
++ .crtc_h = 512,
++ .fourcc = DRM_FORMAT_XRGB8888,
++ .expected_x_scaling = { VC4_SCALING_NONE, },
++ .expected_y_scaling = { VC4_SCALING_PPF, },
++ .expected_lbm_size = 24,
++ },
++ {
++ .src_w = 100,
++ .crtc_w = 73,
++ .src_h = 100,
++ .crtc_h = 73,
++ .fourcc = DRM_FORMAT_XRGB8888,
++ .expected_x_scaling = { VC4_SCALING_PPF, },
++ .expected_y_scaling = { VC4_SCALING_PPF, },
++ .expected_lbm_size = 8,
++ },
++ {
++ .src_w = 256,
++ .crtc_w = 256,
++ .src_h = 256,
++ .crtc_h = 512,
++ .forced_alpha = true,
++ .fourcc = DRM_FORMAT_ARGB8888,
++ .expected_x_scaling = { VC4_SCALING_NONE, },
++ .expected_y_scaling = { VC4_SCALING_PPF, },
++ .expected_lbm_size = 24,
++ },
++ {
++ .src_w = 100,
++ .crtc_w = 73,
++ .src_h = 100,
++ .crtc_h = 73,
++ .forced_alpha = true,
++ .fourcc = DRM_FORMAT_ARGB8888,
++ .expected_x_scaling = { VC4_SCALING_PPF, },
++ .expected_y_scaling = { VC4_SCALING_PPF, },
++ .expected_lbm_size = 8,
++ },
++ {
++ .src_w = 256,
++ .crtc_w = 94,
++ .src_h = 256,
++ .crtc_h = 94,
++ .fourcc = DRM_FORMAT_ARGB8888,
++ .expected_x_scaling = { VC4_SCALING_TPZ, },
++ .expected_y_scaling = { VC4_SCALING_TPZ, },
++ .expected_lbm_size = 6,
++ },
++
++/*
++ * TODO: Those tests reflect the LBM size calculation examples, but the
++ * driver ends up taking different scaler filters decisions, and thus
++ * doesn't end up with the same sizes. It would be valuable to have
++ * those tests, but the driver doesn't take a bad decision either, so
++ * it's not clear what we should do at this point.
++ */
++#if 0
++ {
++ .src_w = 320,
++ .crtc_w = 320,
++ .src_h = 320,
++ .crtc_h = 320,
++ .fourcc = DRM_FORMAT_YUV420,
++ .expected_x_scaling = { VC4_SCALING_NONE, VC4_SCALING_NONE, },
++ .expected_y_scaling = { VC4_SCALING_NONE, VC4_SCALING_PPF, },
++ .expected_lbm_size = 10,
++ },
++ {
++ .src_w = 512,
++ .crtc_w = 512,
++ .src_h = 512,
++ .crtc_h = 256,
++ .fourcc = DRM_FORMAT_YUV420,
++ .expected_x_scaling = { VC4_SCALING_NONE, VC4_SCALING_NONE, },
++ .expected_y_scaling = { VC4_SCALING_TPZ, VC4_SCALING_NONE, },
++ .expected_lbm_size = 5,
++ },
++ {
++ .src_w = 486,
++ .crtc_w = 157,
++ .src_h = 404,
++ .crtc_h = 929,
++ .fourcc = DRM_FORMAT_YUV422,
++ .expected_x_scaling = { VC4_SCALING_PPF, VC4_SCALING_PPF, },
++ .expected_y_scaling = { VC4_SCALING_PPF, VC4_SCALING_PPF, },
++ .expected_lbm_size = 20,
++ },
++ {
++ .src_w = 320,
++ .crtc_w = 128,
++ .src_h = 176,
++ .crtc_h = 70,
++ .fourcc = DRM_FORMAT_YUV420,
++ .expected_x_scaling = { VC4_SCALING_TPZ, VC4_SCALING_TPZ, },
++ .expected_y_scaling = { VC4_SCALING_TPZ, VC4_SCALING_TPZ, },
++ .expected_lbm_size = 8,
++ },
++#endif
++};
++
++static void vc4_test_lbm_size_desc(const struct vc4_lbm_size_param *t, char *desc)
++{
++ snprintf(desc, KUNIT_PARAM_DESC_SIZE,
++ "%ux%u to %ux%u %s(%p4cc)",
++ t->src_w, t->src_h,
++ t->crtc_w, t->crtc_h,
++ t->forced_alpha ? "with forced alpha " : "",
++ &t->fourcc);
++}
++
++KUNIT_ARRAY_PARAM(vc4_test_lbm_size,
++ vc4_test_lbm_size_params,
++ vc4_test_lbm_size_desc);
++
++static void drm_vc4_test_vc4_lbm_size(struct kunit *test)
++{
++ const struct vc4_lbm_size_param *params = test->param_value;
++ const struct vc4_lbm_size_priv *priv = test->priv;
++ const struct drm_format_info *info;
++ struct drm_mode_fb_cmd2 fb_req = { };
++ struct drm_atomic_state *state = priv->state;
++ struct vc4_plane_state *vc4_plane_state;
++ struct drm_plane_state *plane_state;
++ struct vc4_dummy_output *output;
++ struct drm_framebuffer *fb;
++ struct drm_plane *plane;
++ struct drm_crtc *crtc;
++ unsigned int i;
++ int ret;
++
++ info = drm_format_info(params->fourcc);
++ KUNIT_ASSERT_NOT_NULL(test, info);
++
++ output = vc4_mock_atomic_add_output(test, state, VC4_ENCODER_TYPE_HDMI0);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, output);
++
++ crtc = vc4_find_crtc_for_encoder(test, &output->encoder.base);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, crtc);
++
++ plane = vc4_mock_atomic_add_plane(test, state, crtc);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane);
++
++ plane_state = drm_atomic_get_plane_state(state, plane);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, plane_state);
++
++ vc4_plane_state = to_vc4_plane_state(plane_state);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4_plane_state);
++
++ fb_req.pixel_format = params->fourcc;
++ fb_req.width = params->src_w;
++ fb_req.height = params->src_h;
++
++ for (i = 0; i < info->num_planes; i++) {
++ struct drm_mode_create_dumb dumb_args = { };
++
++ dumb_args.width = params->src_w;
++ dumb_args.height = params->src_h;
++ dumb_args.bpp = drm_format_info_bpp(info, i);
++
++ ret = drm_mode_create_dumb(state->dev, &dumb_args, priv->file);
++ KUNIT_ASSERT_EQ(test, ret, 0);
++
++ fb_req.handles[i] = dumb_args.handle;
++ fb_req.pitches[i] = dumb_args.pitch;
++ }
++
++ fb = drm_internal_framebuffer_create(state->dev, &fb_req, priv->file);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, fb);
++
++ drm_atomic_set_fb_for_plane(plane_state, fb);
++
++ plane_state->src_x = 0;
++ plane_state->src_y = 0;
++ plane_state->src_h = params->src_h << 16;
++ plane_state->src_w = params->src_w << 16;
++
++ plane_state->crtc_x = 0;
++ plane_state->crtc_y = 0;
++ plane_state->crtc_h = params->crtc_h;
++ plane_state->crtc_w = params->crtc_w;
++
++ if (params->forced_alpha)
++ plane_state->alpha = 128;
++
++ ret = drm_atomic_check_only(state);
++ KUNIT_ASSERT_EQ(test, ret, 0);
++
++ KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm.size, params->expected_lbm_size);
++
++ for (i = 0; i < 2; i++) {
++ KUNIT_EXPECT_EQ(test,
++ vc4_plane_state->x_scaling[i],
++ params->expected_x_scaling[i]);
++ KUNIT_EXPECT_EQ(test,
++ vc4_plane_state->y_scaling[i],
++ params->expected_y_scaling[i]);
++ }
++
++ drm_framebuffer_put(fb);
++
++ for (i = 0; i < info->num_planes; i++)
++ drm_mode_destroy_dumb(state->dev, fb_req.handles[i], priv->file);
++}
++
++static struct kunit_case vc4_lbm_size_tests[] = {
++ KUNIT_CASE_PARAM(drm_vc4_test_vc4_lbm_size,
++ vc4_test_lbm_size_gen_params),
++ {}
++};
++
++static int vc4_lbm_size_test_init(struct kunit *test)
++{
++ struct drm_atomic_state *state;
++ struct vc4_lbm_size_priv *priv;
++ struct drm_device *drm;
++ struct vc4_dev *vc4;
++
++ priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
++ KUNIT_ASSERT_NOT_NULL(test, priv);
++ test->priv = priv;
++
++ vc4 = vc6_mock_device(test);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, vc4);
++ priv->vc4 = vc4;
++
++ priv->file = drm_file_alloc(priv->vc4->base.primary);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->file);
++
++ drm_modeset_acquire_init(&priv->ctx, 0);
++
++ drm = &vc4->base;
++ state = drm_atomic_state_alloc(drm);
++ KUNIT_ASSERT_NOT_ERR_OR_NULL(test, state);
++
++ state->acquire_ctx = &priv->ctx;
++
++ priv->state = state;
++
++ return 0;
++}
++
++static void vc4_lbm_size_test_exit(struct kunit *test)
++{
++ struct vc4_lbm_size_priv *priv = test->priv;
++ struct vc4_dev *vc4 = priv->vc4;
++ struct drm_device *drm = &vc4->base;
++ struct drm_atomic_state *state = priv->state;
++
++ drm_atomic_state_put(state);
++ drm_modeset_drop_locks(&priv->ctx);
++ drm_modeset_acquire_fini(&priv->ctx);
++ drm_file_free(priv->file);
++ drm_dev_unregister(drm);
++ drm_kunit_helper_free_device(test, vc4->dev);
++}
++
++static struct kunit_suite vc4_lbm_size_test_suite = {
++ .name = "vc4-lbm-size",
++ .init = vc4_lbm_size_test_init,
++ .exit = vc4_lbm_size_test_exit,
++ .test_cases = vc4_lbm_size_tests,
++};
++
++kunit_test_suite(vc4_lbm_size_test_suite);
--- /dev/null
+From 352d96c9e50012f2b5e5dde9933af8d570e7dc81 Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Mon, 17 Jul 2023 17:45:32 +0100
+Subject: [PATCH] drm/vc4: kms: Avoid setting core and disp clocks for hdmi
+ modes
+
+On 2712, the firmware always runs these clock at a speed sufficient
+for dual 4kp60.
+
+The requests here prevent the gpu from going into its lowest voltage
+mode, so just skip the clock requests.
+
+With this applied the idle voltage on my pi 5 reduces from 0.7424V
+to 0.72V.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -435,7 +435,7 @@ static void vc4_atomic_commit_tail(struc
+ old_hvs_state->fifo_state[channel].pending_commit = NULL;
+ }
+
+- if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
++ if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+ unsigned long state_rate = max(old_hvs_state->core_clock_rate,
+ new_hvs_state->core_clock_rate);
+ unsigned long core_rate = clamp_t(unsigned long, state_rate,
+@@ -489,7 +489,7 @@ static void vc4_atomic_commit_tail(struc
+
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+- if (vc4->gen >= VC4_GEN_5 && !vc4->firmware_kms) {
++ if (vc4->gen == VC4_GEN_5 && !vc4->firmware_kms) {
+ unsigned long core_rate = min_t(unsigned long,
+ hvs->max_core_rate,
+ new_hvs_state->core_clock_rate);
--- /dev/null
+From bb0839405b61da6e6ae7141f7433f6a121725e6f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 31 Aug 2023 11:45:38 +0100
+Subject: [PATCH] drm/vc4: Assign LBM memory during atomic_flush.
+
+Avoid double buffering LBM allocations by making the
+allocation a single alloc per crtc at atomic_flush.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c | 2 +-
+ drivers/gpu/drm/vc4/vc4_drv.h | 8 ++--
+ drivers/gpu/drm/vc4/vc4_hvs.c | 47 ++++++++++++++++++-
+ drivers/gpu/drm/vc4/vc4_plane.c | 38 +++------------
+ 4 files changed, 58 insertions(+), 37 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
++++ b/drivers/gpu/drm/vc4/tests/vc4_test_lbm_size.c
+@@ -248,7 +248,7 @@ static void drm_vc4_test_vc4_lbm_size(st
+ ret = drm_atomic_check_only(state);
+ KUNIT_ASSERT_EQ(test, ret, 0);
+
+- KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm.size, params->expected_lbm_size);
++ KUNIT_EXPECT_EQ(test, vc4_plane_state->lbm_size, params->expected_lbm_size);
+
+ for (i = 0; i < 2; i++) {
+ KUNIT_EXPECT_EQ(test,
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -437,6 +437,8 @@ struct vc4_plane_state {
+ u32 dlist_size; /* Number of dwords allocated for the display list */
+ u32 dlist_count; /* Number of used dwords in the display list. */
+
++ u32 lbm_size; /* LBM requirements for this plane */
++
+ /* Offset in the dlist to various words, for pageflip or
+ * cursor updates.
+ */
+@@ -462,9 +464,6 @@ struct vc4_plane_state {
+ bool is_unity;
+ bool is_yuv;
+
+- /* Our allocation in LBM for temporary storage during scaling. */
+- struct drm_mm_node lbm;
+-
+ /* Our allocation in UPM for prefetching. */
+ struct drm_mm_node upm[DRM_FORMAT_MAX_PLANES];
+
+@@ -661,6 +660,9 @@ struct vc4_crtc {
+ * access to that value.
+ */
+ unsigned int current_hvs_channel;
++
++ /* @lbm: Our allocation in LBM for temporary storage during scaling. */
++ struct drm_mm_node lbm;
+ };
+
+ static inline struct vc4_crtc *
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1103,6 +1103,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
+ struct drm_plane *plane;
+ const struct drm_plane_state *plane_state;
+ u32 dlist_count = 0;
++ u32 lbm_count = 0;
+
+ /* The pixelvalve can only feed one encoder (and encoders are
+ * 1:1 with connectors.)
+@@ -1111,6 +1112,8 @@ int vc4_hvs_atomic_check(struct drm_crtc
+ return -EINVAL;
+
+ drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, crtc_state) {
++ const struct vc4_plane_state *vc4_plane_state =
++ to_vc4_plane_state(plane_state);
+ u32 plane_dlist_count = vc4_plane_dlist_size(plane_state);
+
+ drm_dbg_driver(dev, "[CRTC:%d:%s] Found [PLANE:%d:%s] with DLIST size: %u\n",
+@@ -1119,6 +1122,7 @@ int vc4_hvs_atomic_check(struct drm_crtc
+ plane_dlist_count);
+
+ dlist_count += plane_dlist_count;
++ lbm_count += vc4_plane_state->lbm_size;
+ }
+
+ dlist_count++; /* Account for SCALER_CTL0_END. */
+@@ -1132,6 +1136,8 @@ int vc4_hvs_atomic_check(struct drm_crtc
+
+ vc4_state->mm = alloc;
+
++ /* FIXME: Check total lbm allocation here */
++
+ return vc4_hvs_gamma_check(crtc, state);
+ }
+
+@@ -1246,7 +1252,10 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ bool debug_dump_regs = false;
+ bool enable_bg_fill = false;
+ u32 __iomem *dlist_start, *dlist_next;
++ unsigned long irqflags;
+ unsigned int zpos = 0;
++ u32 lbm_offset = 0;
++ u32 lbm_size = 0;
+ bool found = false;
+ int idx;
+
+@@ -1265,6 +1274,35 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ vc4_hvs_dump_state(hvs);
+ }
+
++ drm_atomic_crtc_for_each_plane(plane, crtc) {
++ vc4_plane_state = to_vc4_plane_state(plane->state);
++ lbm_size += vc4_plane_state->lbm_size;
++ }
++
++ if (drm_mm_node_allocated(&vc4_crtc->lbm)) {
++ spin_lock_irqsave(&vc4_crtc->irq_lock, irqflags);
++ drm_mm_remove_node(&vc4_crtc->lbm);
++ spin_unlock_irqrestore(&vc4_crtc->irq_lock, irqflags);
++ }
++
++ if (lbm_size) {
++ int ret;
++
++ spin_lock_irqsave(&vc4_crtc->irq_lock, irqflags);
++ ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
++ &vc4_crtc->lbm,
++ lbm_size, 1,
++ 0, 0);
++ spin_unlock_irqrestore(&vc4_crtc->irq_lock, irqflags);
++
++ if (ret) {
++ pr_err("Failed to allocate LBM ret %d\n", ret);
++ return;
++ }
++ }
++
++ lbm_offset = vc4_crtc->lbm.start;
++
+ dlist_start = vc4->hvs->dlist + vc4_state->mm->mm_node.start;
+ dlist_next = dlist_start;
+
+@@ -1276,6 +1314,8 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ if (plane->state->normalized_zpos != zpos)
+ continue;
+
++ vc4_plane_state = to_vc4_plane_state(plane->state);
++
+ /* Is this the first active plane? */
+ if (dlist_next == dlist_start) {
+ /* We need to enable background fill when a plane
+@@ -1286,10 +1326,15 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ * already needs it or all planes on top blend from
+ * the first or a lower plane.
+ */
+- vc4_plane_state = to_vc4_plane_state(plane->state);
+ enable_bg_fill = vc4_plane_state->needs_bg_fill;
+ }
+
++ if (vc4_plane_state->lbm_size) {
++ vc4_plane_state->dlist[vc4_plane_state->lbm_offset] =
++ lbm_offset;
++ lbm_offset += vc4_plane_state->lbm_size;
++ }
++
+ dlist_next += vc4_plane_write_dlist(plane, dlist_next);
+
+ found = true;
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -288,7 +288,6 @@ struct drm_plane_state *vc4_plane_duplic
+ if (!vc4_state)
+ return NULL;
+
+- memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
+ memset(&vc4_state->upm, 0, sizeof(vc4_state->upm));
+
+ for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++)
+@@ -320,14 +319,6 @@ void vc4_plane_destroy_state(struct drm_
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+ unsigned int i;
+
+- if (drm_mm_node_allocated(&vc4_state->lbm)) {
+- unsigned long irqflags;
+-
+- spin_lock_irqsave(&hvs->mm_lock, irqflags);
+- drm_mm_remove_node(&vc4_state->lbm);
+- spin_unlock_irqrestore(&hvs->mm_lock, irqflags);
+- }
+-
+ for (i = 0; i < DRM_FORMAT_MAX_PLANES; i++) {
+ unsigned long irqflags;
+
+@@ -903,12 +894,13 @@ static int vc4_plane_allocate_lbm(struct
+ struct vc4_dev *vc4 = to_vc4_dev(drm);
+ struct drm_plane *plane = state->plane;
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
+- unsigned long irqflags;
+ u32 lbm_size;
+
+ lbm_size = vc4_lbm_size(state);
+- if (!lbm_size)
++ if (!lbm_size) {
++ vc4_state->lbm_size = 0;
+ return 0;
++ }
+
+ /*
+ * NOTE: BCM2712 doesn't need to be aligned, since the size
+@@ -925,28 +917,10 @@ static int vc4_plane_allocate_lbm(struct
+ if (WARN_ON(!vc4_state->lbm_offset))
+ return -EINVAL;
+
+- /* Allocate the LBM memory that the HVS will use for temporary
+- * storage due to our scaling/format conversion.
++ /* FIXME: Add loop here that ensures that the total LBM assigned in this
++ * state is less than the total lbm size
+ */
+- if (!drm_mm_node_allocated(&vc4_state->lbm)) {
+- int ret;
+-
+- spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
+- ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
+- &vc4_state->lbm,
+- lbm_size, 1,
+- 0, 0);
+- spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
+-
+- if (ret) {
+- drm_err(drm, "Failed to allocate LBM entry: %d\n", ret);
+- return ret;
+- }
+- } else {
+- WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
+- }
+-
+- vc4_state->dlist[vc4_state->lbm_offset] = vc4_state->lbm.start;
++ vc4_state->lbm_size = lbm_size;
+
+ return 0;
+ }
--- /dev/null
+From b1bd2f406eab321be642decd6aee6b6222aec62b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 28 Jul 2023 17:40:27 +0100
+Subject: [PATCH] drm/panel: simple: Alter the timing for the Pi 7" DSI display
+
+vc4 has always fixed up the timing, so the values defined have
+never actually appeared on the wire.
+The display appears to want a slightly longer HFP, so extend
+the timings and recompute the clock to give the same frame rate.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-simple.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-simple.c
++++ b/drivers/gpu/drm/panel/panel-simple.c
+@@ -3241,11 +3241,11 @@ static const struct panel_desc qishenglo
+ };
+
+ static const struct drm_display_mode raspberrypi_7inch_mode = {
+- .clock = 25979400 / 1000,
++ .clock = 27777,
+ .hdisplay = 800,
+- .hsync_start = 800 + 2,
+- .hsync_end = 800 + 2 + 2,
+- .htotal = 800 + 2 + 2 + 46,
++ .hsync_start = 800 + 59,
++ .hsync_end = 800 + 59 + 2,
++ .htotal = 800 + 59 + 2 + 46,
+ .vdisplay = 480,
+ .vsync_start = 480 + 7,
+ .vsync_end = 480 + 7 + 2,
--- /dev/null
+From c7cf33911d477fe55a91a9e4d84dad857b244ae3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 28 Jul 2023 18:10:53 +0100
+Subject: [PATCH] drm/panel: waveshare: Fix up timings for 10.1" panel
+
+The 10.1" panel doesn't work with the timings defined. vc4
+will always have been fixing up the timing due to the limited
+integer divider, so compute the fixed up mode and use it
+directly.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -112,11 +112,11 @@ static const struct drm_display_mode ws_
+ * https://www.waveshare.com/product/raspberry-pi/displays/10.1inch-dsi-lcd-c.htm
+ */
+ static const struct drm_display_mode ws_panel_10_1_mode = {
+- .clock = 76800,
++ .clock = 83333,
+ .hdisplay = 1280,
+- .hsync_start = 1280 + 40,
+- .hsync_end = 1280 + 40 + 20,
+- .htotal = 1280 + 40 + 20 + 40,
++ .hsync_start = 1280 + 156,
++ .hsync_end = 1280 + 156 + 20,
++ .htotal = 1280 + 156 + 20 + 40,
+ .vdisplay = 800,
+ .vsync_start = 800 + 40,
+ .vsync_end = 800 + 40 + 48,
--- /dev/null
+From 72c25bbb761de2b2acd9f8b652d63e2a2f1caeed Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Mon, 11 Sep 2023 12:17:25 +0300
+Subject: [PATCH] media: i2c: imx477: Fix locking in imx477_init_controls()
+
+The driver does not lock the imx477 mutex when calling
+imx477_set_framing_limits(), leading to:
+
+WARNING: CPU: 3 PID: 426 at drivers/media/v4l2-core/v4l2-ctrls-api.c:934 __v4l2_ctrl_modify_range+0x1a0/0x210 [
+videodev]
+
+Fix this by taking the lock.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/i2c/imx477.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -2069,9 +2069,13 @@ static int imx477_init_controls(struct i
+
+ imx477->sd.ctrl_handler = ctrl_hdlr;
+
++ mutex_lock(&imx477->mutex);
++
+ /* Setup exposure and frame/line length limits. */
+ imx477_set_framing_limits(imx477);
+
++ mutex_unlock(&imx477->mutex);
++
+ return 0;
+
+ error:
--- /dev/null
+From 2ff65ffbdeb0c8764985af19df2a687a126136f4 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 29 Sep 2023 16:55:28 +0100
+Subject: [PATCH] overlays: Fix vc4-kms-dsi-7inch
+
+Fix the touchscreen.
+
+See: https://github.com/raspberrypi/linux/issues/5619
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/edt-ft5406.dtsi | 13 ++++---------
+ .../boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts | 2 +-
+ 2 files changed, 5 insertions(+), 10 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
++++ b/arch/arm/boot/dts/overlays/edt-ft5406.dtsi
+@@ -22,11 +22,13 @@
+ };
+ };
+
+- fragment@12 {
+- target = <&i2cbus>;
++ ts_i2c_frag: fragment@12 {
++ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
++ status = "okay";
++
+ ft5406: ts@38 {
+ compatible = "edt,edt-ft5506";
+ reg = <0x38>;
+@@ -37,13 +39,6 @@
+ };
+ };
+
+- ts_i2c_frag: fragment@13 {
+- target = <&i2c_csi_dsi>;
+- i2cbus: __overlay__ {
+- status = "okay";
+- };
+- };
+-
+ __overrides__ {
+ sizex = <&ft5406>,"touchscreen-size-x:0";
+ sizey = <&ft5406>,"touchscreen-size-y:0";
+--- a/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-7inch-overlay.dts
+@@ -119,6 +119,6 @@
+ <&panel_disp>, "reg:0=0",
+ <®_bridge>, "reg:0=0",
+ <®_bridge>, "regulator-name=bridge_reg_0";
+- disable_touch = <0>, "-10-11-12";
++ disable_touch = <&ft5406>, "status=disabled";
+ };
+ };
--- /dev/null
+From a1caea3c996f6bfa8c9568f521257f4e473285fb Mon Sep 17 00:00:00 2001
+From: Matthias Reichl <hias@horus.com>
+Date: Fri, 29 Sep 2023 21:50:28 +0200
+Subject: [PATCH] ASoC: hdmi-codec: Fix broken channel map reporting
+
+Commit b84b53149476b22cc3b8677b771fb4cf06d1d455 upstream.
+
+Commit 4e0871333661 ("ASoC: hdmi-codec: fix channel info for
+compressed formats") accidentally changed hcp->chmap_idx from
+ca_id, the CEA channel allocation ID, to idx, the index to
+the table of channel mappings ordered by preference.
+
+This resulted in wrong channel maps being reported to userspace,
+eg for 5.1 "FL,FR,LFE,FC" was reported instead of the expected
+"FL,FR,LFE,FC,RL,RR":
+
+~ # speaker-test -c 6 -t sine
+...
+ 0 - Front Left
+ 3 - Front Center
+ 1 - Front Right
+ 2 - LFE
+ 4 - Unknown
+ 5 - Unknown
+
+~ # amixer cget iface=PCM,name='Playback Channel Map' | grep ': values'
+ : values=3,4,8,7,0,0,0,0
+
+Switch this back to ca_id in case of PCM audio so the correct channel
+map is reported again and set it to HDMI_CODEC_CHMAP_IDX_UNKNOWN in
+case of non-PCM audio so the PCM channel map control returns "Unknown"
+channels (value 0).
+
+Fixes: 4e0871333661 ("ASoC: hdmi-codec: fix channel info for compressed formats")
+Cc: stable@vger.kernel.org
+Signed-off-by: Matthias Reichl <hias@horus.com>
+Link: https://lore.kernel.org/r/20230929195027.97136-1-hias@horus.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/codecs/hdmi-codec.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/sound/soc/codecs/hdmi-codec.c
++++ b/sound/soc/codecs/hdmi-codec.c
+@@ -520,7 +520,10 @@ static int hdmi_codec_fill_codec_params(
+ hp->sample_rate = sample_rate;
+ hp->channels = channels;
+
+- hcp->chmap_idx = idx;
++ if (pcm_audio)
++ hcp->chmap_idx = ca_id;
++ else
++ hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN;
+
+ return 0;
+ }
--- /dev/null
+From 3922bebc11fcc8459c798cfcb582828f9bbaa9e9 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 28 Sep 2023 11:33:53 +0300
+Subject: [PATCH] media: rp1: cfe: Fix use of freed memory on errors
+
+cfe_probe_complete() calls cfe_put() on both success and fail code paths.
+This works for the success path, but causes the cfe_device struct to be
+freed, even if it will be used later in the teardown code.
+
+Fix this by making the ref handling a bit saner: Let the video nodes
+have the refs as they do now, but also keep a ref in the "main" driver,
+released only at cfe_remove() time. This way the driver does not depend
+on the video nodes keeping the refs.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 9 ++-------
+ 1 file changed, 2 insertions(+), 7 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -1837,17 +1837,10 @@ static int cfe_probe_complete(struct cfe
+ goto unregister;
+ }
+
+- /*
+- * Release the initial reference, all references are now owned by the
+- * video devices.
+- */
+- cfe_put(cfe);
+ return 0;
+
+ unregister:
+ cfe_unregister_nodes(cfe);
+- cfe_put(cfe);
+-
+ return ret;
+ }
+
+@@ -2129,6 +2122,8 @@ static int cfe_remove(struct platform_de
+
+ v4l2_device_unregister(&cfe->v4l2_dev);
+
++ cfe_put(cfe);
++
+ return 0;
+ }
+
--- /dev/null
+From 84c9958dd71b8a4dcf16cbf6fdb867c668652634 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 27 Sep 2023 16:00:39 +0300
+Subject: [PATCH] media: rp1: cfe: Fix width & height in cfe_start_channel()
+
+The logic for handling width & height in cfe_start_channel() is somewhat
+odd and, afaics, broken. The code reads:
+
+bool start_fe = is_fe_enabled(cfe) &&
+ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
+
+if (start_fe || is_image_output_node(node)) {
+ width = node->fmt.fmt.pix.width;
+ height = node->fmt.fmt.pix.height;
+}
+
+cfe_start_channel() is called for all video nodes that will be used. So
+this means that if, say, fe_stats is enabled as the last node, start_fe
+will be true, and width and height will be taken from fe_stats' node.
+The width and height will thus contain garbage, which then gets
+programmed to the csi2 registers.
+
+It seems that this often still works fine, though, probably if the width
+& height are large enough.
+
+Drop the above code, and instead get the width & height from the csi2
+subdev's sink pad for the csi2 channel that is used. For metadata the
+width & height will be 0 as before.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 16 ++++++++++------
+ 1 file changed, 10 insertions(+), 6 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -763,20 +763,16 @@ static void cfe_start_channel(struct cfe
+ struct v4l2_mbus_framefmt *source_fmt;
+ const struct cfe_fmt *fmt;
+ unsigned long flags;
+- unsigned int width = 0, height = 0;
+ bool start_fe = is_fe_enabled(cfe) &&
+ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING);
+
+ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+
+- if (start_fe || is_image_output_node(node)) {
+- width = node->fmt.fmt.pix.width;
+- height = node->fmt.fmt.pix.height;
+- }
+-
+ state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
+
+ if (start_fe) {
++ unsigned int width, height;
++
+ WARN_ON(!is_fe_enabled(cfe));
+ cfe_dbg("%s: %s using csi2 channel %d\n",
+ __func__, node_desc[FE_OUT0].name,
+@@ -785,6 +781,9 @@ static void cfe_start_channel(struct cfe
+ source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, cfe->fe_csi2_channel);
+ fmt = find_format_by_code(source_fmt->code);
+
++ width = source_fmt->width;
++ height = source_fmt->height;
++
+ /*
+ * Start the associated CSI2 Channel as well.
+ *
+@@ -800,6 +799,8 @@ static void cfe_start_channel(struct cfe
+ }
+
+ if (is_csi2_node(node)) {
++ unsigned int width = 0, height = 0;
++
+ u32 mode = CSI2_MODE_NORMAL;
+
+ source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state,
+@@ -807,6 +808,9 @@ static void cfe_start_channel(struct cfe
+ fmt = find_format_by_code(source_fmt->code);
+
+ if (is_image_output_node(node)) {
++ width = source_fmt->width;
++ height = source_fmt->height;
++
+ if (node->fmt.fmt.pix.pixelformat ==
+ fmt->remap[CFE_REMAP_16BIT])
+ mode = CSI2_MODE_REMAP;
--- /dev/null
+From 62e8ab88d2c230dad122aabe2ad0e227d7ceba40 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 28 Sep 2023 10:42:22 +0300
+Subject: [PATCH] media: rp1: csi2: Fix missing reg writes
+
+The driver has two places where it writes a register based on a
+condition, and when that condition is false, the driver presumes that
+the register has the reset value. This is not a good idea, so fix those
+places to always write the register.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/csi2.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -253,6 +253,7 @@ void csi2_start_channel(struct csi2_devi
+ */
+ set_field(&ctrl, 0x3ff, LC_MASK);
+ set_field(&ctrl, 0x00, CH_MODE_MASK);
++ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+ }
+
+ set_field(&ctrl, dt, DT_MASK);
+@@ -277,8 +278,8 @@ void csi2_open_rx(struct csi2_device *cs
+ {
+ dphy_start(&csi2->dphy);
+
+- if (!csi2->multipacket_line)
+- csi2_reg_write(csi2, CSI2_CTRL, EOP_IS_EOL);
++ csi2_reg_write(csi2, CSI2_CTRL,
++ csi2->multipacket_line ? 0 : EOP_IS_EOL);
+ }
+
+ void csi2_close_rx(struct csi2_device *csi2)
--- /dev/null
+From 455c4ae2c70348a5842835d2f67f7cd8e665a2a6 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 21 Sep 2023 16:03:07 +0300
+Subject: [PATCH] media: rp1: fe: Use ~0, not -1, when working with unsigned
+ values
+
+Use ~0, not -1, when working with unsigned values (-1 is not unsigned).
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -372,7 +372,7 @@ void pisp_fe_submit_job(struct pisp_fe_d
+ void pisp_fe_start(struct pisp_fe_device *fe)
+ {
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);
+- pisp_fe_reg_write(fe, FE_INT_STATUS, -1);
++ pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+ pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF | FE_INT_LINES0 | FE_INT_LINES1);
+ fe->inframe_count = 0;
+ }
+@@ -383,7 +383,7 @@ void pisp_fe_stop(struct pisp_fe_device
+ pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);
+ usleep_range(1000, 2000);
+ WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
+- pisp_fe_reg_write(fe, FE_INT_STATUS, -1);
++ pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+ }
+
+ static struct pisp_fe_device *to_pisp_fe_device(struct v4l2_subdev *subdev)
--- /dev/null
+From bf1709d2cf2b57c4ea98b8363156835249ae02cc Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 22 Sep 2023 12:41:35 +0300
+Subject: [PATCH] media: rp1: cfe: Fix verbose debug print
+
+The debug print in cfe_schedule_next_csi2_job() is printed every frame,
+and should thus use cfe_dbg_irq() to avoid spamming, rather than cfe_dbg().
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -518,8 +518,8 @@ static void cfe_schedule_next_csi2_job(s
+ node->next_frm = buf;
+ list_del(&buf->list);
+
+- cfe_dbg("%s: [%s] buffer:%p\n",
+- __func__, node_desc[node->id].name, &buf->vb.vb2_buf);
++ cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, &buf->vb.vb2_buf);
+
+ if (is_meta_node(node)) {
+ size = node->fmt.fmt.meta.buffersize;
--- /dev/null
+From a1ea528e187ee045aeff929ff0f4b2e53fdd970f Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 4 Oct 2023 10:12:37 +0300
+Subject: [PATCH] media: rp1: cfe: Rename xxx_dbg_irq() to xxx_dbg_verbose()
+
+Rename the xxx_dbg_irq() macros to xxx_dbg_verbose(), as they can be
+used to verbose debugs outside irq context too.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 40 +++++++++----------
+ .../media/platform/raspberrypi/rp1_cfe/cfe.h | 2 +-
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 26 ++++++------
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 12 +++---
+ 4 files changed, 40 insertions(+), 40 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -49,11 +49,11 @@
+ #define CFE_MODULE_NAME "rp1-cfe"
+ #define CFE_VERSION "1.0"
+
+-bool cfe_debug_irq;
++bool cfe_debug_verbose;
+
+-#define cfe_dbg_irq(fmt, arg...) \
++#define cfe_dbg_verbose(fmt, arg...) \
+ do { \
+- if (cfe_debug_irq) \
++ if (cfe_debug_verbose) \
+ dev_dbg(&cfe->pdev->dev, fmt, ##arg); \
+ } while (0)
+ #define cfe_dbg(fmt, arg...) dev_dbg(&cfe->pdev->dev, fmt, ##arg)
+@@ -518,8 +518,8 @@ static void cfe_schedule_next_csi2_job(s
+ node->next_frm = buf;
+ list_del(&buf->list);
+
+- cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__,
+- node_desc[node->id].name, &buf->vb.vb2_buf);
++ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, &buf->vb.vb2_buf);
+
+ if (is_meta_node(node)) {
+ size = node->fmt.fmt.meta.buffersize;
+@@ -550,8 +550,8 @@ static void cfe_schedule_next_pisp_job(s
+ buf = list_first_entry(&node->dma_queue, struct cfe_buffer,
+ list);
+
+- cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__,
+- node_desc[node->id].name, &buf->vb.vb2_buf);
++ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, &buf->vb.vb2_buf);
+
+ node->next_frm = buf;
+ vb2_bufs[node_desc[i].link_pad] = &buf->vb.vb2_buf;
+@@ -573,8 +573,8 @@ static bool cfe_check_job_ready(struct c
+ continue;
+
+ if (list_empty(&node->dma_queue)) {
+- cfe_dbg_irq("%s: [%s] has no buffer, unable to schedule job\n",
+- __func__, node_desc[i].name);
++ cfe_dbg_verbose("%s: [%s] has no buffer, unable to schedule job\n",
++ __func__, node_desc[i].name);
+ return false;
+ }
+ }
+@@ -592,7 +592,7 @@ static void cfe_prepare_next_job(struct
+ /* Flag if another job is ready after this. */
+ cfe->job_ready = cfe_check_job_ready(cfe);
+
+- cfe_dbg_irq("%s: end with scheduled job\n", __func__);
++ cfe_dbg_verbose("%s: end with scheduled job\n", __func__);
+ }
+
+ static void cfe_process_buffer_complete(struct cfe_node *node,
+@@ -600,8 +600,8 @@ static void cfe_process_buffer_complete(
+ {
+ struct cfe_device *cfe = node->cfe;
+
+- cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
+- &node->cur_frm->vb.vb2_buf);
++ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, &node->cur_frm->vb.vb2_buf);
+
+ node->cur_frm->vb.sequence = sequence;
+ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
+@@ -621,8 +621,8 @@ static void cfe_sof_isr_handler(struct c
+ {
+ struct cfe_device *cfe = node->cfe;
+
+- cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+- cfe->sequence);
++ cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++ cfe->sequence);
+
+ node->cur_frm = node->next_frm;
+ node->next_frm = NULL;
+@@ -651,8 +651,8 @@ static void cfe_eof_isr_handler(struct c
+ {
+ struct cfe_device *cfe = node->cfe;
+
+- cfe_dbg_irq("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+- cfe->sequence);
++ cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
++ cfe->sequence);
+
+ if (node->cur_frm)
+ cfe_process_buffer_complete(node, cfe->sequence);
+@@ -921,8 +921,8 @@ static int cfe_buffer_prepare(struct vb2
+ struct cfe_buffer *buf = to_cfe_buffer(vb);
+ unsigned long size;
+
+- cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
+- vb);
++ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, vb);
+
+ size = is_image_output_node(node) ? node->fmt.fmt.pix.sizeimage :
+ node->fmt.fmt.meta.buffersize;
+@@ -954,8 +954,8 @@ static void cfe_buffer_queue(struct vb2_
+ struct cfe_buffer *buf = to_cfe_buffer(vb);
+ unsigned long flags;
+
+- cfe_dbg_irq("%s: [%s] buffer:%p\n", __func__, node_desc[node->id].name,
+- vb);
++ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
++ node_desc[node->id].name, vb);
+
+ spin_lock_irqsave(&cfe->state_lock, flags);
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -11,7 +11,7 @@
+ #include <linux/media-bus-format.h>
+ #include <linux/videodev2.h>
+
+-extern bool cfe_debug_irq;
++extern bool cfe_debug_verbose;
+
+ enum cfe_remap_types {
+ CFE_REMAP_16BIT,
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -16,9 +16,9 @@
+ #include "csi2.h"
+ #include "cfe.h"
+
+-#define csi2_dbg_irq(fmt, arg...) \
++#define csi2_dbg_verbose(fmt, arg...) \
+ do { \
+- if (cfe_debug_irq) \
++ if (cfe_debug_verbose) \
+ dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg); \
+ } while (0)
+ #define csi2_dbg(fmt, arg...) dev_dbg(csi2->v4l2_dev->dev, fmt, ##arg)
+@@ -154,7 +154,7 @@ void csi2_isr(struct csi2_device *csi2,
+ u32 status;
+
+ status = csi2_reg_read(csi2, CSI2_STATUS);
+- csi2_dbg_irq("ISR: STA: 0x%x\n", status);
++ csi2_dbg_verbose("ISR: STA: 0x%x\n", status);
+
+ /* Write value back to clear the interrupts */
+ csi2_reg_write(csi2, CSI2_STATUS, status);
+@@ -167,16 +167,16 @@ void csi2_isr(struct csi2_device *csi2,
+
+ dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i));
+
+- csi2_dbg_irq("ISR: [%u], %s%s%s%s%s frame: %u line: %u\n", i,
+- (status & IRQ_FS(i)) ? "FS " : "",
+- (status & IRQ_FE(i)) ? "FE " : "",
+- (status & IRQ_FE_ACK(i)) ? "FE_ACK " : "",
+- (status & IRQ_LE(i)) ? "LE " : "",
+- (status & IRQ_LE_ACK(i)) ? "LE_ACK " : "",
+- dbg >> 16,
+- csi2->num_lines[i] ?
+- ((dbg & 0xffff) % csi2->num_lines[i]) :
+- 0);
++ csi2_dbg_verbose("ISR: [%u], %s%s%s%s%s frame: %u line: %u\n",
++ i, (status & IRQ_FS(i)) ? "FS " : "",
++ (status & IRQ_FE(i)) ? "FE " : "",
++ (status & IRQ_FE_ACK(i)) ? "FE_ACK " : "",
++ (status & IRQ_LE(i)) ? "LE " : "",
++ (status & IRQ_LE_ACK(i)) ? "LE_ACK " : "",
++ dbg >> 16,
++ csi2->num_lines[i] ?
++ ((dbg & 0xffff) % csi2->num_lines[i]) :
++ 0);
+
+ sof[i] = !!(status & IRQ_FS(i));
+ eof[i] = !!(status & IRQ_FE_ACK(i));
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -114,9 +114,9 @@ static const struct pisp_fe_config_param
+ sizeof(struct pisp_fe_output_config) },
+ };
+
+-#define pisp_fe_dbg_irq(fmt, arg...) \
++#define pisp_fe_dbg_verbose(fmt, arg...) \
+ do { \
+- if (cfe_debug_irq) \
++ if (cfe_debug_verbose) \
+ dev_dbg(fe->v4l2_dev->dev, fmt, ##arg); \
+ } while (0)
+ #define pisp_fe_dbg(fmt, arg...) dev_dbg(fe->v4l2_dev->dev, fmt, ##arg)
+@@ -202,9 +202,9 @@ void pisp_fe_isr(struct pisp_fe_device *
+ int_status = pisp_fe_reg_read(fe, FE_INT_STATUS);
+ pisp_fe_reg_write(fe, FE_INT_STATUS, int_status);
+
+- pisp_fe_dbg_irq("%s: status 0x%x out 0x%x frame 0x%x error 0x%x int 0x%x\n",
+- __func__, status, out_status, frame_status, error_status,
+- int_status);
++ pisp_fe_dbg_verbose("%s: status 0x%x out 0x%x frame 0x%x error 0x%x int 0x%x\n",
++ __func__, status, out_status, frame_status, error_status,
++ int_status);
+
+ /* We do not report interrupts for the input/stream pad. */
+ for (i = 0; i < FE_NUM_PADS - 1; i++) {
+@@ -339,7 +339,7 @@ void pisp_fe_submit_job(struct pisp_fe_d
+ * sequence of relaxed writes which follow.
+ */
+ status = pisp_fe_reg_read(fe, FE_STATUS);
+- pisp_fe_dbg_irq("%s: status = 0x%x\n", __func__, status);
++ pisp_fe_dbg_verbose("%s: status = 0x%x\n", __func__, status);
+ if (WARN_ON(status & FE_STATUS_QUEUED))
+ return;
+
--- /dev/null
+From 8240f1328ead0152f116b385b3169f8f010a7869 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 22 Sep 2023 12:39:33 +0300
+Subject: [PATCH] media: rp1: Add back reg write debug prints
+
+Add back debug prints in csi2 and pisp_fe reg_write() functions, but use
+the 'irq' variants to avoid spamming in normal situation.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/csi2.c | 1 +
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 2 ++
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -92,6 +92,7 @@ static inline u32 csi2_reg_read(struct c
+ static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
+ {
+ writel(val, csi2->base + offset);
++ csi2_dbg_verbose("csi2: write 0x%04x -> 0x%03x\n", val, offset);
+ }
+
+ static inline void set_field(u32 *valp, u32 field, u32 mask)
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -132,12 +132,14 @@ static inline void pisp_fe_reg_write(str
+ u32 val)
+ {
+ writel(val, fe->base + offset);
++ pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset);
+ }
+
+ static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe, u32 offset,
+ u32 val)
+ {
+ writel_relaxed(val, fe->base + offset);
++ pisp_fe_dbg_verbose("fe: write 0x%04x -> 0x%03x\n", val, offset);
+ }
+
+ static int pisp_regs_show(struct seq_file *s, void *data)
--- /dev/null
+From f2553458c0c1942731447aac3878f51aa1b326a7 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 4 Oct 2023 10:19:47 +0300
+Subject: [PATCH] media: rp1: cfe: Add verbose debug module parameter
+
+Expose the verbose debug flag as a module parameter.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -50,6 +50,8 @@
+ #define CFE_VERSION "1.0"
+
+ bool cfe_debug_verbose;
++module_param_named(verbose_debug, cfe_debug_verbose, bool, 0644);
++MODULE_PARM_DESC(verbose_debug, "verbose debugging messages");
+
+ #define cfe_dbg_verbose(fmt, arg...) \
+ do { \
--- /dev/null
+From b19c2b5f88f141e58044e5d1012f867d46f74bf3 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 21 Sep 2023 18:18:53 +0300
+Subject: [PATCH] media: rp1: csi2: Track CSI-2 errors
+
+Track the errors from the CSI-2 receiver: overflows and discards. These
+are recorded in a table which can be read by the userspace via debugfs.
+
+As tracking the errors may cause much more interrupt load, the tracking
+needs to be enabled with a module parameter.
+
+Note that the recording is not perfect: we only record the last
+discarded DT for each discard type, instead of recording all of them.
+This means that e.g. if the device is discarding two unmatched DTs, the
+debugfs file only shows the last one recorded. Recording all of them
+would need a more sophisticated recording system to avoid the need of a
+very large table, or dynamic allocation.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 123 ++++++++++++++++++
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h | 16 +++
+ 2 files changed, 139 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -16,6 +16,10 @@
+ #include "csi2.h"
+ #include "cfe.h"
+
++static bool csi2_track_errors;
++module_param_named(track_csi2_errors, csi2_track_errors, bool, 0);
++MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors");
++
+ #define csi2_dbg_verbose(fmt, arg...) \
+ do { \
+ if (cfe_debug_verbose) \
+@@ -32,9 +36,28 @@
+ #define CSI2_DISCARDS_INACTIVE 0x00c
+ #define CSI2_DISCARDS_UNMATCHED 0x010
+ #define CSI2_DISCARDS_LEN_LIMIT 0x014
++
++#define CSI2_DISCARDS_AMOUNT_SHIFT 0
++#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0)
++#define CSI2_DISCARDS_DT_SHIFT 24
++#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24)
++#define CSI2_DISCARDS_VC_SHIFT 30
++#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30)
++
+ #define CSI2_LLEV_PANICS 0x018
+ #define CSI2_ULEV_PANICS 0x01c
+ #define CSI2_IRQ_MASK 0x020
++#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3)
++#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4)
++#define CSI2_IRQ_MASK_IRQ_ALL \
++ (CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \
++ CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \
++ CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \
++ CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE)
++
+ #define CSI2_CTRL 0x024
+ #define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28)
+ #define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c)
+@@ -149,6 +172,92 @@ static int csi2_regs_show(struct seq_fil
+
+ DEFINE_SHOW_ATTRIBUTE(csi2_regs);
+
++static int csi2_errors_show(struct seq_file *s, void *data)
++{
++ struct csi2_device *csi2 = s->private;
++ unsigned long flags;
++ u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
++ u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
++ u32 overflows;
++
++ spin_lock_irqsave(&csi2->errors_lock, flags);
++
++ memcpy(discards_table, csi2->discards_table, sizeof(discards_table));
++ memcpy(discards_dt_table, csi2->discards_dt_table,
++ sizeof(discards_dt_table));
++ overflows = csi2->overflows;
++
++ csi2->overflows = 0;
++ memset(csi2->discards_table, 0, sizeof(discards_table));
++ memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table));
++
++ spin_unlock_irqrestore(&csi2->errors_lock, flags);
++
++ seq_printf(s, "Overflows %u\n", overflows);
++ seq_puts(s, "Discards:\n");
++ seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n");
++
++ for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) {
++ seq_printf(s, "%u %10u %10u %10u %10u\n", vc,
++ discards_table[vc][DISCARDS_TABLE_OVERFLOW],
++ discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT],
++ discards_table[vc][DISCARDS_TABLE_UNMATCHED],
++ discards_table[vc][DISCARDS_TABLE_INACTIVE]);
++ }
++
++ seq_printf(s, "Last DT %10u %10u %10u %10u\n",
++ discards_dt_table[DISCARDS_TABLE_OVERFLOW],
++ discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT],
++ discards_dt_table[DISCARDS_TABLE_UNMATCHED],
++ discards_dt_table[DISCARDS_TABLE_INACTIVE]);
++
++ return 0;
++}
++
++DEFINE_SHOW_ATTRIBUTE(csi2_errors);
++
++static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status)
++{
++ spin_lock(&csi2->errors_lock);
++
++ if (status & IRQ_OVERFLOW)
++ csi2->overflows++;
++
++ for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) {
++ static const u32 discard_bits[] = {
++ IRQ_DISCARD_OVERFLOW,
++ IRQ_DISCARD_LEN_LIMIT,
++ IRQ_DISCARD_UNMATCHED,
++ IRQ_DISCARD_INACTIVE,
++ };
++ static const u8 discard_regs[] = {
++ CSI2_DISCARDS_OVERFLOW,
++ CSI2_DISCARDS_LEN_LIMIT,
++ CSI2_DISCARDS_UNMATCHED,
++ CSI2_DISCARDS_INACTIVE,
++ };
++ u32 amount;
++ u8 dt, vc;
++ u32 v;
++
++ if (!(status & discard_bits[i]))
++ continue;
++
++ v = csi2_reg_read(csi2, discard_regs[i]);
++ csi2_reg_write(csi2, discard_regs[i], 0);
++
++ amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >>
++ CSI2_DISCARDS_AMOUNT_SHIFT;
++ dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT;
++ vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT;
++
++ csi2->discards_table[vc][i] += amount;
++ csi2->discards_dt_table[i] = dt;
++ }
++
++ spin_unlock(&csi2->errors_lock);
++}
++
+ void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
+ {
+ unsigned int i;
+@@ -183,6 +292,9 @@ void csi2_isr(struct csi2_device *csi2,
+ eof[i] = !!(status & IRQ_FE_ACK(i));
+ lci[i] = !!(status & IRQ_LE_ACK(i));
+ }
++
++ if (csi2_track_errors)
++ csi2_isr_handle_errors(csi2, status);
+ }
+
+ void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
+@@ -277,6 +389,9 @@ void csi2_stop_channel(struct csi2_devic
+
+ void csi2_open_rx(struct csi2_device *csi2)
+ {
++ csi2_reg_write(csi2, CSI2_IRQ_MASK,
++ csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);
++
+ dphy_start(&csi2->dphy);
+
+ csi2_reg_write(csi2, CSI2_CTRL,
+@@ -286,6 +401,8 @@ void csi2_open_rx(struct csi2_device *cs
+ void csi2_close_rx(struct csi2_device *csi2)
+ {
+ dphy_stop(&csi2->dphy);
++
++ csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
+ }
+
+ static struct csi2_device *to_csi2_device(struct v4l2_subdev *subdev)
+@@ -398,11 +515,17 @@ int csi2_init(struct csi2_device *csi2,
+ {
+ unsigned int i, ret;
+
++ spin_lock_init(&csi2->errors_lock);
++
+ csi2->dphy.dev = csi2->v4l2_dev->dev;
+ dphy_probe(&csi2->dphy);
+
+ debugfs_create_file("csi2_regs", 0444, debugfs, csi2, &csi2_regs_fops);
+
++ if (csi2_track_errors)
++ debugfs_create_file("csi2_errors", 0444, debugfs, csi2,
++ &csi2_errors_fops);
++
+ for (i = 0; i < CSI2_NUM_CHANNELS * 2; i++)
+ csi2->pad[i].flags = i < CSI2_NUM_CHANNELS ?
+ MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE;
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -17,6 +17,8 @@
+
+ #define CSI2_NUM_CHANNELS 4
+
++#define DISCARDS_TABLE_NUM_VCS 4
++
+ enum csi2_mode {
+ CSI2_MODE_NORMAL,
+ CSI2_MODE_REMAP,
+@@ -37,6 +39,14 @@ struct csi2_cfg {
+ u32 buffer_size;
+ };
+
++enum discards_table_index {
++ DISCARDS_TABLE_OVERFLOW = 0,
++ DISCARDS_TABLE_LENGTH_LIMIT,
++ DISCARDS_TABLE_UNMATCHED,
++ DISCARDS_TABLE_INACTIVE,
++ DISCARDS_TABLE_NUM_ENTRIES,
++};
++
+ struct csi2_device {
+ /* Parent V4l2 device */
+ struct v4l2_device *v4l2_dev;
+@@ -53,6 +63,12 @@ struct csi2_device {
+
+ struct media_pad pad[CSI2_NUM_CHANNELS * 2];
+ struct v4l2_subdev sd;
++
++ /* lock for csi2 errors counters */
++ spinlock_t errors_lock;
++ u32 overflows;
++ u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
++ u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
+ };
+
+ void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);
--- /dev/null
+From d6bd676b49cf10046665efde820b0cc00dc43994 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 28 Sep 2023 17:04:07 +0300
+Subject: [PATCH] media: rp1: cfe: Drop unused field
+
+Drop 'sensor_embedded_data' field, as it is unused.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -292,7 +292,6 @@ struct cfe_device {
+ struct csi2_device csi2;
+ struct pisp_fe_device fe;
+
+- bool sensor_embedded_data;
+ int fe_csi2_channel;
+
+ unsigned int sequence;
+@@ -1821,8 +1820,6 @@ static int cfe_probe_complete(struct cfe
+
+ cfe->v4l2_dev.notify = cfe_notify;
+
+- cfe->sensor_embedded_data = (cfe->sensor->entity.num_pads >= 2);
+-
+ for (i = 0; i < NUM_NODES; i++) {
+ ret = cfe_register_node(cfe, i);
+ if (ret) {
--- /dev/null
+From c56b53d62116b4672962124afab02f3beada4244 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 12:57:23 +0300
+Subject: [PATCH] media: rp1: csi2: Set values for enum csi2_mode
+
+Set hardcoded values for enum csi2_mode, as the values will be
+programmed to HW registers.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/csi2.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -20,10 +20,10 @@
+ #define DISCARDS_TABLE_NUM_VCS 4
+
+ enum csi2_mode {
+- CSI2_MODE_NORMAL,
+- CSI2_MODE_REMAP,
+- CSI2_MODE_COMPRESSED,
+- CSI2_MODE_FE_STREAMING
++ CSI2_MODE_NORMAL = 0,
++ CSI2_MODE_REMAP = 1,
++ CSI2_MODE_COMPRESSED = 2,
++ CSI2_MODE_FE_STREAMING = 3,
+ };
+
+ enum csi2_compression_mode {
--- /dev/null
+From 5edacf33de9e0349dd5a02ad684bfceae1d5565f Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 13:29:15 +0300
+Subject: [PATCH] media: rp1: fe: Fix default mbus code
+
+When pisp_fe_pad_set_fmt() is given an mbus code that CFE does not
+support, it currently defaults to MEDIA_BUS_FMT_SBGGR10_1X10. This is
+not correct, as FE does not support SBGGR10.
+
+Set the default to MEDIA_BUS_FMT_SRGGB16_1X16 instead.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -437,7 +437,7 @@ static int pisp_fe_pad_set_fmt(struct v4
+ case FE_OUTPUT1_PAD:
+ cfe_fmt = find_format_by_code(format->format.code);
+ if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
+- cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
+
+ format->format.code = cfe_fmt->code;
+
--- /dev/null
+From 47fd9528bc1d93dd07ce9baa19c736966b536bd9 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Mon, 2 Oct 2023 14:38:07 +0300
+Subject: [PATCH] media: rp1: cfe: Fix default meta format's field
+
+Set default meta format's field to V4L2_FIELD_NONE, instead of zeroing
+it which indicates V4L2_FIELD_ANY. Metadata doesn't have fields, so NONE
+makes sense, and furthermore the default v4l2 link validation will check
+for matching fields, or that the sink field is NONE.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -110,6 +110,7 @@ const struct v4l2_mbus_framefmt cfe_defa
+ .width = 8192,
+ .height = 1,
+ .code = MEDIA_BUS_FMT_SENSOR_DATA,
++ .field = V4L2_FIELD_NONE,
+ };
+
+ enum node_ids {
--- /dev/null
+From 3e2203b265ddd8630fea0fbb69b3a2ec1496f773 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 4 Oct 2023 09:39:59 +0100
+Subject: [PATCH] media: rp1: cfe: Fail streaming if FE_CONFIG node is not
+ enabled
+
+When the FE is enabled, ensure that the FE_CONFIG node is enabled.
+Otherwise fail cfe_start_streaming() entirely.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -997,6 +997,14 @@ static int cfe_start_streaming(struct vb
+ goto err_streaming;
+ }
+
++ /* When using the Frontend, we must enable the FE_CONFIG node. */
++ if (is_fe_enabled(cfe) &&
++ !check_state(cfe, NODE_ENABLED, cfe->node[FE_CONFIG].id)) {
++ cfe_err("FE enabled, but FE_CONFIG node is not\n");
++ ret = -EINVAL;
++ goto err_streaming;
++ }
++
+ ret = media_pipeline_start(&node->pad, &cfe->pipe);
+ if (ret < 0) {
+ cfe_err("Failed to start media pipeline: %d\n", ret);
--- /dev/null
+From 52811174f534824f5e5bcdb681f0c508aa335244 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 4 Oct 2023 11:09:10 +0100
+Subject: [PATCH] media: i2c: Move Kconfig entry for IMX477 to the camera
+ sensor section
+
+It was accidentally placed in the audio decoder section.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/Kconfig | 22 +++++++++++-----------
+ 1 file changed, 11 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -270,6 +270,17 @@ config VIDEO_IMX412
+ To compile this driver as a module, choose M here: the
+ module will be called imx412.
+
++config VIDEO_IMX477
++ tristate "Sony IMX477 sensor support"
++ depends on I2C && VIDEO_DEV
++ select VIDEO_V4L2_SUBDEV_API
++ help
++ This is a Video4Linux2 sensor driver for the Sony
++ IMX477 camera. Also supports the Sony IMX378.
++
++ To compile this driver as a module, choose M here: the
++ module will be called imx477.
++
+ config VIDEO_IMX519
+ tristate "Arducam IMX519 sensor support"
+ depends on I2C && VIDEO_DEV
+@@ -1106,17 +1117,6 @@ config VIDEO_UDA1342
+ To compile this driver as a module, choose M here: the
+ module will be called uda1342.
+
+-config VIDEO_IMX477
+- tristate "Sony IMX477 sensor support"
+- depends on I2C && VIDEO_DEV
+- select VIDEO_V4L2_SUBDEV_API
+- help
+- This is a Video4Linux2 sensor driver for the Sony
+- IMX477 camera. Also supports the Sony IMX378.
+-
+- To compile this driver as a module, choose M here: the
+- module will be called imx477.
+-
+ config VIDEO_VP27SMPX
+ tristate "Panasonic VP27's internal MPX"
+ depends on VIDEO_DEV && I2C
--- /dev/null
+From 3aa1f2477545ea6298bc6f2d7ffae68f090af9b8 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 28 Sep 2023 18:27:09 +0100
+Subject: [PATCH] drm: Look for an alias for the displays to use as the DRM
+ device name
+
+Allow DT aliases of eg DSI2 to force make DRM allocate the
+display with the requested name.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_connector.c | 60 ++++++++++++++++++++++++++++++---
+ 1 file changed, 56 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -81,6 +81,7 @@ struct drm_conn_prop_enum_list {
+ int type;
+ const char *name;
+ struct ida ida;
++ int first_dyn_num;
+ };
+
+ /*
+@@ -110,12 +111,41 @@ static struct drm_conn_prop_enum_list dr
+ { DRM_MODE_CONNECTOR_USB, "USB" },
+ };
+
++#define MAX_DT_NODE_NAME_LEN 20
++#define DT_DRM_NODE_PREFIX "drm_"
++
++static void drm_connector_get_of_name(int type, char *node_name, int length)
++{
++ int i = 0;
++
++ strcpy(node_name, DT_DRM_NODE_PREFIX);
++
++ do {
++ node_name[i + strlen(DT_DRM_NODE_PREFIX)] =
++ tolower(drm_connector_enum_list[type].name[i]);
++
++ } while (drm_connector_enum_list[type].name[i++] &&
++ i < length);
++
++ node_name[length - 1] = '\0';
++}
++
+ void drm_connector_ida_init(void)
+ {
+- int i;
++ int i, id;
++ char node_name[MAX_DT_NODE_NAME_LEN];
+
+- for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++)
++ for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) {
+ ida_init(&drm_connector_enum_list[i].ida);
++
++ drm_connector_get_of_name(i, node_name, MAX_DT_NODE_NAME_LEN);
++
++ id = of_alias_get_highest_id(node_name);
++ if (id > 0)
++ drm_connector_enum_list[i].first_dyn_num = id + 1;
++ else
++ drm_connector_enum_list[i].first_dyn_num = 1;
++ }
+ }
+
+ void drm_connector_ida_destroy(void)
+@@ -222,7 +252,9 @@ static int __drm_connector_init(struct d
+ struct i2c_adapter *ddc)
+ {
+ struct drm_mode_config *config = &dev->mode_config;
++ char node_name[MAX_DT_NODE_NAME_LEN];
+ int ret;
++ int id;
+ struct ida *connector_ida =
+ &drm_connector_enum_list[connector_type].ida;
+
+@@ -252,8 +284,28 @@ static int __drm_connector_init(struct d
+ ret = 0;
+
+ connector->connector_type = connector_type;
+- connector->connector_type_id =
+- ida_alloc_min(connector_ida, 1, GFP_KERNEL);
++ connector->connector_type_id = 0;
++
++ drm_connector_get_of_name(connector_type, node_name, MAX_DT_NODE_NAME_LEN);
++ id = of_alias_get_id(dev->dev->of_node, node_name);
++ if (id > 0) {
++ /* Try and allocate the requested ID
++ * Valid range is 1 to 31, hence ignoring 0 as an error
++ */
++ int type_id = ida_alloc_range(connector_ida, id, id, GFP_KERNEL);
++
++ if (type_id > 0)
++ connector->connector_type_id = type_id;
++ else
++ drm_err(dev, "Failed to acquire type ID %d for interface type %s, ret %d\n",
++ id, drm_connector_enum_list[connector_type].name,
++ type_id);
++ }
++ if (!connector->connector_type_id)
++ connector->connector_type_id =
++ ida_alloc_min(connector_ida,
++ drm_connector_enum_list[connector_type].first_dyn_num,
++ GFP_KERNEL);
+ if (connector->connector_type_id < 0) {
+ ret = connector->connector_type_id;
+ goto out_put_id;
--- /dev/null
+From 7ec42740a45b21bca859cde5b7cbe2f09ef3d586 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 28 Sep 2023 18:40:36 +0100
+Subject: [PATCH] dt: Add DSI1 and DSI2 aliases to 2712
+
+In order to keep the DRM names consistent as DSI-1 and DSI-2, add
+aliases to the Pi5 DT.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -785,6 +785,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ gpio4 = &pinctrl_aon;
+ usb0 = &rp1_usb0;
+ usb1 = &rp1_usb1;
++ drm_dsi1 = &dsi0;
++ drm_dsi2 = &dsi1;
+ };
+
+ __overrides__ {
--- /dev/null
+From e6e0631fdeb0cd7d4c50e629b4b298e0b76e885b Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Wed, 4 Oct 2023 16:02:39 +0100
+Subject: [PATCH] vc4/drm: Remove the clear of SCALER_DISPBKGND_FILL
+
+Since "drm/vc4: hvs: Support BCM2712 HVS" booting Pi4
+with dual 4kp30 displays connected fails with:
+vc4-drm gpu: [drm] *ERROR* [CRTC:107:pixelvalve-4] flip_done timed out
+
+It has been tracked down to the referenced commit adding a
+path to clear the SCALER_DISPBKGND_FILL when not required.
+
+Dual 4kp30 works with a core clock of 297MHz when background fill
+is enabled, but requires a higher value with it disabled.
+320MHz still fails, while 330MHz seems okay.
+
+Lets always enable background fill for Pi0-4.
+
+Fixes: e84da235223d ("drm/vc4: hvs: Support BCM2712 HVS")
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 20 +++++++++-----------
+ 1 file changed, 9 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1349,27 +1349,25 @@ void vc4_hvs_atomic_flush(struct drm_crt
+ WARN_ON(!vc4_state->mm);
+ WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm->mm_node.size);
+
+- if (enable_bg_fill) {
++ if (vc4->gen >= VC4_GEN_6) {
+ /* This sets a black background color fill, as is the case
+ * with other DRM drivers.
+ */
+- if (vc4->gen >= VC4_GEN_6)
++ if (enable_bg_fill)
+ HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+ HVS_READ(SCALER6_DISPX_CTRL1(channel)) |
+ SCALER6_DISPX_CTRL1_BGENB);
+ else
+- HVS_WRITE(SCALER_DISPBKGNDX(channel),
+- HVS_READ(SCALER_DISPBKGNDX(channel)) |
+- SCALER_DISPBKGND_FILL);
+- } else {
+- if (vc4->gen >= VC4_GEN_6)
+ HVS_WRITE(SCALER6_DISPX_CTRL1(channel),
+ HVS_READ(SCALER6_DISPX_CTRL1(channel)) &
+ ~SCALER6_DISPX_CTRL1_BGENB);
+- else
+- HVS_WRITE(SCALER_DISPBKGNDX(channel),
+- HVS_READ(SCALER_DISPBKGNDX(channel)) &
+- ~SCALER_DISPBKGND_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.
++ */
++ HVS_WRITE(SCALER_DISPBKGNDX(channel),
++ HVS_READ(SCALER_DISPBKGNDX(channel)) |
++ SCALER_DISPBKGND_FILL);
+ }
+
+ /* Only update DISPLIST if the CRTC was already running and is not
--- /dev/null
+From 450d617786926b27c25e930241efbd2e37d66bb8 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 6 Oct 2023 16:30:35 +0100
+Subject: [PATCH] gpio: brcmstb: Use dynamic GPIO base numbers
+
+Forcing a gpiochip to have a fixed base number now leads to a warning
+message. Remove the need to do so by calculating hwirq numbers based
+on bank numbers.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+Fixes: 3b0213d56eb7 ("gpio: Add GPIO support for Broadcom STB SoCs")
+---
+ drivers/gpio/gpio-brcmstb.c | 21 ++++++++++-----------
+ 1 file changed, 10 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpio/gpio-brcmstb.c
++++ b/drivers/gpio/gpio-brcmstb.c
+@@ -50,7 +50,6 @@ struct brcmstb_gpio_priv {
+ struct irq_domain *irq_domain;
+ struct irq_chip irq_chip;
+ int parent_irq;
+- int gpio_base;
+ int num_gpios;
+ int parent_wake_irq;
+ };
+@@ -92,7 +91,7 @@ brcmstb_gpio_get_active_irqs(struct brcm
+ static int brcmstb_gpio_hwirq_to_offset(irq_hw_number_t hwirq,
+ struct brcmstb_gpio_bank *bank)
+ {
+- return hwirq - (bank->gc.base - bank->parent_priv->gpio_base);
++ return hwirq - bank->id * 32;
+ }
+
+ static void brcmstb_gpio_set_imask(struct brcmstb_gpio_bank *bank,
+@@ -117,8 +116,9 @@ static void brcmstb_gpio_set_imask(struc
+ static int brcmstb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
+ {
+ struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
++ struct brcmstb_gpio_bank *bank = gpiochip_get_data(gc);
+ /* gc_offset is relative to this gpio_chip; want real offset */
+- int hwirq = offset + (gc->base - priv->gpio_base);
++ int hwirq = offset + bank->id * 32;
+
+ if (hwirq >= priv->num_gpios)
+ return -ENXIO;
+@@ -263,7 +263,7 @@ static void brcmstb_gpio_irq_bank_handle
+ {
+ struct brcmstb_gpio_priv *priv = bank->parent_priv;
+ struct irq_domain *domain = priv->irq_domain;
+- int hwbase = bank->gc.base - priv->gpio_base;
++ int hwbase = bank->id * 32;
+ unsigned long status;
+
+ while ((status = brcmstb_gpio_get_active_irqs(bank))) {
+@@ -414,7 +414,7 @@ static int brcmstb_gpio_of_xlate(struct
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+- offset = gpiospec->args[0] - (gc->base - priv->gpio_base);
++ offset = gpiospec->args[0] - bank->id * 32;
+ if (offset >= gc->ngpio || offset < 0)
+ return -EINVAL;
+
+@@ -598,8 +598,8 @@ static int brcmstb_gpio_probe(struct pla
+ const __be32 *p;
+ u32 bank_width;
+ int num_banks = 0;
++ int num_gpios = 0;
+ int err;
+- static int gpio_base;
+ unsigned long flags = 0;
+ bool need_wakeup_event = false;
+
+@@ -614,7 +614,6 @@ static int brcmstb_gpio_probe(struct pla
+ if (IS_ERR(reg_base))
+ return PTR_ERR(reg_base);
+
+- priv->gpio_base = gpio_base;
+ priv->reg_base = reg_base;
+ priv->pdev = pdev;
+
+@@ -656,7 +655,7 @@ static int brcmstb_gpio_probe(struct pla
+ dev_dbg(dev, "Width 0 found: Empty bank @ %d\n",
+ num_banks);
+ num_banks++;
+- gpio_base += MAX_GPIO_PER_BANK;
++ num_gpios += MAX_GPIO_PER_BANK;
+ continue;
+ }
+
+@@ -698,7 +697,7 @@ static int brcmstb_gpio_probe(struct pla
+ err = -ENOMEM;
+ goto fail;
+ }
+- gc->base = gpio_base;
++ gc->base = -1;
+ gc->of_gpio_n_cells = 2;
+ gc->of_xlate = brcmstb_gpio_of_xlate;
+ /* not all ngpio lines are valid, will use bank width later */
+@@ -722,7 +721,7 @@ static int brcmstb_gpio_probe(struct pla
+ bank->id);
+ goto fail;
+ }
+- gpio_base += gc->ngpio;
++ num_gpios += gc->ngpio;
+
+ dev_dbg(dev, "bank=%d, base=%d, ngpio=%d, width=%d\n", bank->id,
+ gc->base, gc->ngpio, bank->width);
+@@ -733,7 +732,7 @@ static int brcmstb_gpio_probe(struct pla
+ num_banks++;
+ }
+
+- priv->num_gpios = gpio_base - priv->gpio_base;
++ priv->num_gpios = num_gpios;
+ if (priv->parent_irq > 0) {
+ err = brcmstb_gpio_irq_setup(pdev, priv);
+ if (err)
--- /dev/null
+From 1770efc97981dbf756a18f5eb7615c4bafab665b Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Mon, 2 Oct 2023 15:12:52 +0100
+Subject: [PATCH] media/rpivid: Allow use of iommu in rpivid
+
+In order to use iommu on hevc set dma mask_and_coherent in probe.
+I am assured dma_set_mask_and_coherent is benign on Pi4 (which has
+no iommu) and it seems to be so in practice.
+Also adds a bit of debug to make internal buffer allocation failure
+easier to spot in future
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ drivers/staging/media/rpivid/rpivid.c | 7 +++++++
+ drivers/staging/media/rpivid/rpivid_h265.c | 12 ++++++++++--
+ 2 files changed, 17 insertions(+), 2 deletions(-)
+
+--- a/drivers/staging/media/rpivid/rpivid.c
++++ b/drivers/staging/media/rpivid/rpivid.c
+@@ -360,6 +360,13 @@ static int rpivid_probe(struct platform_
+ snprintf(vfd->name, sizeof(vfd->name), "%s", rpivid_video_device.name);
+ video_set_drvdata(vfd, dev);
+
++ ret = dma_set_mask_and_coherent(dev->dev, DMA_BIT_MASK(36));
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed dma_set_mask_and_coherent\n");
++ goto err_v4l2;
++ }
++
+ dev->m2m_dev = v4l2_m2m_init(&rpivid_m2m_ops);
+ if (IS_ERR(dev->m2m_dev)) {
+ v4l2_err(&dev->v4l2_dev,
+--- a/drivers/staging/media/rpivid/rpivid_h265.c
++++ b/drivers/staging/media/rpivid/rpivid_h265.c
+@@ -2495,11 +2495,19 @@ static int rpivid_h265_start(struct rpiv
+ for (i = 0; i != ARRAY_SIZE(ctx->pu_bufs); ++i) {
+ // Don't actually need a kernel mapping here
+ if (gptr_alloc(dev, ctx->pu_bufs + i, pu_alloc,
+- DMA_ATTR_NO_KERNEL_MAPPING))
++ DMA_ATTR_NO_KERNEL_MAPPING)) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to alloc %#zx PU%d buffer\n",
++ pu_alloc, i);
+ goto fail;
++ }
+ if (gptr_alloc(dev, ctx->coeff_bufs + i, coeff_alloc,
+- DMA_ATTR_NO_KERNEL_MAPPING))
++ DMA_ATTR_NO_KERNEL_MAPPING)) {
++ v4l2_err(&dev->v4l2_dev,
++ "Failed to alloc %#zx Coeff%d buffer\n",
++ pu_alloc, i);
+ goto fail;
++ }
+ }
+ aux_q_init(ctx);
+
--- /dev/null
+From 10ff8ba24c68f704ecf5a58878617dfe149a14c6 Mon Sep 17 00:00:00 2001
+From: John Cox <jc@kynesim.co.uk>
+Date: Mon, 2 Oct 2023 15:15:13 +0100
+Subject: [PATCH] dts/bcm2712: Add iommu to rpivid
+
+Add iommu to rpivid so it can cope with scatter/gather
+
+Signed-off-by: John Cox <jc@kynesim.co.uk>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -1158,6 +1158,7 @@
+ clocks = <&firmware_clocks 11>;
+ clock-names = "hevc";
+ status = "disabled";
++ iommus = <&iommu2>;
+ };
+
+ sdio1: mmc@fff000 {
--- /dev/null
+From b5c3cc7fd9fca73352310e61092fb445b56a362a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 10 Oct 2023 12:41:15 +0100
+Subject: [PATCH] drivers: media: rp1_cfe: Remove PISP specific MBUS formats
+
+Remove the MEDIA_BUS_FMT_PISP* format codcs entirely. For the image
+pad formats, use the 16-bit Bayer format mbus codes instead. For the
+config and stats pad formats, use MEDIA_BUS_FMT_FIXED.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe_fmts.h | 10 ++++++----
+ .../media/platform/raspberrypi/rp1_cfe/pisp_fe.c | 11 ++++-------
+ include/uapi/linux/media-bus-format.h | 14 --------------
+ 3 files changed, 10 insertions(+), 25 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -215,25 +215,25 @@ static const struct cfe_fmt formats[] =
+ /* PiSP Compressed Mode 1 */
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
+- .code = MEDIA_BUS_FMT_PISP_COMP1_RGGB,
++ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
+- .code = MEDIA_BUS_FMT_PISP_COMP1_BGGR,
++ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
+- .code = MEDIA_BUS_FMT_PISP_COMP1_GBRG,
++ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
+- .code = MEDIA_BUS_FMT_PISP_COMP1_GRBG,
++ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .depth = 8,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+@@ -283,10 +283,12 @@ static const struct cfe_fmt formats[] =
+ /* Frontend formats */
+ {
+ .fourcc = V4L2_META_FMT_RPI_FE_CFG,
++ .code = MEDIA_BUS_FMT_FIXED,
+ .flags = CFE_FORMAT_FLAG_META_OUT,
+ },
+ {
+ .fourcc = V4L2_META_FMT_RPI_FE_STATS,
++ .code = MEDIA_BUS_FMT_FIXED,
+ .flags = CFE_FORMAT_FLAG_META_CAP,
+ },
+ };
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -404,7 +404,7 @@ static int pisp_fe_init_cfg(struct v4l2_
+
+ fmt = v4l2_subdev_get_pad_format(sd, state, FE_CONFIG_PAD);
+ *fmt = cfe_default_meta_format;
+- fmt->code = MEDIA_BUS_FMT_PISP_FE_CONFIG;
++ fmt->code = MEDIA_BUS_FMT_FIXED;
+
+ fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD);
+ *fmt = cfe_default_format;
+@@ -416,7 +416,7 @@ static int pisp_fe_init_cfg(struct v4l2_
+
+ fmt = v4l2_subdev_get_pad_format(sd, state, FE_STATS_PAD);
+ *fmt = cfe_default_meta_format;
+- fmt->code = MEDIA_BUS_FMT_PISP_FE_STATS;
++ fmt->code = MEDIA_BUS_FMT_FIXED;
+
+ return 0;
+ }
+@@ -443,12 +443,9 @@ static int pisp_fe_pad_set_fmt(struct v4
+
+ break;
+
+- case FE_CONFIG_PAD:
+- format->format.code = MEDIA_BUS_FMT_PISP_FE_CONFIG;
+- break;
+-
+ case FE_STATS_PAD:
+- format->format.code = MEDIA_BUS_FMT_PISP_FE_STATS;
++ case FE_CONFIG_PAD:
++ format->format.code = MEDIA_BUS_FMT_FIXED;
+ break;
+ }
+
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -175,18 +175,4 @@
+ /* Sensor ancillary metadata formats - next is 0x7002 */
+ #define MEDIA_BUS_FMT_SENSOR_DATA 0x7002
+
+-/* PiSP Formats */
+-#define MEDIA_BUS_FMT_PISP_COMP1_RGGB 0x8001
+-#define MEDIA_BUS_FMT_PISP_COMP1_GRBG 0x8002
+-#define MEDIA_BUS_FMT_PISP_COMP1_GBRG 0x8003
+-#define MEDIA_BUS_FMT_PISP_COMP1_BGGR 0x8004
+-#define MEDIA_BUS_FMT_PISP_COMP2_RGGB 0x8005
+-#define MEDIA_BUS_FMT_PISP_COMP2_GRBG 0x8006
+-#define MEDIA_BUS_FMT_PISP_COMP2_GBRG 0x8007
+-#define MEDIA_BUS_FMT_PISP_COMP2_BGGR 0x8008
+-
+-#define MEDIA_BUS_FMT_PISP_FE_CONFIG 0x8100
+-#define MEDIA_BUS_FMT_PISP_FE_STATS 0x8101
+-#define MEDIA_BUS_FMT_PISP_BE_CONFIG 0x8200
+-
+ #endif /* __LINUX_MEDIA_BUS_FORMAT_H */
--- /dev/null
+From 9f4002165439d02a63760e68948246e3af764318 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Oct 2023 15:05:38 +0100
+Subject: [PATCH] vc04_services: bcm2835-codec: Correct alignment requirements
+ for YUYV
+
+The firmware wants the YUYV format stride alignment to be to a multiple
+of 32pixels / 64 bytes. The kernel driver was configuring it to a multiple
+of 16 pixels / 32 bytes, which then failed when it tried starting to
+stream.
+
+Correct the alignment requirements.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ .../vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
++++ b/drivers/staging/vc04_services/bcm2835-codec/bcm2835-v4l2-codec.c
+@@ -206,28 +206,28 @@ static const struct bcm2835_codec_fmt su
+ }, {
+ .fourcc = V4L2_PIX_FMT_YUYV,
+ .depth = 16,
+- .bytesperline_align = { 32, 32, 32, 32, 32 },
++ .bytesperline_align = { 64, 64, 64, 64, 64 },
+ .flags = 0,
+ .mmal_fmt = MMAL_ENCODING_YUYV,
+ .size_multiplier_x2 = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_UYVY,
+ .depth = 16,
+- .bytesperline_align = { 32, 32, 32, 32, 32 },
++ .bytesperline_align = { 64, 64, 64, 64, 64 },
+ .flags = 0,
+ .mmal_fmt = MMAL_ENCODING_UYVY,
+ .size_multiplier_x2 = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_YVYU,
+ .depth = 16,
+- .bytesperline_align = { 32, 32, 32, 32, 32 },
++ .bytesperline_align = { 64, 64, 64, 64, 64 },
+ .flags = 0,
+ .mmal_fmt = MMAL_ENCODING_YVYU,
+ .size_multiplier_x2 = 2,
+ }, {
+ .fourcc = V4L2_PIX_FMT_VYUY,
+ .depth = 16,
+- .bytesperline_align = { 32, 32, 32, 32, 32 },
++ .bytesperline_align = { 64, 64, 64, 64, 64 },
+ .flags = 0,
+ .mmal_fmt = MMAL_ENCODING_VYUY,
+ .size_multiplier_x2 = 2,
--- /dev/null
+From dd802fc03b1c71b4297e87068077bfcf9810300a Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 11 Oct 2023 15:14:59 +0100
+Subject: [PATCH] input: touchscreen: edt-ft5x06: Suppress bogus data on
+ startup
+
+When polled without the use of IRQ, FT5x06 registers may return
+undefined initial data, causing unwanted touches or event spamming.
+A simple way to filter this out is to suppress touches until the
+TD_STATUS register changes for the first time.
+
+Increase the delay before first polling to 300ms, to avoid
+transient I2C read flakiness that seems to occur after reset.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/input/touchscreen/edt-ft5x06.c | 31 +++++++++++++++++++++++---
+ 1 file changed, 28 insertions(+), 3 deletions(-)
+
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -75,6 +75,8 @@
+ #define EDT_DEFAULT_NUM_X 1024
+ #define EDT_DEFAULT_NUM_Y 1024
+
++#define RESET_DELAY_MS 300 /* reset deassert to I2C */
++#define FIRST_POLL_DELAY_MS 300 /* in addition to the above */
+ #define POLL_INTERVAL_MS 17 /* 17ms = 60fps */
+
+ enum edt_pmode {
+@@ -134,6 +136,7 @@ struct edt_ft5x06_ts_data {
+
+ char name[EDT_NAME_LEN];
+ char fw_version[EDT_NAME_LEN];
++ int init_td_status;
+
+ struct edt_reg_addr reg_addr;
+ enum edt_ver version;
+@@ -268,12 +271,33 @@ static irqreturn_t edt_ft5x06_ts_isr(int
+ * points.
+ */
+ num_points = min(rdbuf[2] & 0xf, tsdata->max_support_points);
++
++ /* When polling FT5x06 without IRQ: initial register contents
++ * could be stale or undefined; discard all readings until
++ * TD_STATUS changes for the first time (or num_points is 0).
++ */
++ if (tsdata->init_td_status) {
++ if (tsdata->init_td_status < 0)
++ tsdata->init_td_status = rdbuf[2];
++
++ if (num_points && rdbuf[2] == tsdata->init_td_status)
++ goto out;
++
++ tsdata->init_td_status = 0;
++ }
++
+ if (num_points) {
+ datalen = tplen * num_points + crclen;
+ cmd = offset;
+ error = edt_ft5x06_ts_readwrite(tsdata->client,
+ sizeof(cmd), &cmd,
+ datalen, &rdbuf[offset]);
++ if (error) {
++ dev_err_ratelimited(dev,
++ "Unable to fetch data, error: %d\n",
++ error);
++ goto out;
++ }
+ }
+ }
+
+@@ -1294,7 +1318,7 @@ static int edt_ft5x06_ts_probe(struct i2
+ if (tsdata->reset_gpio) {
+ usleep_range(5000, 6000);
+ gpiod_set_value_cansleep(tsdata->reset_gpio, 0);
+- msleep(300);
++ msleep(RESET_DELAY_MS);
+ }
+
+ input = devm_input_allocate_device(&client->dev);
+@@ -1384,11 +1408,12 @@ static int edt_ft5x06_ts_probe(struct i2
+ return error;
+ }
+ } else {
++ tsdata->init_td_status = -1; /* filter bogus initial data */
+ INIT_WORK(&tsdata->work_i2c_poll,
+ edt_ft5x06_ts_work_i2c_poll);
+ timer_setup(&tsdata->timer, edt_ft5x06_ts_irq_poll_timer, 0);
+- tsdata->timer.expires = jiffies +
+- msecs_to_jiffies(POLL_INTERVAL_MS);
++ tsdata->timer.expires =
++ jiffies + msecs_to_jiffies(FIRST_POLL_DELAY_MS);
+ add_timer(&tsdata->timer);
+ }
+
--- /dev/null
+From 27f0e0e195568c06f21ce380f0736bdd219baf3c Mon Sep 17 00:00:00 2001
+From: Janis Streib <janis+github@dogcraft.de>
+Date: Sun, 15 Oct 2023 21:08:40 +0200
+Subject: [PATCH] overlays: mcp23017: allow specification of the i2c bus
+
+Analogous to i2c-rtc-overlay.dts
+
+See: https://github.com/raspberrypi/linux/pull/5650
+---
+ arch/arm/boot/dts/overlays/README | 10 ++++++
+ .../boot/dts/overlays/mcp23017-overlay.dts | 36 +++++++++++++++++--
+ 2 files changed, 44 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2810,6 +2810,16 @@ 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
++ 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)
+
+
+ Name: mcp23s17
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -7,7 +7,7 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c1>;
++ target = <&i2cbus>;
+ __overlay__ {
+ status = "okay";
+ };
+@@ -24,7 +24,7 @@
+ };
+
+ fragment@2 {
+- target = <&i2c1>;
++ target = <&i2cbus>;
+ __overlay__ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+@@ -58,12 +58,44 @@
+ };
+ };
+
++ frag100: fragment@100 {
++ target = <&i2c1>;
++ i2cbus: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@101 {
++ target = <&i2c0if>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
++ fragment@102 {
++ target = <&i2c0mux>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ gpiopin = <&mcp23017_pins>,"brcm,pins:0",
+ <&mcp23017_irq>,"interrupts:0";
+ addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0";
+ mcp23008 = <0>,"=3";
+ noints = <0>,"!1!4";
++ i2c0 = <&frag100>, "target:0=",<&i2c0>;
++ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
++ <0>,"+101+102";
++ i2c3 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c3";
++ i2c4 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c4";
++ i2c5 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c5";
++ i2c6 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c6";
+ };
+ };
+
--- /dev/null
+From 0339c8c61ca6b54c529f699e7bafd65cda9f3733 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 16 Oct 2023 09:06:25 +0100
+Subject: [PATCH] dts: bcm2712: Set default I2C baudrates to 100kHz
+
+The RP1 I2C interfaces were being left with their default clock rates,
+apparently 400kHz.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm/boot/dts/bcm2712-rpi.dtsi | 2 ++
+ 2 files changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -250,11 +250,13 @@ i2c0mux: &rp1_gpio {};
+ i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only
+ pinctrl-0 = <&rp1_i2c6_38_39>;
+ pinctrl-names = "default";
++ clock-frequency = <100000>;
+ };
+
+ i2c_csi_dsi1: &i2c4 { // Note: This is for MIPI1 connector only
+ pinctrl-0 = <&rp1_i2c4_40_41>;
+ pinctrl-names = "default";
++ clock-frequency = <100000>;
+ };
+
+ i2c_csi_dsi: &i2c_csi_dsi1 { }; // An alias for compatibility
+--- a/arch/arm/boot/dts/bcm2712-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm2712-rpi.dtsi
+@@ -204,11 +204,13 @@ uart4: &rp1_uart4 { };
+ i2c_vc: &i2c0 { // This is pins 27,28 on the header (not MIPI)
+ pinctrl-0 = <&rp1_i2c0_0_1>;
+ pinctrl-names = "default";
++ clock-frequency = <100000>;
+ };
+
+ i2c_arm: &i2c1 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&rp1_i2c1_2_3>;
++ clock-frequency = <100000>;
+ };
+
+ &i2c2 {
--- /dev/null
+From 2a47ccf97c6a91bc56f8cfb387d47f59cc347dd5 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Sat, 14 Oct 2023 14:57:49 +0100
+Subject: [PATCH] vc_mem: Add the DMA memcpy support from bcm2708_fb
+
+bcm2708_fb is disabled by the vc4-kms-v3d overlay, which means that the
+DMA memcpy support it provides is not available to allow vclog to read
+the VC logs from the top 16MB on Pi 2 and Pi 3. Add the code to the
+vc_mem driver, which will still be enabled.
+
+It ought to be possible to do a proper DMA_MEM_TO_MEM copy via the
+generic DMA customer API, but that can be a later step.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/char/broadcom/vc_mem.c | 259 +++++++++++++++++++++++++++++++++
+ 1 file changed, 259 insertions(+)
+
+--- a/drivers/char/broadcom/vc_mem.c
++++ b/drivers/char/broadcom/vc_mem.c
+@@ -23,9 +23,21 @@
+ #include <linux/uaccess.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/broadcom/vc_mem.h>
++#include <linux/compat.h>
++#include <linux/platform_data/dma-bcm2708.h>
++#include <soc/bcm2835/raspberrypi-firmware.h>
+
+ #define DRIVER_NAME "vc-mem"
+
++/* N.B. These use a different magic value for compatibility with bmc7208_fb */
++#define VC_MEM_IOC_DMACOPY _IOW('z', 0x22, struct vc_mem_dmacopy)
++#define VC_MEM_IOC_DMACOPY32 _IOW('z', 0x22, struct vc_mem_dmacopy32)
++
++/* address with no aliases */
++#define INTALIAS_NORMAL(x) ((x) & ~0xc0000000)
++/* cache coherent but non-allocating in L1 and L2 */
++#define INTALIAS_L1L2_NONALLOCATING(x) (((x) & ~0xc0000000) | 0x80000000)
++
+ /* Device (/dev) related variables */
+ static dev_t vc_mem_devnum;
+ static struct class *vc_mem_class;
+@@ -36,6 +48,20 @@ static int vc_mem_inited;
+ static struct dentry *vc_mem_debugfs_entry;
+ #endif
+
++struct vc_mem_dmacopy {
++ void *dst;
++ __u32 src;
++ __u32 length;
++};
++
++#ifdef CONFIG_COMPAT
++struct vc_mem_dmacopy32 {
++ compat_uptr_t dst;
++ __u32 src;
++ __u32 length;
++};
++#endif
++
+ /*
+ * Videocore memory addresses and size
+ *
+@@ -62,6 +88,20 @@ static uint phys_addr;
+ static uint mem_size;
+ static uint mem_base;
+
++struct vc_mem_dma {
++ struct device *dev;
++ int dma_chan;
++ int dma_irq;
++ void __iomem *dma_chan_base;
++ wait_queue_head_t dma_waitq;
++ void *cb_base; /* DMA control blocks */
++ dma_addr_t cb_handle;
++};
++
++struct { u32 base, length; } gpu_mem;
++static struct mutex dma_mutex;
++static struct vc_mem_dma vc_mem_dma;
++
+ static int
+ vc_mem_open(struct inode *inode, struct file *file)
+ {
+@@ -99,6 +139,189 @@ vc_mem_get_current_size(void)
+ }
+ EXPORT_SYMBOL_GPL(vc_mem_get_current_size);
+
++static int
++vc_mem_dma_init(void)
++{
++ struct vc_mem_dma *vcdma = &vc_mem_dma;
++ struct platform_device *pdev;
++ struct device_node *fwnode;
++ struct rpi_firmware *fw;
++ struct device *dev;
++ u32 revision;
++ int rc;
++
++ if (vcdma->dev)
++ return 0;
++
++ fwnode = of_find_node_by_path("/system");
++ rc = of_property_read_u32(fwnode, "linux,revision", &revision);
++ revision = (revision >> 12) & 0xf;
++ if (revision != 1 && revision != 2) {
++ /* Only BCM2709 and BCM2710 may have logs where the ARMs
++ * can't see them.
++ */
++ return -ENXIO;
++ }
++
++ fwnode = rpi_firmware_find_node();
++ if (!fwnode)
++ return -ENXIO;
++
++ pdev = of_find_device_by_node(fwnode);
++ dev = &pdev->dev;
++
++ rc = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
++ if (rc)
++ return rc;
++
++ fw = rpi_firmware_get(fwnode);
++ if (!fw)
++ return -ENXIO;
++ rc = rpi_firmware_property(fw, RPI_FIRMWARE_GET_VC_MEMORY,
++ &gpu_mem, sizeof(gpu_mem));
++ if (rc)
++ return rc;
++
++ gpu_mem.base = INTALIAS_NORMAL(gpu_mem.base);
++
++ if (!gpu_mem.base || !gpu_mem.length) {
++ dev_err(dev, "%s: unable to determine gpu memory (%x,%x)\n",
++ __func__, gpu_mem.base, gpu_mem.length);
++ return -EFAULT;
++ }
++
++ vcdma->cb_base = dma_alloc_wc(dev, SZ_4K, &vcdma->cb_handle, GFP_KERNEL);
++ if (!vcdma->cb_base) {
++ dev_err(dev, "failed to allocate DMA CBs\n");
++ return -ENOMEM;
++ }
++
++ rc = bcm_dma_chan_alloc(BCM_DMA_FEATURE_BULK,
++ &vcdma->dma_chan_base,
++ &vcdma->dma_irq);
++ if (rc < 0) {
++ dev_err(dev, "failed to allocate a DMA channel\n");
++ goto free_cb;
++ }
++
++ vcdma->dma_chan = rc;
++
++ init_waitqueue_head(&vcdma->dma_waitq);
++
++ vcdma->dev = dev;
++
++ return 0;
++
++free_cb:
++ dma_free_wc(dev, SZ_4K, vcdma->cb_base, vcdma->cb_handle);
++
++ return rc;
++}
++
++static void
++vc_mem_dma_uninit(void)
++{
++ struct vc_mem_dma *vcdma = &vc_mem_dma;
++
++ if (vcdma->dev) {
++ bcm_dma_chan_free(vcdma->dma_chan);
++ dma_free_wc(vcdma->dev, SZ_4K, vcdma->cb_base, vcdma->cb_handle);
++ vcdma->dev = NULL;
++ }
++}
++
++static int dma_memcpy(struct vc_mem_dma *vcdma, dma_addr_t dst, dma_addr_t src,
++ int size)
++{
++ struct bcm2708_dma_cb *cb = vcdma->cb_base;
++ int burst_size = (vcdma->dma_chan == 0) ? 8 : 2;
++
++ cb->info = BCM2708_DMA_BURST(burst_size) | BCM2708_DMA_S_WIDTH |
++ BCM2708_DMA_S_INC | BCM2708_DMA_D_WIDTH |
++ BCM2708_DMA_D_INC;
++ cb->dst = dst;
++ cb->src = src;
++ cb->length = size;
++ cb->stride = 0;
++ cb->pad[0] = 0;
++ cb->pad[1] = 0;
++ cb->next = 0;
++
++ bcm_dma_start(vcdma->dma_chan_base, vcdma->cb_handle);
++ bcm_dma_wait_idle(vcdma->dma_chan_base);
++
++ return 0;
++}
++
++static long vc_mem_copy(struct vc_mem_dmacopy *ioparam)
++{
++ struct vc_mem_dma *vcdma = &vc_mem_dma;
++ size_t size = PAGE_SIZE;
++ const u32 dma_xfer_chunk = 256;
++ u32 *buf = NULL;
++ dma_addr_t bus_addr;
++ long rc = 0;
++ size_t offset;
++
++ /* restrict this to root user */
++ if (!uid_eq(current_euid(), GLOBAL_ROOT_UID))
++ return -EFAULT;
++
++ if (mutex_lock_interruptible(&dma_mutex))
++ return -EINTR;
++
++ rc = vc_mem_dma_init();
++ if (rc)
++ goto out;
++
++ vcdma = &vc_mem_dma;
++
++ if (INTALIAS_NORMAL(ioparam->src) < gpu_mem.base ||
++ INTALIAS_NORMAL(ioparam->src) >= gpu_mem.base + gpu_mem.length) {
++ pr_err("%s: invalid memory access %x (%x-%x)", __func__,
++ INTALIAS_NORMAL(ioparam->src), gpu_mem.base,
++ gpu_mem.base + gpu_mem.length);
++ rc = -EFAULT;
++ goto out;
++ }
++
++ buf = dma_alloc_coherent(vcdma->dev, PAGE_ALIGN(size), &bus_addr,
++ GFP_ATOMIC);
++ if (!buf) {
++ rc = -ENOMEM;
++ goto out;
++ }
++
++ for (offset = 0; offset < ioparam->length; offset += size) {
++ size_t remaining = ioparam->length - offset;
++ size_t s = min(size, remaining);
++ u8 *p = (u8 *)((uintptr_t)ioparam->src + offset);
++ u8 *q = (u8 *)ioparam->dst + offset;
++
++ rc = dma_memcpy(vcdma, bus_addr,
++ INTALIAS_L1L2_NONALLOCATING((u32)(uintptr_t)p),
++ (s + dma_xfer_chunk - 1) & ~(dma_xfer_chunk - 1));
++ if (rc) {
++ dev_err(vcdma->dev, "dma_memcpy failed\n");
++ break;
++ }
++ if (copy_to_user(q, buf, s) != 0) {
++ pr_err("%s: copy_to_user failed\n", __func__);
++ rc = -EFAULT;
++ break;
++ }
++ }
++
++out:
++ if (buf)
++ dma_free_coherent(vcdma->dev, PAGE_ALIGN(size), buf,
++ bus_addr);
++
++ mutex_unlock(&dma_mutex);
++
++ return rc;
++}
++
+ static long
+ vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+ {
+@@ -163,6 +386,21 @@ vc_mem_ioctl(struct file *file, unsigned
+ }
+ break;
+ }
++ case VC_MEM_IOC_DMACOPY:
++ {
++ struct vc_mem_dmacopy ioparam;
++ /* Get the parameter data.
++ */
++ if (copy_from_user
++ (&ioparam, (void *)arg, sizeof(ioparam))) {
++ pr_err("%s: copy_from_user failed\n", __func__);
++ rc = -EFAULT;
++ break;
++ }
++
++ rc = vc_mem_copy(&ioparam);
++ break;
++ }
+ default:
+ {
+ return -ENOTTY;
+@@ -193,6 +431,24 @@ vc_mem_compat_ioctl(struct file *file, u
+
+ break;
+
++ case VC_MEM_IOC_DMACOPY32:
++ {
++ struct vc_mem_dmacopy32 param32;
++ struct vc_mem_dmacopy param;
++ /* Get the parameter data.
++ */
++ if (copy_from_user(¶m32, (void *)arg, sizeof(param32))) {
++ pr_err("%s: copy_from_user failed\n", __func__);
++ rc = -EFAULT;
++ break;
++ }
++ param.dst = compat_ptr(param32.dst);
++ param.src = param32.src;
++ param.length = param32.length;
++ rc = vc_mem_copy(¶m);
++ break;
++ }
++
+ default:
+ rc = vc_mem_ioctl(file, cmd, arg);
+ break;
+@@ -330,6 +586,7 @@ vc_mem_init(void)
+ vc_mem_debugfs_init(dev);
+ #endif
+
++ mutex_init(&dma_mutex);
+ vc_mem_inited = 1;
+ return 0;
+
+@@ -352,6 +609,7 @@ vc_mem_exit(void)
+ {
+ pr_debug("%s: called\n", __func__);
+
++ vc_mem_dma_uninit();
+ if (vc_mem_inited) {
+ #ifdef CONFIG_DEBUG_FS
+ vc_mem_debugfs_deinit();
+@@ -360,6 +618,7 @@ vc_mem_exit(void)
+ class_destroy(vc_mem_class);
+ cdev_del(&vc_mem_cdev);
+ unregister_chrdev_region(vc_mem_devnum, 1);
++ vc_mem_inited = 0;
+ }
+ }
+
--- /dev/null
+From 6ab30a5dd3fb2ccbd918f147e91eb9bfe9849970 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 16 Oct 2023 12:13:38 +0100
+Subject: [PATCH] drm/vc4: Correct address offset for planes with src_[xy]
+ offsets
+
+11cf37e741b4 switched to using drm_fb_dma_get_gem_addr instead of
+drm_fb_dma_get_gem_obj and adding fb->offset[].
+
+However the tiled formats need to compute the offset in a more
+involved manner than drm_fb_dma_get_gem_addr applies, and we
+were ending up with the offset for src_[xy] being applied twice.
+
+Switch back to using drm_fb_dma_get_gem_obj and fully computing
+the offsets ourselves.
+
+Fixes: 11cf37e741b4 ("drm/vc4: Move the buffer offset out of the vc4_plane_state")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1447,9 +1447,9 @@ static int vc4_plane_mode_set(struct drm
+ vc4_state->ptr0_offset[0] = vc4_state->dlist_count;
+
+ for (i = 0; i < num_planes; i++) {
+- dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
++ struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, i);
+
+- vc4_dlist_write(vc4_state, paddr + offsets[i]);
++ vc4_dlist_write(vc4_state, bo->dma_addr + fb->offsets[i] + offsets[i]);
+ }
+
+ /* Pointer Context Word 0/1/2: Written by the HVS */
+@@ -1842,9 +1842,8 @@ static int vc6_plane_mode_set(struct drm
+ * TODO: This only covers Raster Scan Order planes
+ */
+ for (i = 0; i < num_planes; i++) {
+- dma_addr_t paddr = drm_fb_dma_get_gem_addr(fb, state, i);
+-
+- paddr += offsets[i];
++ struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, i);
++ dma_addr_t paddr = bo->dma_addr + fb->offsets[i] + offsets[i];
+
+ /* Pointer Word 0 */
+ vc4_state->ptr0_offset[i] = vc4_state->dlist_count;
--- /dev/null
+From c14550658832026e82111f2a89b3f7bf567afc1c Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Tue, 17 Oct 2023 09:35:44 +0100
+Subject: [PATCH] drivers: media: rp1_cfe: Fix link validate test for pixel
+ format
+
+Now that we have removed unique PISP media bus codes, the cfe format
+table has multiple entries with the same media bus code for 16-bit
+formats. The test in cfe_video_link_validate() did not account for this.
+Fix it by testing the media bus code and the V4L2 pixelformat 4cc
+together.
+
+As a drive-by, ensure we have a valid CSI2 datatype id when programming
+the hardware block.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 19 ++++++++++++++++---
+ 1 file changed, 16 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -786,6 +786,9 @@ static void cfe_start_channel(struct cfe
+ width = source_fmt->width;
+ height = source_fmt->height;
+
++ /* Must have a valid CSI2 datatype. */
++ WARN_ON(!fmt->csi_dt);
++
+ /*
+ * Start the associated CSI2 Channel as well.
+ *
+@@ -809,6 +812,9 @@ static void cfe_start_channel(struct cfe
+ node_desc[node->id].link_pad - CSI2_NUM_CHANNELS);
+ fmt = find_format_by_code(source_fmt->code);
+
++ /* Must have a valid CSI2 datatype. */
++ WARN_ON(!fmt->csi_dt);
++
+ if (is_image_output_node(node)) {
+ width = source_fmt->width;
+ height = source_fmt->height;
+@@ -1504,7 +1510,8 @@ static int cfe_video_link_validate(struc
+
+ if (is_image_output_node(node)) {
+ struct v4l2_pix_format *pix_fmt = &node->fmt.fmt.pix;
+- const struct cfe_fmt *fmt;
++ const struct cfe_fmt *fmt = NULL;
++ unsigned int i;
+
+ if (source_fmt->width != pix_fmt->width ||
+ source_fmt->height != pix_fmt->height) {
+@@ -1516,8 +1523,14 @@ static int cfe_video_link_validate(struc
+ goto out;
+ }
+
+- fmt = find_format_by_code(source_fmt->code);
+- if (!fmt || fmt->fourcc != pix_fmt->pixelformat) {
++ for (i = 0; i < ARRAY_SIZE(formats); i++) {
++ if (formats[i].code == source_fmt->code &&
++ formats[i].fourcc == pix_fmt->pixelformat) {
++ fmt = &formats[i];
++ break;
++ }
++ }
++ if (!fmt) {
+ cfe_err("Format mismatch!\n");
+ ret = -EINVAL;
+ goto out;
--- /dev/null
+From b6bfece0d9ddf21e1526fead81340ef02f98f6ad Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 17 Oct 2023 17:28:11 +0100
+Subject: [PATCH] dts: bcm2712: Use the new model name
+
+"Model B" is no more - "Raspberry Pi 5" is the official name.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -52,7 +52,7 @@
+
+ / {
+ compatible = "raspberrypi,5-model-b", "brcm,bcm2712";
+- model = "Raspberry Pi 5 Model B";
++ model = "Raspberry Pi 5";
+
+ /* Will be filled by the bootloader */
+ memory@0 {
--- /dev/null
+From 0c7fb448e0e0e47c2b3be64e438208682c6a5e61 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Oct 2023 16:32:45 +0100
+Subject: [PATCH] fbdev: Allow client to request a particular /dev/fbN node
+
+Add a flag custom_fb_num to denote that the client has
+requested a specific fbdev node number via node.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/video/fbdev/core/fbmem.c | 24 +++++++++++++++++-------
+ include/linux/fb.h | 2 ++
+ 2 files changed, 19 insertions(+), 7 deletions(-)
+
+--- a/drivers/video/fbdev/core/fbmem.c
++++ b/drivers/video/fbdev/core/fbmem.c
+@@ -52,6 +52,7 @@ static DEFINE_MUTEX(registration_lock);
+
+ struct fb_info *registered_fb[FB_MAX] __read_mostly;
+ int num_registered_fb __read_mostly;
++int min_dynamic_fb __read_mostly;
+ #define for_each_registered_fb(i) \
+ for (i = 0; i < FB_MAX; i++) \
+ if (!registered_fb[i]) {} else
+@@ -1579,19 +1580,22 @@ static int do_register_framebuffer(struc
+ return -ENXIO;
+
+ num_registered_fb++;
+- for (i = 0 ; i < FB_MAX; i++)
+- if (!registered_fb[i])
+- break;
+- fb_info->node = i;
++ if (!fb_info->custom_fb_num || fb_info->node >= FB_MAX || registered_fb[fb_info->node]) {
++ for (i = min_dynamic_fb ; i < FB_MAX; i++)
++ if (!registered_fb[i])
++ break;
++ fb_info->node = i;
++ }
+ refcount_set(&fb_info->count, 1);
+ mutex_init(&fb_info->lock);
+ mutex_init(&fb_info->mm_lock);
+
+ fb_info->dev = device_create(fb_class, fb_info->device,
+- MKDEV(FB_MAJOR, i), NULL, "fb%d", i);
++ MKDEV(FB_MAJOR, fb_info->node), NULL, "fb%d", fb_info->node);
+ if (IS_ERR(fb_info->dev)) {
+ /* Not fatal */
+- printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev));
++ printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n",
++ fb_info->node, PTR_ERR(fb_info->dev));
+ fb_info->dev = NULL;
+ } else
+ fb_init_device(fb_info);
+@@ -1624,7 +1628,7 @@ static int do_register_framebuffer(struc
+
+ fb_var_to_videomode(&mode, &fb_info->var);
+ fb_add_videomode(&mode, &fb_info->modelist);
+- registered_fb[i] = fb_info;
++ registered_fb[fb_info->node] = fb_info;
+
+ #ifdef CONFIG_GUMSTIX_AM200EPD
+ {
+@@ -1719,6 +1723,12 @@ static int fb_aperture_acquire_for_platf
+ return ret;
+ }
+
++void fb_set_lowest_dynamic_fb(int min_fb_dev)
++{
++ min_dynamic_fb = min_fb_dev;
++}
++EXPORT_SYMBOL(fb_set_lowest_dynamic_fb);
++
+ /**
+ * register_framebuffer - registers a frame buffer device
+ * @fb_info: frame buffer info structure
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -512,6 +512,7 @@ struct fb_info {
+ } *apertures;
+
+ bool skip_vt_switch; /* no VT switch on suspend/resume required */
++ bool custom_fb_num; /* Use value in node as the preferred node number */
+ };
+
+ static inline struct apertures_struct *alloc_apertures(unsigned int max_num) {
+@@ -614,6 +615,7 @@ extern ssize_t fb_sys_write(struct fb_in
+ size_t count, loff_t *ppos);
+
+ /* drivers/video/fbmem.c */
++extern void fb_set_lowest_dynamic_fb(int min_fb_dev);
+ extern int register_framebuffer(struct fb_info *fb_info);
+ extern void unregister_framebuffer(struct fb_info *fb_info);
+ extern int fb_prepare_logo(struct fb_info *fb_info, int rotate);
--- /dev/null
+From 1216ea56c2e30aee4975b4dcce79ebd199afaf8f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 9 Oct 2023 16:34:36 +0100
+Subject: [PATCH] drm/fb-helper: Look up preferred fbdev node number from DT
+
+For situations where there are multiple DRM cards in a system,
+add a query of DT for "drm_fb" designations for cards to set
+their preferred /dev/fbN designation.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -1932,7 +1932,7 @@ __drm_fb_helper_initial_config_and_unloc
+ struct drm_device *dev = fb_helper->dev;
+ struct fb_info *info;
+ unsigned int width, height;
+- int ret;
++ int ret, id;
+
+ width = dev->mode_config.max_width;
+ height = dev->mode_config.max_height;
+@@ -1967,6 +1967,15 @@ __drm_fb_helper_initial_config_and_unloc
+ * register the fbdev emulation instance in kernel_fb_helper_list. */
+ mutex_unlock(&fb_helper->lock);
+
++ id = of_alias_get_highest_id("drm_fb");
++ if (id >= 0)
++ fb_set_lowest_dynamic_fb(id + 1);
++
++ id = of_alias_get_id(dev->dev->of_node, "drm_fb");
++ if (id >= 0) {
++ info->node = id;
++ info->custom_fb_num = true;
++ }
+ ret = register_framebuffer(info);
+ if (ret < 0)
+ return ret;
--- /dev/null
+From 61b138adaeaddefe749f421a3b69c67d4a49a8e3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 11 Oct 2023 11:03:51 +0100
+Subject: [PATCH] dt: Add overrides for drm framebuffer allocations on Pi5
+
+Adds dtparam overrides to the base Pi5 DT such that vc4,
+DSI0, DSI1, or DPI can be requested to be /dev/fb[012].
+No override is specified by default, so the order will be
+based on probe order (aka semi-random). Any device that
+doesn't have an override specified will be placed above
+all specified overrides. Having an fb1 or fb2 override but
+no fb0 one will result in no console via fbcon.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 12 ++++++++++++
+ arch/arm/boot/dts/overlays/README | 24 ++++++++++++++++++++++++
+ 2 files changed, 36 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -824,5 +824,17 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ act_led_trigger = <&act_led>, "linux,default-trigger";
+ pwr_led_activelow = <&pwr_led>, "gpios:8";
+ pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
++ drm_fb0_rp1_dsi0 = <&aliases>, "drm_fb0=",&dsi0;
++ drm_fb0_rp1_dsi1 = <&aliases>, "drm_fb0=",&dsi1;
++ drm_fb0_rp1_dpi = <&aliases>, "drm_fb0=",&dpi;
++ drm_fb0_vc4 = <&aliases>, "drm_fb0=",&vc4;
++ drm_fb1_rp1_dsi0 = <&aliases>, "drm_fb1=",&dsi0;
++ drm_fb1_rp1_dsi1 = <&aliases>, "drm_fb1=",&dsi1;
++ drm_fb1_rp1_dpi = <&aliases>, "drm_fb1=",&dpi;
++ drm_fb1_vc4 = <&aliases>, "drm_fb1=",&vc4;
++ drm_fb2_rp1_dsi0 = <&aliases>, "drm_fb2=",&dsi0;
++ drm_fb2_rp1_dsi1 = <&aliases>, "drm_fb2=",&dsi1;
++ drm_fb2_rp1_dpi = <&aliases>, "drm_fb2=",&dpi;
++ drm_fb2_vc4 = <&aliases>, "drm_fb2=",&vc4;
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -173,6 +173,30 @@ Params:
+ cooling_fan Enables the Pi 5 cooling fan (enabled
+ automatically by the firmware)
+
++ drm_fb0_rp1_dpi Assign /dev/fb0 to the RP1 DPI output
++
++ drm_fb0_rp1_dsi0 Assign /dev/fb0 to the RP1 DSI0 output
++
++ drm_fb0_rp1_dsi1 Assign /dev/fb0 to the RP1 DSI1 output
++
++ drm_fb0_vc4 Assign /dev/fb0 to the vc4 outputs
++
++ drm_fb1_rp1_dpi Assign /dev/fb1 to the RP1 DPI output
++
++ drm_fb1_rp1_dsi0 Assign /dev/fb1 to the RP1 DSI0 output
++
++ drm_fb1_rp1_dsi1 Assign /dev/fb1 to the RP1 DSI1 output
++
++ drm_fb1_vc4 Assign /dev/fb1 to the vc4 outputs
++
++ drm_fb2_rp1_dpi Assign /dev/fb2 to the RP1 DPI output
++
++ drm_fb2_rp1_dsi0 Assign /dev/fb2 to the RP1 DSI0 output
++
++ drm_fb2_rp1_dsi1 Assign /dev/fb2 to the RP1 DSI1 output
++
++ drm_fb2_vc4 Assign /dev/fb2 to the vc4 outputs
++
+ eee Enable Energy Efficient Ethernet support for
+ compatible devices (default "on"). See also
+ "tx_lpi_timer". Pi3B+ only.
--- /dev/null
+From f429fc1a072d4bb35e622a1012a5a52914eba4e3 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:34:58 +0100
+Subject: [PATCH] drm/connector: Change DRM card alias from underscore to
+ hyphen
+
+Apparently aliases are only allowed lower case and hyphens,
+so swap the use of underscore to hyphen.
+
+Fixes: 3aa1f2477545 ("drm: Look for an alias for the displays to use as the DRM device name")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_connector.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/drm_connector.c
++++ b/drivers/gpu/drm/drm_connector.c
+@@ -112,7 +112,7 @@ static struct drm_conn_prop_enum_list dr
+ };
+
+ #define MAX_DT_NODE_NAME_LEN 20
+-#define DT_DRM_NODE_PREFIX "drm_"
++#define DT_DRM_NODE_PREFIX "drm-"
+
+ static void drm_connector_get_of_name(int type, char *node_name, int length)
+ {
--- /dev/null
+From e33170e21494279801e9f17eeb910ec45ebd740c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:28:43 +0100
+Subject: [PATCH] dt: Alter alias names from _ to - for drm_dsiN
+
+Fixes: 7ec42740a45b ("dt: Add DSI1 and DSI2 aliases to 2712")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -787,8 +787,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ gpio4 = &pinctrl_aon;
+ usb0 = &rp1_usb0;
+ usb1 = &rp1_usb1;
+- drm_dsi1 = &dsi0;
+- drm_dsi2 = &dsi1;
++ drm-dsi1 = &dsi0;
++ drm-dsi2 = &dsi1;
+ };
+
+ __overrides__ {
--- /dev/null
+From 0e9e925112fabdbd448e17947796317a6dbca96e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:32:04 +0100
+Subject: [PATCH] drm/fb_helper: Change query for FB designation from drm_fb to
+ drm-fb
+
+Fixes: 1216ea56c2e3 ("drm/fb-helper: Look up preferred fbdev node number from DT")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -1967,11 +1967,11 @@ __drm_fb_helper_initial_config_and_unloc
+ * register the fbdev emulation instance in kernel_fb_helper_list. */
+ mutex_unlock(&fb_helper->lock);
+
+- id = of_alias_get_highest_id("drm_fb");
++ id = of_alias_get_highest_id("drm-fb");
+ if (id >= 0)
+ fb_set_lowest_dynamic_fb(id + 1);
+
+- id = of_alias_get_id(dev->dev->of_node, "drm_fb");
++ id = of_alias_get_id(dev->dev->of_node, "drm-fb");
+ if (id >= 0) {
+ info->node = id;
+ info->custom_fb_num = true;
--- /dev/null
+From 87be94059193d0bc64d52a9535df4fb1891c72fe Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 19 Oct 2023 10:29:20 +0100
+Subject: [PATCH] dt: Alter alias names from _ to - for drm_fbN_* overrides
+
+Fixes: 61b138adaead ("dt: Add overrides for drm framebuffer allocations on Pi5")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 24 ++++++++++++------------
+ 1 file changed, 12 insertions(+), 12 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -824,17 +824,17 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ act_led_trigger = <&act_led>, "linux,default-trigger";
+ pwr_led_activelow = <&pwr_led>, "gpios:8";
+ pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
+- drm_fb0_rp1_dsi0 = <&aliases>, "drm_fb0=",&dsi0;
+- drm_fb0_rp1_dsi1 = <&aliases>, "drm_fb0=",&dsi1;
+- drm_fb0_rp1_dpi = <&aliases>, "drm_fb0=",&dpi;
+- drm_fb0_vc4 = <&aliases>, "drm_fb0=",&vc4;
+- drm_fb1_rp1_dsi0 = <&aliases>, "drm_fb1=",&dsi0;
+- drm_fb1_rp1_dsi1 = <&aliases>, "drm_fb1=",&dsi1;
+- drm_fb1_rp1_dpi = <&aliases>, "drm_fb1=",&dpi;
+- drm_fb1_vc4 = <&aliases>, "drm_fb1=",&vc4;
+- drm_fb2_rp1_dsi0 = <&aliases>, "drm_fb2=",&dsi0;
+- drm_fb2_rp1_dsi1 = <&aliases>, "drm_fb2=",&dsi1;
+- drm_fb2_rp1_dpi = <&aliases>, "drm_fb2=",&dpi;
+- drm_fb2_vc4 = <&aliases>, "drm_fb2=",&vc4;
++ drm_fb0_rp1_dsi0 = <&aliases>, "drm-fb0=",&dsi0;
++ drm_fb0_rp1_dsi1 = <&aliases>, "drm-fb0=",&dsi1;
++ drm_fb0_rp1_dpi = <&aliases>, "drm-fb0=",&dpi;
++ drm_fb0_vc4 = <&aliases>, "drm-fb0=",&vc4;
++ drm_fb1_rp1_dsi0 = <&aliases>, "drm-fb1=",&dsi0;
++ drm_fb1_rp1_dsi1 = <&aliases>, "drm-fb1=",&dsi1;
++ drm_fb1_rp1_dpi = <&aliases>, "drm-fb1=",&dpi;
++ drm_fb1_vc4 = <&aliases>, "drm-fb1=",&vc4;
++ drm_fb2_rp1_dsi0 = <&aliases>, "drm-fb2=",&dsi0;
++ drm_fb2_rp1_dsi1 = <&aliases>, "drm-fb2=",&dsi1;
++ drm_fb2_rp1_dpi = <&aliases>, "drm-fb2=",&dpi;
++ drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4;
+ };
+ };
--- /dev/null
+From cb8a4adb586c5e926e415ac0dae3ffb4af30b0a9 Mon Sep 17 00:00:00 2001
+From: Andrew Scheller <andrew.scheller@raspberrypi.com>
+Date: Thu, 19 Oct 2023 14:13:36 +0100
+Subject: [PATCH] Typo in overlays README
+
+touchscreen-size-y for rpi-ft5406 defaults to 480, not 600
+---
+ arch/arm/boot/dts/overlays/README | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3738,7 +3738,7 @@ Name: rpi-ft5406
+ Info: Official Raspberry Pi display touchscreen
+ Load: dtoverlay=rpi-ft5406,<param>=<val>
+ Params: touchscreen-size-x Touchscreen X resolution (default 800)
+- touchscreen-size-y Touchscreen Y resolution (default 600);
++ touchscreen-size-y Touchscreen Y resolution (default 480);
+ touchscreen-inverted-x Invert touchscreen X coordinates (default 0);
+ touchscreen-inverted-y Invert touchscreen Y coordinates (default 0);
+ touchscreen-swapped-x-y Swap X and Y cordinates (default 0);
--- /dev/null
+From 4cb97982f88d8fb623ace5a9511198e442e993ba Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 20 Oct 2023 17:15:25 +0100
+Subject: [PATCH] dts: bcm2712: Add the krnbt parameter
+
+Add a Pi 5 implementation of the krnbt parameter, for symmetry and
+for tinkering purposes.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -809,6 +809,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ i2c_baudrate = <&i2c_arm>, "clock-frequency:0";
+ i2c_arm_baudrate = <&i2c_arm>, "clock-frequency:0";
+ i2c_vc_baudrate = <&i2c_vc>, "clock-frequency:0";
++ krnbt = <&bluetooth>, "status";
+ nvme = <&pciex1>, "status";
+ pciex1 = <&pciex1>, "status";
+ pciex1_gen = <&pciex1> , "max-link-speed:0";
--- /dev/null
+From 4137b49989ce710305e476d0bd1086d7d906ff50 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 20 Oct 2023 17:09:54 +0100
+Subject: [PATCH] drm/vc4_fkms: Fix up interrupt handler for both 2835/2711 and
+ 2712
+
+2712 has switched from using the SMI peripheral to another interrupt
+source for the vsync interrupt, so handle both sources cleanly.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_firmware_kms.c | 48 ++++++++++++++++++++------
+ 1 file changed, 38 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_firmware_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_firmware_kms.c
+@@ -47,9 +47,15 @@ struct get_display_cfg {
+ u32 max_pixel_clock[2]; //Max pixel clock for each display
+ };
+
++enum vc4_fkms_revision {
++ BCM2835_6_7,
++ BCM2711,
++ BCM2712,
++};
++
+ struct vc4_fkms {
+ struct get_display_cfg cfg;
+- bool bcm2711;
++ enum vc4_fkms_revision revision;
+ };
+
+ #define PLANES_PER_CRTC 8
+@@ -1149,7 +1155,7 @@ vc4_crtc_mode_valid(struct drm_crtc *crt
+ /* Pi4 can't generate odd horizontal timings on HDMI, so reject modes
+ * that would set them.
+ */
+- if (fkms->bcm2711 &&
++ if (fkms->revision >= BCM2711 &&
+ (vc4_crtc->display_number == 2 || vc4_crtc->display_number == 7) &&
+ !(mode->flags & DRM_MODE_FLAG_DBLCLK) &&
+ ((mode->hdisplay | /* active */
+@@ -1267,6 +1273,20 @@ static irqreturn_t vc4_crtc_irq_handler(
+ return ret;
+ }
+
++static irqreturn_t vc4_crtc2712_irq_handler(int irq, void *data)
++{
++ struct vc4_crtc **crtc_list = data;
++ int i;
++
++ for (i = 0; crtc_list[i]; i++) {
++ if (crtc_list[i]->vblank_enabled)
++ drm_crtc_handle_vblank(&crtc_list[i]->base);
++ vc4_crtc_handle_page_flip(crtc_list[i]);
++ }
++
++ return IRQ_HANDLED;
++}
++
+ static int vc4_fkms_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+@@ -1352,9 +1372,12 @@ static const struct drm_crtc_helper_func
+ };
+
+ static const struct of_device_id vc4_firmware_kms_dt_match[] = {
+- { .compatible = "raspberrypi,rpi-firmware-kms" },
++ { .compatible = "raspberrypi,rpi-firmware-kms",
++ .data = (void *)BCM2835_6_7 },
+ { .compatible = "raspberrypi,rpi-firmware-kms-2711",
+- .data = (void *)1 },
++ .data = (void *)BCM2711 },
++ { .compatible = "raspberrypi,rpi-firmware-kms-2712",
++ .data = (void *)BCM2712 },
+ {}
+ };
+
+@@ -1924,8 +1947,7 @@ static int vc4_fkms_bind(struct device *
+ match = of_match_device(vc4_firmware_kms_dt_match, dev);
+ if (!match)
+ return -ENODEV;
+- if (match->data)
+- fkms->bcm2711 = true;
++ fkms->revision = (enum vc4_fkms_revision)match->data;
+
+ firmware_node = of_parse_phandle(dev->of_node, "brcm,firmware", 0);
+ vc4->firmware = devm_rpi_firmware_get(&pdev->dev, firmware_node);
+@@ -1992,10 +2014,16 @@ static int vc4_fkms_bind(struct device *
+ if (IS_ERR(crtc_list[0]->regs))
+ DRM_ERROR("Oh dear, failed to map registers\n");
+
+- writel(0, crtc_list[0]->regs + SMICS);
+- ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
+- vc4_crtc_irq_handler, 0,
+- "vc4 firmware kms", crtc_list);
++ if (fkms->revision >= BCM2712) {
++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
++ vc4_crtc2712_irq_handler, 0,
++ "vc4 firmware kms", crtc_list);
++ } else {
++ writel(0, crtc_list[0]->regs + SMICS);
++ ret = devm_request_irq(dev, platform_get_irq(pdev, 0),
++ vc4_crtc_irq_handler, 0,
++ "vc4 firmware kms", crtc_list);
++ }
+ if (ret)
+ DRM_ERROR("Oh dear, failed to register IRQ\n");
+ } else {
--- /dev/null
+From 5a52cae54a05499a8487f392cf5dfc3d8a837e6f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 20 Oct 2023 17:12:09 +0100
+Subject: [PATCH] dt: Switch bcm2712 firmware-kms node to using the 2712
+ compatible
+
+With the new compatible to handle the interrupts correctly, switch
+the base dt to use it.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -103,7 +103,7 @@
+ };
+
+ firmwarekms: firmwarekms@7d503000 {
+- compatible = "raspberrypi,rpi-firmware-kms";
++ compatible = "raspberrypi,rpi-firmware-kms-2712";
+ /* SUN_L2 interrupt reg */
+ reg = <0x7d503000 0x18>;
+ interrupt-parent = <&cpu_l2_irq>;
--- /dev/null
+From f075893e9b0e241879998c0b12cf8af0ba7737da Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 23 Oct 2023 10:03:03 +0100
+Subject: [PATCH] drivers: media: imx477: Disable the scaler
+
+The horizontal scaler was enabled for the 2028x1520 and 2028x1080 modes,
+with a scale factor of 1. It caused a single column of bad pixels on the
+right edge of the image. Since scaling is not needed for these modes,
+disable it entirely.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -632,7 +632,7 @@ static const struct imx477_reg mode_2028
+ {0x9e9f, 0x00},
+ {0xa2a9, 0x60},
+ {0xa2b7, 0x00},
+- {0x0401, 0x01},
++ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x20},
+ {0x0408, 0x00},
+@@ -733,7 +733,7 @@ static const struct imx477_reg mode_2028
+ {0x9e9f, 0x00},
+ {0xa2a9, 0x60},
+ {0xa2b7, 0x00},
+- {0x0401, 0x01},
++ {0x0401, 0x00},
+ {0x0404, 0x00},
+ {0x0405, 0x20},
+ {0x0408, 0x00},
--- /dev/null
+From eccaa8588fca9c9ec950664f1d5894bd826b57b0 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 23 Oct 2023 14:10:15 +0100
+Subject: [PATCH] dt: Add drm_fbN_vc4 overrides for Pi0-4
+
+Follows up '61b138adaead ("dt: Add overrides for drm framebuffer
+allocations on Pi5")' with an equivalent for Pi0-4.
+
+These will have no effect on most normal systems, but drm_fb0_vc4
+will stop SPI displays jumping in and claiming /dev/fb0.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm270x-rpi.dtsi | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm270x-rpi.dtsi
++++ b/arch/arm/boot/dts/bcm270x-rpi.dtsi
+@@ -1,7 +1,7 @@
+ /* Downstream modifications to bcm2835-rpi.dtsi */
+
+ / {
+- aliases {
++ aliases: aliases {
+ aux = &aux;
+ sound = &sound;
+ soc = &soc;
+@@ -98,6 +98,9 @@
+ sdio_overclock = <&mmc>,"brcm,overclock-50:0",
+ <&mmcnr>,"brcm,overclock-50:0";
+ axiperf = <&axiperf>,"status";
++ drm_fb0_vc4 = <&aliases>, "drm-fb0=",&vc4;
++ drm_fb1_vc4 = <&aliases>, "drm-fb1=",&vc4;
++ drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4;
+ };
+ };
+
--- /dev/null
+From 3ed6d34d53e94ecbebc64c8fa3d1b6d3c41db8fb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 24 Oct 2023 09:58:52 +0100
+Subject: [PATCH] fixup! overlays: mcp23017: allow specification of the i2c bus
+
+The incorrect fragment order (*) caused broke the interrupt usage, and
+while it was being fixed the lack of a reference to the pinctrl
+declaration was noticed.
+
+See: https://github.com/raspberrypi/linux/issues/5677
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+
+(*) Ideally all fragments would appear in the file in the order in which
+they should be merged, but that is easy to forget and can be awkward, so
+the firmware merges all "intra" fragments (those that target other
+fragments in the overlay) before "inter" fragments (those that target
+the base DTB). However, intra fragments that target other intra
+fragments is a level of nesting too far for this logic to cope, so they
+must appear before the fragments they target.
+---
+ .../boot/dts/overlays/mcp23017-overlay.dts | 42 ++++++++++---------
+ 1 file changed, 22 insertions(+), 20 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
++++ b/arch/arm/boot/dts/overlays/mcp23017-overlay.dts
+@@ -24,30 +24,13 @@
+ };
+
+ fragment@2 {
+- target = <&i2cbus>;
+- __overlay__ {
+- #address-cells = <1>;
+- #size-cells = <0>;
+-
+- mcp23017: mcp@20 {
+- compatible = "microchip,mcp23017";
+- reg = <0x20>;
+- gpio-controller;
+- #gpio-cells = <2>;
+-
+- status = "okay";
+- };
+- };
+- };
+-
+- fragment@3 {
+ target = <&mcp23017>;
+ __dormant__ {
+ compatible = "microchip,mcp23008";
+ };
+ };
+
+- fragment@4 {
++ fragment@3 {
+ target = <&mcp23017>;
+ mcp23017_irq: __overlay__ {
+ #interrupt-cells=<2>;
+@@ -58,6 +41,25 @@
+ };
+ };
+
++ fragment@4 {
++ target = <&i2cbus>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ mcp23017: mcp@20 {
++ compatible = "microchip,mcp23017";
++ pinctrl-name = "default";
++ pinctrl-0 = <&mcp23017_pins>;
++ reg = <0x20>;
++ gpio-controller;
++ #gpio-cells = <2>;
++
++ status = "okay";
++ };
++ };
++ };
++
+ frag100: fragment@100 {
+ target = <&i2c1>;
+ i2cbus: __overlay__ {
+@@ -83,8 +85,8 @@
+ gpiopin = <&mcp23017_pins>,"brcm,pins:0",
+ <&mcp23017_irq>,"interrupts:0";
+ addr = <&mcp23017>,"reg:0", <&mcp23017_pins>,"reg:0";
+- mcp23008 = <0>,"=3";
+- noints = <0>,"!1!4";
++ mcp23008 = <0>,"=2";
++ noints = <0>,"!1!3";
+ i2c0 = <&frag100>, "target:0=",<&i2c0>;
+ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
+ <0>,"+101+102";
--- /dev/null
+From 8d53cc5b4b2a6f9baed7a0aa801a39ad9dce9bf8 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 26 Oct 2023 08:55:24 +0100
+Subject: [PATCH] drivers: media: pisp_be: Add back V4L2_PIX_FMT_RPI_BE format
+
+Add the opaque V4L2_PIX_FMT_RPI_BE format back to the format list as it
+is needed for the verification test suite. Also set the default format
+to YUV420 non-multiplanar.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/pisp_be/pisp_be.c | 9 ++++++---
+ .../media/platform/raspberrypi/pisp_be/pisp_be_formats.h | 5 +++++
+ 2 files changed, 11 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be.c
+@@ -1230,8 +1230,11 @@ static int try_format(struct v4l2_format
+ return verify_be_pix_format(f, node);
+
+ fmt = find_format(pixfmt);
+- if (!fmt)
+- fmt = find_format(V4L2_PIX_FMT_YUV420M);
++ if (!fmt) {
++ dev_dbg(pispbe->dev, "%s: [%s] Format not found, defaulting to YUV420\n",
++ __func__, NODE_NAME(node));
++ fmt = find_format(V4L2_PIX_FMT_YUV420);
++ }
+
+ f->fmt.pix_mp.pixelformat = fmt->fourcc;
+ f->fmt.pix_mp.num_planes = fmt->num_planes;
+@@ -1576,7 +1579,7 @@ static void node_set_default_format(stru
+ } else {
+ struct v4l2_format f = {0};
+
+- f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420M;
++ f.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_YUV420;
+ f.fmt.pix_mp.width = 1920;
+ f.fmt.pix_mp.height = 1080;
+ f.type = node->buf_type;
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+@@ -457,6 +457,11 @@ static const struct pisp_be_format suppo
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
++ /* Opaque BE format for HW verification. */
++ {
++ .fourcc = V4L2_PIX_FMT_RPI_BE,
++ .align = 32,
++ },
+ };
+
+ static const struct pisp_be_format meta_out_supported_formats[] = {
--- /dev/null
+From f1154884295a4bd00d6ebcaf01fa30141145903d Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 20 Sep 2023 10:04:15 +0100
+Subject: [PATCH] dt-bindings: PCI: brcmstb: add optional property -
+ "brcm,tperst-clk-ms"
+
+This property can be used to delay deassertion of external fundamental
+reset, which may be useful for endpoints that require an extended time for
+internal setup to complete.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
++++ b/Documentation/devicetree/bindings/pci/brcm,stb-pcie.yaml
+@@ -77,6 +77,14 @@ properties:
+ minItems: 1
+ maxItems: 3
+
++ brcm,tperst-clk-ms:
++ category: optional
++ type: int
++ description: u32 giving the number of milliseconds to extend
++ the time between internal release of fundamental reset and
++ the deassertion of the external PERST# pin. This has the
++ effect of increasing the Tperst_clk phase of link init.
++
+ required:
+ - compatible
+ - reg
--- /dev/null
+From 4b0c6453808a662869a43c504913f3b7ed64486a Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 20 Sep 2023 13:01:11 +0100
+Subject: [PATCH] drivers: pci: brcmstb: optionally extend Tperst_clk time
+ during link-up
+
+The RC has a feature that allows for manual control over the deassertion
+of the PERST# output pin, which allows the time between refclk active
+and reset deassert at the EP to be increased.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/pci/controller/pcie-brcmstb.c | 24 +++++++++++++++++++++++-
+ 1 file changed, 23 insertions(+), 1 deletion(-)
+
+--- a/drivers/pci/controller/pcie-brcmstb.c
++++ b/drivers/pci/controller/pcie-brcmstb.c
+@@ -138,6 +138,7 @@
+
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG pcie->reg_offsets[PCIE_HARD_DEBUG]
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK 0x2
++#define PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK 0x8
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x08000000
+ #define PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK 0x00800000
+ #define PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_L1SS_ENABLE_MASK 0x00200000
+@@ -352,6 +353,7 @@ struct brcm_pcie {
+ bool (*rc_mode)(struct brcm_pcie *pcie);
+ struct subdev_regulators *sr;
+ bool ep_wakeup_capable;
++ u32 tperst_clk_ms;
+ };
+
+ static inline bool is_bmips(const struct brcm_pcie *pcie)
+@@ -1388,9 +1390,28 @@ static int brcm_pcie_start_link(struct b
+ u16 nlw, cls, lnksta;
+ bool ssc_good = false;
+ int ret, i;
++ u32 tmp;
+
+ /* Unassert the fundamental reset */
+- pcie->perst_set(pcie, 0);
++ if (pcie->tperst_clk_ms) {
++ /*
++ * Increase Tperst_clk time by forcing PERST# output low while
++ * the internal reset is released, so the PLL generates stable
++ * refclk output further in advance of PERST# deassertion.
++ */
++ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ u32p_replace_bits(&tmp, 1, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK);
++ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++
++ pcie->perst_set(pcie, 0);
++ msleep(pcie->tperst_clk_ms);
++
++ tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_PERST_ASSERT_MASK);
++ writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
++ } else {
++ pcie->perst_set(pcie, 0);
++ }
+
+ /*
+ * Wait for 100ms after PERST# deassertion; see PCIe CEM specification
+@@ -1923,6 +1944,7 @@ static int brcm_pcie_probe(struct platfo
+ pcie->ssc = of_property_read_bool(np, "brcm,enable-ssc");
+ pcie->l1ss = of_property_read_bool(np, "brcm,enable-l1ss");
+ pcie->rcb_mps_mode = of_property_read_bool(np, "brcm,enable-mps-rcb");
++ of_property_read_u32(np, "brcm,tperst-clk-ms", &pcie->tperst_clk_ms);
+
+ ret = clk_prepare_enable(pcie->clk);
+ if (ret) {
--- /dev/null
+From db90a5e5fc2fbd843b29eb8110ed5e03604a2887 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 20 Sep 2023 13:04:54 +0100
+Subject: [PATCH] arm: dt: add dtparams for PCIe reset timing override
+
+The Pi 5 variant gets two parameters so that the CM4-compatible
+name will also work on Pi 5.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2711-rpi-cm4.dts | 2 ++
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm/boot/dts/overlays/README | 7 +++++++
+ 3 files changed, 11 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
++++ b/arch/arm/boot/dts/bcm2711-rpi-cm4.dts
+@@ -446,5 +446,7 @@ i2c_csi_dsi0: &i2c0 {
+ cam1_reg = <&cam1_reg>,"status";
+ cam1_reg_gpio = <&cam1_reg>,"gpio:4",
+ <&cam1_reg>,"gpio:0=", <&gpio>;
++
++ pcie_tperst_clk_ms = <&pcie0>,"brcm,tperst-clk-ms:0";
+ };
+ };
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -814,6 +814,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ pciex1 = <&pciex1>, "status";
+ pciex1_gen = <&pciex1> , "max-link-speed:0";
+ pciex1_no_l0s = <&pciex1>, "aspm-no-l0s?";
++ pciex1_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
++ pcie_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
+ random = <&random>, "status";
+ rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0";
+ spi = <&spi0>, "status";
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -280,6 +280,10 @@ Params:
+ (2711 only, but not applicable on CM4S)
+ N.B. USB-A ports on 4B are subsequently disabled
+
++ pcie_tperst_clk_ms Add N milliseconds between PCIe reference clock
++ activation and PERST# deassertion
++ (CM4 and 2712, default "0")
++
+ pciex1 Set to "on" to enable the external PCIe link
+ (2712 only, default "off")
+
+@@ -290,6 +294,9 @@ Params:
+ PCIe link for devices that have broken
+ implementations (2712 only, default "off")
+
++ pciex1_tperst_clk_ms Alias for pcie_tperst_clk_ms
++ (2712 only, default "0")
++
+ spi Set to "on" to enable the spi interfaces
+ (default "off")
+
--- /dev/null
+From cb013b6602de32c647ed08faf899596664a18635 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Thu, 26 Oct 2023 13:47:54 +0100
+Subject: [PATCH] arm: dt: bcm2712: don't unconditionally enable MPS read
+ completions
+
+RP1 supports it, but it's not a given that an arbitrary EP device
+on PCIE2 will. Migrate the property to the rp1_target fragment.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 1 +
+ arch/arm/boot/dts/bcm2712.dtsi | 1 -
+ 2 files changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -160,6 +160,7 @@
+ };
+
+ rp1_target: &pcie2 {
++ brcm,enable-mps-rcb;
+ brcm,vdm-qos-map = <0xbbaa9888>;
+ aspm-no-l0s;
+ status = "okay";
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -1086,7 +1086,6 @@
+ 0x00 0x00000000
+ 0x10 0x00000000>;
+
+- brcm,enable-mps-rcb;
+ brcm,enable-l1ss;
+ status = "disabled";
+ };
--- /dev/null
+From 8dcc16f0adc50f5cb8a11a6dde238131d0ca45a0 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+Date: Fri, 27 Oct 2023 12:14:22 +0100
+Subject: [PATCH] drivers: media: imx477: Set horizontal binning when disabling
+ the scaler
+
+The horizontal scaler has been disabled but actually the sensor is not
+binning horizontally, resulting in images that are stretched 2x
+horizontally (missing the right half of the field of view completely).
+
+Therefore we must additionally set the horizontal binning mode. There
+is only marginal change in output quality and noise levels.
+
+Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
+Fixes: f075893e9b0e ("drivers: media: imx477: Disable the scaler")
+---
+ drivers/media/i2c/imx477.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -607,7 +607,7 @@ static const struct imx477_reg mode_2028
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0900, 0x01},
+- {0x0901, 0x12},
++ {0x0901, 0x22},
+ {0x0902, 0x02},
+ {0x3140, 0x02},
+ {0x3c00, 0x00},
+@@ -708,7 +708,7 @@ static const struct imx477_reg mode_2028
+ {0x0385, 0x01},
+ {0x0387, 0x01},
+ {0x0900, 0x01},
+- {0x0901, 0x12},
++ {0x0901, 0x22},
+ {0x0902, 0x02},
+ {0x3140, 0x02},
+ {0x3c00, 0x00},
--- /dev/null
+From e641fd7a50987ad6b7ce1ab36189bc8817295e42 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 31 Oct 2023 16:34:56 +0000
+Subject: [PATCH] fixup! arch/arm64: Add Revision, Serial, Model to cpuinfo
+
+Delete the Hardware string, which is pointless and misleading.
+
+See: https://github.com/raspberrypi/bookworm-feedback/issues/129
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm64/kernel/cpuinfo.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/arch/arm64/kernel/cpuinfo.c
++++ b/arch/arm64/kernel/cpuinfo.c
+@@ -224,8 +224,6 @@ static int c_show(struct seq_file *m, vo
+ seq_printf(m, "CPU revision\t: %d\n\n", MIDR_REVISION(midr));
+ }
+
+- seq_printf(m, "Hardware\t: BCM2835\n");
+-
+ np = of_find_node_by_path("/system");
+ if (np) {
+ if (!of_property_read_u32(np, "linux,revision", &revision))
--- /dev/null
+From e86c43b86179fba90a1d9dd5acb554767af6740f Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 12 Jun 2023 15:23:55 +0100
+Subject: [PATCH] dts: bcm2710-rpi-zero-2-w: Remove WLAN firmwares
+
+With careful use of qualified firmware names there is no need for the
+ability to override the device names based on Device Tree properties.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
++++ b/arch/arm/boot/dts/bcm2710-rpi-zero-2-w.dts
+@@ -161,19 +161,6 @@
+ brcmf: wifi@1 {
+ reg = <1>;
+ compatible = "brcm,bcm4329-fmac";
+-
+- firmwares {
+- fw_43436p {
+- chipid = <43430>;
+- revmask = <4>;
+- fw_base = "brcm/brcmfmac43436-sdio";
+- };
+- fw_43436s {
+- chipid = <43430>;
+- revmask = <2>;
+- fw_base = "brcm/brcmfmac43436s-sdio";
+- };
+- };
+ };
+ };
+
--- /dev/null
+From a11312709f46b71bf320a9dcc8cf4e09056552cd Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 1 Nov 2023 13:25:54 +0000
+Subject: [PATCH] drivers: media: cfe: Set the CSI-2 link frequency correctly
+
+Use the sensor provided link frequency to set the DPHY timing parameters
+on stream_on. This replaces the hard-coded 999 MHz value currently being
+used. As a fallback, revert to the original 999 Mhz link frequency.
+
+As a drive-by, fix a 80-character line formatting error.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 63 +++++++++++++++++--
+ 1 file changed, 58 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -780,7 +780,8 @@ static void cfe_start_channel(struct cfe
+ __func__, node_desc[FE_OUT0].name,
+ cfe->fe_csi2_channel);
+
+- source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, cfe->fe_csi2_channel);
++ source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state,
++ cfe->fe_csi2_channel);
+ fmt = find_format_by_code(source_fmt->code);
+
+ width = source_fmt->width;
+@@ -982,6 +983,59 @@ static void cfe_buffer_queue(struct vb2_
+ spin_unlock_irqrestore(&cfe->state_lock, flags);
+ }
+
++static u64 sensor_link_frequency(struct cfe_device *cfe)
++{
++ struct v4l2_mbus_framefmt *source_fmt;
++ struct v4l2_subdev_state *state;
++ struct media_entity *entity;
++ struct v4l2_subdev *subdev;
++ const struct cfe_fmt *fmt;
++ struct media_pad *pad;
++ s64 link_freq;
++
++ state = v4l2_subdev_lock_and_get_active_state(&cfe->csi2.sd);
++ source_fmt = v4l2_subdev_get_pad_format(&cfe->csi2.sd, state, 0);
++ fmt = find_format_by_code(source_fmt->code);
++ v4l2_subdev_unlock_state(state);
++
++ /*
++ * Walk up the media graph to find either the sensor entity, or another
++ * entity that advertises the V4L2_CID_LINK_FREQ or V4L2_CID_PIXEL_RATE
++ * control through the subdev.
++ */
++ entity = &cfe->csi2.sd.entity;
++ while (1) {
++ pad = &entity->pads[0];
++ if (!(pad->flags & MEDIA_PAD_FL_SINK))
++ goto err;
++
++ pad = media_pad_remote_pad_first(pad);
++ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
++ goto err;
++
++ entity = pad->entity;
++ subdev = media_entity_to_v4l2_subdev(entity);
++ if (entity->function == MEDIA_ENT_F_CAM_SENSOR ||
++ v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_LINK_FREQ) ||
++ v4l2_ctrl_find(subdev->ctrl_handler, V4L2_CID_PIXEL_RATE))
++ break;
++ }
++
++ link_freq = v4l2_get_link_freq(subdev->ctrl_handler, fmt->depth,
++ cfe->csi2.active_data_lanes * 2);
++ if (link_freq < 0)
++ goto err;
++
++ /* x2 for DDR. */
++ link_freq *= 2;
++ cfe_info("Using a link frequency of %lld Hz\n", link_freq);
++ return link_freq;
++
++err:
++ cfe_err("Unable to determine sensor link frequency, using 999 MHz\n");
++ return 999 * 1000000UL;
++}
++
+ static int cfe_start_streaming(struct vb2_queue *vq, unsigned int count)
+ {
+ struct v4l2_mbus_config mbus_config = { 0 };
+@@ -1049,10 +1103,11 @@ static int cfe_start_streaming(struct vb
+ goto err_disable_cfe;
+ }
+
+- cfe_dbg("Starting sensor streaming\n");
+-
++ cfe_dbg("Configuring CSI-2 block\n");
++ cfe->csi2.dphy.dphy_freq = sensor_link_frequency(cfe) / 1000000UL;
+ csi2_open_rx(&cfe->csi2);
+
++ cfe_dbg("Starting sensor streaming\n");
+ cfe->sequence = 0;
+ ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);
+ if (ret < 0) {
+@@ -1945,8 +2000,6 @@ static int of_cfe_connect_subdevs(struct
+ }
+ }
+
+- /* TODO: Get the frequency from devicetree */
+- cfe->csi2.dphy.dphy_freq = 999;
+ cfe->csi2.dphy.num_lanes = ep.bus.mipi_csi2.num_data_lanes;
+ cfe->csi2.bus_flags = ep.bus.mipi_csi2.flags;
+
--- /dev/null
+From fb78b2617e70e58e03e0d50e674758fdd2a80d45 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Nov 2023 10:39:11 +0000
+Subject: [PATCH] dts: bcm2712-rpi-5-b: Create some dummy nodes
+
+The kernel now treats multiple fragments targeting the same node as an
+error. For this reason, it is important that labels created just for
+compatibility with other systems (e.g. i2c0if and i2c0mux) are
+attached to unique nodes, not just tacked onto existing nodes.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -157,6 +157,12 @@
+ dummy: dummy {
+ // A target for unwanted overlay fragments
+ };
++
++
++ // A few extra labels to keep overlays happy
++
++ i2c0if: i2c0if {};
++ i2c0mux: i2c0mux {};
+ };
+
+ rp1_target: &pcie2 {
+@@ -243,11 +249,6 @@ aux: &dummy {};
+
+ #include "bcm2712-rpi.dtsi"
+
+-// A few extra labels to keep overlays happy
+-
+-i2c0if: &rp1_gpio {};
+-i2c0mux: &rp1_gpio {};
+-
+ i2c_csi_dsi0: &i2c6 { // Note: This is for MIPI0 connector only
+ pinctrl-0 = <&rp1_i2c6_38_39>;
+ pinctrl-names = "default";
--- /dev/null
+From cd66a0832351762b496bdce6f2f94a871d11484e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Nov 2023 13:12:55 +0000
+Subject: [PATCH] dts: rp1: Add spi6, fix spi1 #address-cells
+
+spi6 won't be useful on Pi 5 because it can't be enabled on the 40-pin
+header, but include it for completeness.
+
+Also fix the #address-cells value for spi1, otherwise the kernel will
+reject attempts to apply the, say, spi1-2cs overlay at runtime.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/rp1.dtsi | 17 ++++++++++++++++-
+ 1 file changed, 16 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/rp1.dtsi
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -187,7 +187,7 @@
+ interrupts = <RP1_INT_SPI1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
+ clock-names = "ssi_clk";
+- #address-cells = <0>;
++ #address-cells = <1>;
+ #size-cells = <0>;
+ num-cs = <2>;
+ dmas = <&rp1_dma RP1_DMA_SPI1_TX>,
+@@ -262,6 +262,21 @@
+ dma-names = "tx", "rx";
+ status = "disabled";
+ };
++
++ rp1_spi6: spi@68000 {
++ reg = <0xc0 0x40068000 0x0 0x130>;
++ compatible = "snps,dw-apb-ssi";
++ interrupts = <RP1_INT_SPI6 IRQ_TYPE_LEVEL_HIGH>;
++ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ clock-names = "ssi_clk";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ num-cs = <2>;
++ dmas = <&rp1_dma RP1_DMA_SPI6_TX>,
++ <&rp1_dma RP1_DMA_SPI6_RX>;
++ dma-names = "tx", "rx";
++ status = "disabled";
++ };
+
+ // SPI7 is a target/slave interface
+ rp1_spi7: spi@6c000 {
--- /dev/null
+From 17f135b742c4edb340afb365873c3a574f7e16cb Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 2 Nov 2023 17:05:46 +0000
+Subject: [PATCH] overlays: uart<n>-pi5: Add the pinctrl-0 property
+
+Without the pinctrl-0 property in the overlays, the UARTs may not be
+mapped correctly.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts | 1 +
+ arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts | 1 +
+ 5 files changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart0-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+ target = <&uart0>;
+ frag0: __overlay__ {
+ status = "okay";
++ pinctrl-0 = <&uart0_pins>;
+ };
+ };
+
+--- a/arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart1-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+ target = <&uart1>;
+ frag0: __overlay__ {
+ status = "okay";
++ pinctrl-0 = <&uart1_pins>;
+ };
+ };
+
+--- a/arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart2-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+ target = <&uart2>;
+ frag0: __overlay__ {
+ status = "okay";
++ pinctrl-0 = <&uart2_pins>;
+ };
+ };
+
+--- a/arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart3-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+ target = <&uart3>;
+ frag0: __overlay__ {
+ status = "okay";
++ pinctrl-0 = <&uart3_pins>;
+ };
+ };
+
+--- a/arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
++++ b/arch/arm/boot/dts/overlays/uart4-pi5-overlay.dts
+@@ -8,6 +8,7 @@
+ target = <&uart4>;
+ frag0: __overlay__ {
+ status = "okay";
++ pinctrl-0 = <&uart4_pins>;
+ };
+ };
+
--- /dev/null
+From c9a785d57c302d5f1d4de4e67fa57522e66c7882 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 6 Nov 2023 09:40:50 +0000
+Subject: [PATCH] drivers: media: imx477: Add V4L2_CID_LINK_FREQ control
+
+Add V4L2_CID_LINK_FREQ as a read-only control with a value of 450 Mhz.
+This will be used by the CFE driver to corretly setup the DPHY timing
+parameters in the CSI-2 block.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -164,6 +164,10 @@ struct imx477_mode {
+ struct imx477_reg_list reg_list;
+ };
+
++static const s64 imx477_link_freq_menu[] = {
++ IMX477_DEFAULT_LINK_FREQ,
++};
++
+ static const struct imx477_reg mode_common_regs[] = {
+ {0x0136, 0x18},
+ {0x0137, 0x00},
+@@ -1110,6 +1114,7 @@ struct imx477 {
+ struct v4l2_ctrl_handler ctrl_handler;
+ /* V4L2 Controls */
+ struct v4l2_ctrl *pixel_rate;
++ struct v4l2_ctrl *link_freq;
+ struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *vflip;
+ struct v4l2_ctrl *hflip;
+@@ -1997,6 +2002,15 @@ static int imx477_init_controls(struct i
+ IMX477_PIXEL_RATE, 1,
+ IMX477_PIXEL_RATE);
+
++ /* 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);
++ if (imx477->link_freq)
++ imx477->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+ /*
+ * Create the controls here, but mode specific limits are setup
+ * in the imx477_set_framing_limits() call below.
--- /dev/null
+From 46913ee0590ee0e3f607ab189be19a4d0ce785f2 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 6 Nov 2023 09:42:37 +0000
+Subject: [PATCH] drivers: media: imx477: Correctly set IMX477_PIXEL_RATE as a
+ r/o control
+
+This control is meant to be read-only, mark it as such.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/i2c/imx477.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -2001,6 +2001,8 @@ static int imx477_init_controls(struct i
+ IMX477_PIXEL_RATE,
+ IMX477_PIXEL_RATE, 1,
+ IMX477_PIXEL_RATE);
++ if (imx477->pixel_rate)
++ imx477->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+
+ /* LINK_FREQ is also read only */
+ imx477->link_freq =
--- /dev/null
+From 6e9f68bba01b9c36a77b68c4b3167c317da986da Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Oct 2023 17:46:13 +0100
+Subject: [PATCH] drm/vc4: Correct logic on stopping an HVS channel
+
+When factoring out __vc4_hvs_stop_channel, the logic got inverted from
+ if (condition)
+ // stop channel
+to
+ if (condition)
+ goto out
+ //stop channel
+ out:
+and also changed the exact register writes used to stop the channel.
+
+Correct the logic so that the channel is actually stopped, and revert
+to the original register writes.
+
+Fixes: 6d01a106b4c8 ("drm/vc4: crtc: Move HVS init and close to a function")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -992,13 +992,11 @@ static void __vc4_hvs_stop_channel(struc
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+- if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE)
++ if (!(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE))
+ goto out;
+
+- HVS_WRITE(SCALER_DISPCTRLX(chan),
+- HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET);
+- HVS_WRITE(SCALER_DISPCTRLX(chan),
+- HVS_READ(SCALER_DISPCTRLX(chan)) & ~SCALER_DISPCTRLX_ENABLE);
++ HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET);
++ HVS_WRITE(SCALER_DISPCTRLX(chan), 0);
+
+ /* Once we leave, the scaler should be disabled and its fifo empty. */
+ WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET);
+@@ -1026,7 +1024,7 @@ static void __vc6_hvs_stop_channel(struc
+ if (!drm_dev_enter(drm, &idx))
+ return;
+
+- if (HVS_READ(SCALER6_DISPX_CTRL0(chan)) & SCALER6_DISPX_CTRL0_ENB)
++ if (!(HVS_READ(SCALER6_DISPX_CTRL0(chan)) & SCALER6_DISPX_CTRL0_ENB))
+ goto out;
+
+ HVS_WRITE(SCALER6_DISPX_CTRL0(chan),
--- /dev/null
+From 31c4c359aa2dbb1a7c095f0a6ef4e13cd46cfd14 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Oct 2023 18:05:09 +0100
+Subject: [PATCH] drm/vc4: Drop WARN for HVS FIFOs not being empty
+
+The reset condition for the EMPTY flag in DISPSTATx is 0,
+so seeing as we've just reset the pipeline there is no
+guarantee that the flag will denote empty if it hasn't been
+enabled.
+
+Drop the WARN.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 4 ----
+ 1 file changed, 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -1005,10 +1005,6 @@ static void __vc4_hvs_stop_channel(struc
+ SCALER_DISPSTATX_MODE) !=
+ SCALER_DISPSTATX_MODE_DISABLED);
+
+- WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) &
+- (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) !=
+- SCALER_DISPSTATX_EMPTY);
+-
+ out:
+ drm_dev_exit(idx);
+ }
--- /dev/null
+From 8b7078d1bbd8bb548cc97d5214adb828e9f0037c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 26 Oct 2023 18:23:31 +0100
+Subject: [PATCH] drm/vc4: Free all stale dlists if channel is disabled
+
+The code handling freeing stale dlists had 2 issues:
+- it disabled the interrupt as soon as the first EOF interrupt
+ occurred, even if it didn't clear all stale allocations, thus
+ leading to stale entries
+- It didn't free stale entries from disabled channels, so eg
+ "kmstest -c 0" could leave a stale alloc on channel 1 floating
+ around.
+
+Keep the interrupt enabled whilst there are any outstanding
+allocs, and discard those on disabled channels. This second
+channel does require us to call vc4_hvs_stop_channel from
+vc4_crtc_atomic_disable so that the channel actually gets stopped.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 2 ++
+ drivers/gpu/drm/vc4/vc4_hvs.c | 27 +++++++++++++++++++++++++--
+ 2 files changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -660,6 +660,8 @@ static void vc4_crtc_atomic_disable(stru
+
+ vc4_crtc_disable(crtc, encoder, state, old_vc4_state->assigned_channel);
+
++ vc4_hvs_atomic_disable(crtc, state);
++
+ /*
+ * Make sure we issue a vblank event after disabling the CRTC if
+ * someone was waiting it.
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -697,7 +697,8 @@ static void vc4_hvs_schedule_dlist_sweep
+ if (!list_empty(&hvs->stale_dlist_entries))
+ queue_work(system_unbound_wq, &hvs->free_dlist_work);
+
+- vc4_hvs_irq_clear_eof(hvs, channel);
++ if (list_empty(&hvs->stale_dlist_entries))
++ vc4_hvs_irq_clear_eof(hvs, channel);
+
+ spin_unlock_irqrestore(&hvs->mm_lock, flags);
+ }
+@@ -712,6 +713,27 @@ static bool vc4_hvs_frcnt_lte(u8 cnt1, u
+ return (s8)((cnt1 << 2) - (cnt2 << 2)) <= 0;
+ }
+
++bool vc4_hvs_check_channel_active(struct vc4_hvs *hvs, unsigned int fifo)
++{
++ struct vc4_dev *vc4 = hvs->vc4;
++ struct drm_device *drm = &vc4->base;
++ bool enabled = false;
++ int idx;
++
++ WARN_ON_ONCE(vc4->gen > VC4_GEN_6);
++
++ if (!drm_dev_enter(drm, &idx))
++ return 0;
++
++ if (vc4->gen >= VC4_GEN_6)
++ enabled = HVS_READ(SCALER6_DISPX_CTRL0(fifo)) & SCALER6_DISPX_CTRL0_ENB;
++ else
++ enabled = HVS_READ(SCALER_DISPCTRLX(fifo)) & SCALER_DISPCTRLX_ENABLE;
++
++ drm_dev_exit(idx);
++ return enabled;
++}
++
+ /*
+ * Some atomic commits (legacy cursor updates, mostly) will not wait for
+ * the next vblank and will just return once the commit has been pushed
+@@ -746,7 +768,8 @@ static void vc4_hvs_dlist_free_work(stru
+ u8 frcnt;
+
+ frcnt = vc4_hvs_get_fifo_frame_count(hvs, cur->channel);
+- if (!vc4_hvs_frcnt_lte(cur->target_frame_count, frcnt))
++ if (vc4_hvs_check_channel_active(hvs, cur->channel) &&
++ !vc4_hvs_frcnt_lte(cur->target_frame_count, frcnt))
+ continue;
+
+ vc4_hvs_free_dlist_entry_locked(hvs, cur);
--- /dev/null
+From 665e9810340abc37769b317445907bac1843dd64 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 27 Oct 2023 16:46:04 +0100
+Subject: [PATCH] drm/vc4: Add hvs_dlist_allocs debugfs function.
+
+Users are reporting running out of DLIST memory. Add a
+debugfs file to dump out all the allocations.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 35 +++++++++++++++++++++++++++++++++++
+ 1 file changed, 35 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -347,6 +347,36 @@ static int vc5_hvs_debugfs_gamma(struct
+ return 0;
+ }
+
++static int vc4_hvs_debugfs_dlist_allocs(struct seq_file *m, void *data)
++{
++ struct drm_info_node *node = m->private;
++ struct drm_device *dev = node->minor->dev;
++ struct vc4_dev *vc4 = to_vc4_dev(dev);
++ struct vc4_hvs *hvs = vc4->hvs;
++ struct drm_printer p = drm_seq_file_printer(m);
++ struct vc4_hvs_dlist_allocation *cur, *next;
++ struct drm_mm_node *mm_node;
++ unsigned long flags;
++
++ spin_lock_irqsave(&hvs->mm_lock, flags);
++
++ drm_printf(&p, "Allocated nodes:\n");
++ list_for_each_entry(mm_node, drm_mm_nodes(&hvs->dlist_mm), node_list) {
++ drm_printf(&p, "node [%08llx + %08llx]\n", mm_node->start, mm_node->size);
++ }
++
++ drm_printf(&p, "Stale nodes:\n");
++ list_for_each_entry_safe(cur, next, &hvs->stale_dlist_entries, node) {
++ drm_printf(&p, "node [%08llx + %08llx] channel %u frcnt %u\n",
++ cur->mm_node.start, cur->mm_node.size, cur->channel,
++ cur->target_frame_count);
++ }
++
++ spin_unlock_irqrestore(&hvs->mm_lock, flags);
++
++ return 0;
++}
++
+ /* The filter kernel is composed of dwords each containing 3 9-bit
+ * signed integers packed next to each other.
+ */
+@@ -1602,6 +1632,11 @@ int vc4_hvs_debugfs_init(struct drm_mino
+ if (ret)
+ return ret;
+
++ ret = vc4_debugfs_add_file(minor, "hvs_dlist_allocs",
++ vc4_hvs_debugfs_dlist_allocs, NULL);
++ if (ret)
++ return ret;
++
+ ret = vc4_debugfs_add_regset32(minor, "hvs_regs",
+ &hvs->regset);
+ if (ret)
--- /dev/null
+From 23ea21ef5f6efb3082c184843b35a2f8f2e4374c Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 31 Oct 2023 11:15:38 +0000
+Subject: [PATCH] drm/vc4: Log the size of the dlist allocation that was
+ attempted
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -659,7 +659,8 @@ vc4_hvs_alloc_dlist_entry(struct vc4_hvs
+ dlist_count);
+ spin_unlock_irqrestore(&hvs->mm_lock, flags);
+ if (ret) {
+- drm_err(dev, "Failed to allocate DLIST entry: %d\n", ret);
++ drm_err(dev, "Failed to allocate DLIST entry. Requested size=%zu. ret=%d\n",
++ dlist_count, ret);
+ return ERR_PTR(ret);
+ }
+
--- /dev/null
+From f9f480b04f1dc280bd4411477f5ee7336361367b Mon Sep 17 00:00:00 2001
+From: Dom Cobley <popcornmix@gmail.com>
+Date: Tue, 24 Oct 2023 16:20:42 +0100
+Subject: [PATCH] drm/vc4: crtc: Support odd horizontal timings on BCM2712
+
+BCM2711 runs pixelvalve at two pixels per clock cycle which results
+in an unfortunate limitation that odd horizontal timings are not
+possible. This is apparent on the standard DMT mode of 1366x768@60
+which cannot be driven with correct timing.
+
+BCM2712 defaults to the same behaviour, but has a mode to support
+odd timings. While internally it still runs at two pixels per clock,
+setting the PV_VCONTROL_ODD_TIMING bit makes it appear externally
+to behave as it is one pixel per clock.
+
+Switching to this mode fixes 1366x768@60 mode, and other custom
+resultions with odd horizontal timings.
+
+Signed-off-by: Dom Cobley <popcornmix@gmail.com>
+---
+ drivers/gpu/drm/vc4/vc4_crtc.c | 12 ++++--------
+ drivers/gpu/drm/vc4/vc4_hdmi.c | 4 ++--
+ drivers/gpu/drm/vc4/vc4_regs.h | 1 +
+ 3 files changed, 7 insertions(+), 10 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_crtc.c
++++ b/drivers/gpu/drm/vc4/vc4_crtc.c
+@@ -398,12 +398,6 @@ static void vc4_crtc_config_pv(struct dr
+
+ vc4_crtc_pixelvalve_reset(crtc);
+
+- /*
+- * NOTE: The BCM2712 has a H_OTE (Horizontal Odd Timing Enable)
+- * bit that, when set, will allow to specify the timings in
+- * pixels instead of cycles, thus allowing to specify odd
+- * timings.
+- */
+ CRTC_WRITE(PV_HORZA,
+ VC4_SET_FIELD((mode->htotal - mode->hsync_end) * pixel_rep / ppc,
+ PV_HORZA_HBP) |
+@@ -448,6 +442,7 @@ static void vc4_crtc_config_pv(struct dr
+ */
+ CRTC_WRITE(PV_V_CONTROL,
+ PV_VCONTROL_CONTINUOUS |
++ (vc4->gen >= VC4_GEN_6 ? PV_VCONTROL_ODD_TIMING : 0) |
+ (is_dsi ? PV_VCONTROL_DSI : 0) |
+ PV_VCONTROL_INTERLACE |
+ (odd_field_first
+@@ -459,6 +454,7 @@ static void vc4_crtc_config_pv(struct dr
+ } else {
+ CRTC_WRITE(PV_V_CONTROL,
+ PV_VCONTROL_CONTINUOUS |
++ (vc4->gen >= VC4_GEN_6 ? PV_VCONTROL_ODD_TIMING : 0) |
+ (is_dsi ? PV_VCONTROL_DSI : 0));
+ CRTC_WRITE(PV_VSYNCD_EVEN, 0);
+ }
+@@ -1334,7 +1330,7 @@ const struct vc4_pv_data bcm2712_pv0_dat
+ .hvs_output = 0,
+ },
+ .fifo_depth = 64,
+- .pixels_per_clock = 2,
++ .pixels_per_clock = 1,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_HDMI0,
+ },
+@@ -1347,7 +1343,7 @@ const struct vc4_pv_data bcm2712_pv1_dat
+ .hvs_output = 1,
+ },
+ .fifo_depth = 64,
+- .pixels_per_clock = 2,
++ .pixels_per_clock = 1,
+ .encoder_types = {
+ [0] = VC4_ENCODER_TYPE_HDMI1,
+ },
+--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
++++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
+@@ -3958,7 +3958,7 @@ static const struct vc4_hdmi_variant bcm
+ PHY_LANE_2,
+ PHY_LANE_CK,
+ },
+- .unsupported_odd_h_timings = true,
++ .unsupported_odd_h_timings = false,
+ .external_irq_controller = true,
+
+ .init_resources = vc5_hdmi_init_resources,
+@@ -3985,7 +3985,7 @@ static const struct vc4_hdmi_variant bcm
+ PHY_LANE_2,
+ PHY_LANE_CK,
+ },
+- .unsupported_odd_h_timings = true,
++ .unsupported_odd_h_timings = false,
+ .external_irq_controller = true,
+
+ .init_resources = vc5_hdmi_init_resources,
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -155,6 +155,7 @@
+ # define PV_CONTROL_EN BIT(0)
+
+ #define PV_V_CONTROL 0x04
++# define PV_VCONTROL_ODD_TIMING BIT(29)
+ # define PV_VCONTROL_ODD_DELAY_MASK VC4_MASK(22, 6)
+ # define PV_VCONTROL_ODD_DELAY_SHIFT 6
+ # define PV_VCONTROL_ODD_FIRST BIT(5)
--- /dev/null
+From 686fe776309fba5cad642c40177d39bf1fb320b2 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 7 Nov 2023 14:49:47 +0000
+Subject: [PATCH] spi: dw-dma: Get the last DMA scoop out of the FIFO
+
+With a DMA FIFO threshold greater than 1 (encoded as 0), it is possible
+for data in the FIFO to be inaccessible, causing the transfer to fail
+after a timeout. If the transfer includes a transmission, reduce the
+RX threshold when the TX completes, otherwise use 1 for the whole
+transfer (inefficient, but not catastrophic at SPI data rates).
+
+See: https://github.com/raspberrypi/linux/issues/5696
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi-dw-dma.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/spi/spi-dw-dma.c
++++ b/drivers/spi/spi-dw-dma.c
+@@ -275,8 +275,10 @@ static void dw_spi_dma_tx_done(void *arg
+ struct dw_spi *dws = arg;
+
+ clear_bit(DW_SPI_TX_BUSY, &dws->dma_chan_busy);
+- if (test_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy))
++ if (test_bit(DW_SPI_RX_BUSY, &dws->dma_chan_busy)) {
++ dw_writel(dws, DW_SPI_DMARDLR, 0);
+ return;
++ }
+
+ complete(&dws->dma_completion);
+ }
+@@ -602,6 +604,8 @@ static int dw_spi_dma_transfer(struct dw
+
+ nents = max(xfer->tx_sg.nents, xfer->rx_sg.nents);
+
++ dw_writel(dws, DW_SPI_DMARDLR, xfer->tx_buf ? (dws->rxburst - 1) : 0);
++
+ /*
+ * Execute normal DMA-based transfer (which submits the Rx and Tx SG
+ * lists directly to the DMA engine at once) if either full hardware
--- /dev/null
+From 4d2261fe86ce08bbee3c000718000e9f86593d88 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 11:52:16 +0000
+Subject: [PATCH] drivers: mmc: sdhci: add SPURIOUS_INT_RESP quirk
+
+Certain controllers (dwc-mshc) generate timeout conditions separately to
+command-completion conditions, where the end result is interrupts are
+separated in time depending on the current SDCLK frequency.
+
+This causes spurious interrupts if SDCLK is slow compared to the CPU's
+ability to process and return from interrupt. This occurs during card
+probe with an empty slot where all commands that would generate a
+response time out.
+
+Add a quirk to squelch command response interrupts when a command
+timeout interrupt is received.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/host/sdhci.c | 11 +++++++++++
+ drivers/mmc/host/sdhci.h | 3 +++
+ 2 files changed, 14 insertions(+)
+
+--- a/drivers/mmc/host/sdhci.c
++++ b/drivers/mmc/host/sdhci.c
+@@ -1728,6 +1728,12 @@ static bool sdhci_send_command(struct sd
+ if (host->use_external_dma)
+ sdhci_external_dma_pre_transfer(host, cmd);
+
++ if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_INT_RESP) {
++ host->ier |= SDHCI_INT_RESPONSE;
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
++ }
++
+ sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND);
+
+ return true;
+@@ -3330,6 +3336,11 @@ static void sdhci_cmd_irq(struct sdhci_h
+ if (intmask & SDHCI_INT_TIMEOUT) {
+ host->cmd->error = -ETIMEDOUT;
+ sdhci_err_stats_inc(host, CMD_TIMEOUT);
++ if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_INT_RESP) {
++ host->ier &= ~SDHCI_INT_RESPONSE;
++ sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
++ sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
++ }
+ } else {
+ host->cmd->error = -EILSEQ;
+ if (!mmc_op_tuning(host->cmd->opcode))
+--- a/drivers/mmc/host/sdhci.h
++++ b/drivers/mmc/host/sdhci.h
+@@ -486,6 +486,9 @@ struct sdhci_host {
+ #define SDHCI_QUIRK2_NO_SDR50 (1<<20)
+ #define SDHCI_QUIRK2_NO_SDR104 (1<<21)
+
++/* Command timeouts may generate a trailing INT_RESPONSE later */
++#define SDHCI_QUIRK2_SPURIOUS_INT_RESP (1<<31)
++
+ int irq; /* Device IRQ */
+ void __iomem *ioaddr; /* Mapped address */
+ phys_addr_t mapbase; /* physical address base */
--- /dev/null
+From ebe13d0d4314255d226ba740e37a14172a8b9091 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:10:13 +0000
+Subject: [PATCH] dt-bindings: mmc: sdhci-of-dwcmhsc: Add Raspberry Pi RP1
+ support
+
+The DWC MSHC controller on RP1 needs differentiating from the generic
+version.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ .../devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
++++ b/Documentation/devicetree/bindings/mmc/snps,dwcmshc-sdhci.yaml
+@@ -16,6 +16,7 @@ allOf:
+ properties:
+ compatible:
+ enum:
++ - raspberrypi,rp1-dwcmshc
+ - rockchip,rk3568-dwcmshc
+ - rockchip,rk3588-dwcmshc
+ - snps,dwcmshc-sdhci
+@@ -34,6 +35,8 @@ properties:
+ - description: axi clock for rockchip specified
+ - description: block clock for rockchip specified
+ - description: timer clock for rockchip specified
++ - description: timeout clock for rp1 specified
++ - description: sdio clock generator for rp1 specified
+
+
+ clock-names:
+@@ -44,6 +47,8 @@ properties:
+ - const: axi
+ - const: block
+ - const: timer
++ - const: timeout
++ - const: sdio
+
+ rockchip,txclk-tapnum:
+ description: Specify the number of delay for tx sampling.
--- /dev/null
+From 80dd8795ca631ac692fd3079487aea6d934a829c Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:12:59 +0000
+Subject: [PATCH] drivers: mmc: sdhci-of-dwcmshc: add RP1 dt ID and quirks
+
+Differentiate the RP1 variant of the Designware MSHC controller(s).
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ drivers/mmc/host/sdhci-of-dwcmshc.c | 13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
++++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
+@@ -373,6 +373,15 @@ static const struct sdhci_pltfm_data sdh
+ };
+ #endif
+
++static const struct sdhci_pltfm_data sdhci_dwcmshc_rp1_pdata = {
++ .ops = &sdhci_dwcmshc_ops,
++ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
++ SDHCI_QUIRK_BROKEN_CARD_DETECTION,
++ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
++ SDHCI_QUIRK2_BROKEN_HS200 |
++ SDHCI_QUIRK2_SPURIOUS_INT_RESP,
++};
++
+ static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = {
+ .ops = &sdhci_dwcmshc_rk35xx_ops,
+ .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
+@@ -441,6 +450,10 @@ static void dwcmshc_rk35xx_postinit(stru
+
+ static const struct of_device_id sdhci_dwcmshc_dt_ids[] = {
+ {
++ .compatible = "raspberrypi,rp1-dwcmshc",
++ .data = &sdhci_dwcmshc_rp1_pdata,
++ },
++ {
+ .compatible = "rockchip,rk3588-dwcmshc",
+ .data = &sdhci_dwcmshc_rk35xx_pdata,
+ },
--- /dev/null
+From 51cdff455e3c3df29764f71bc0c9dd0e099945d6 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:14:25 +0000
+Subject: [PATCH] arm: dts: change RP1 SDHCI controller compatible string
+
+Also add a sdio-pi5 overlay which enables mmc0 on GPIOs 22-27, as was
+possible with earlier models of Pi.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 7 ++++++
+ arch/arm/boot/dts/overlays/overlay_map.dts | 4 ++++
+ .../boot/dts/overlays/sdio-pi5-overlay.dts | 24 +++++++++++++++++++
+ arch/arm/boot/dts/rp1.dtsi | 4 ++--
+ 5 files changed, 38 insertions(+), 2 deletions(-)
+ create mode 100644 arch/arm/boot/dts/overlays/sdio-pi5-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -225,6 +225,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ sc16is752-spi1.dtbo \
+ sdhost.dtbo \
+ sdio.dtbo \
++ sdio-pi5.dtbo \
+ seeed-can-fd-hat-v1.dtbo \
+ seeed-can-fd-hat-v2.dtbo \
+ sh1106-spi.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -3932,6 +3932,13 @@ Info: This overlay is now deprecated.
+ Load: <Deprecated>
+
+
++Name: sdio-pi5
++Info: Selects the rp1_mmc0 interface and enables it on GPIOs 22-27.
++ Pi 5 only.
++Load: dtoverlay=sdio-pi5
++Params: <None>
++
++
+ Name: sdtweak
+ Info: This overlay is now deprecated. Use the sd_* dtparams in the
+ base DTB, e.g. "dtoverlay=sdtweak,poll_once" becomes
+--- a/arch/arm/boot/dts/overlays/overlay_map.dts
++++ b/arch/arm/boot/dts/overlays/overlay_map.dts
+@@ -250,6 +250,10 @@
+ deprecated = "use sdio,bus_width=1,gpios_22_25";
+ };
+
++ sdio-pi5 {
++ bcm2712;
++ };
++
+ sdtweak {
+ deprecated = "use 'dtparam=sd_poll_once' etc.";
+ };
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/sdio-pi5-overlay.dts
+@@ -0,0 +1,24 @@
++/dts-v1/;
++/plugin/;
++
++/* SDIO/SD/MMC on RP1 bank 0 */
++
++/{
++ compatible = "brcm,bcm2712";
++
++ fragment@0 {
++ target = <&rp1_mmc0>;
++ frag0: __overlay__ {
++ status = "okay";
++ pinctrl-0 = <&rp1_sdio0_22_27>;
++ pinctrl-names = "default";
++ };
++ };
++
++ fragment@1 {
++ target = <&rp1_sdio_clk0>;
++ frag1: __overlay__ {
++ status = "okay";
++ };
++ };
++};
+--- a/arch/arm/boot/dts/rp1.dtsi
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -962,7 +962,7 @@
+
+ rp1_mmc0: mmc@180000 {
+ reg = <0xc0 0x40180000 0x0 0x100>;
+- compatible = "snps,dwcmshc-sdhci";
++ compatible = "raspberrypi,rp1-dwcmshc";
+ interrupts = <RP1_INT_SDIO0 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
+ &rp1_clocks RP1_CLK_SDIO_TIMER
+@@ -978,7 +978,7 @@
+
+ rp1_mmc1: mmc@184000 {
+ reg = <0xc0 0x40184000 0x0 0x100>;
+- compatible = "snps,dwcmshc-sdhci";
++ compatible = "raspberrypi,rp1-dwcmshc";
+ interrupts = <RP1_INT_SDIO1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core
+ &rp1_clocks RP1_CLK_SDIO_TIMER
--- /dev/null
+From 020ee5029ab0b11e47696f538418105ccfdb44de Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 8 Nov 2023 19:17:33 +0000
+Subject: [PATCH] ASoC: bcm: audioinjector_octo: Add soundcard "owner"
+
+See: https://github.com/raspberrypi/linux/issues/5697
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/bcm/audioinjector-octo-soundcard.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/sound/soc/bcm/audioinjector-octo-soundcard.c
++++ b/sound/soc/bcm/audioinjector-octo-soundcard.c
+@@ -252,6 +252,7 @@ static const struct snd_soc_dapm_route a
+
+ static struct snd_soc_card snd_soc_audioinjector_octo = {
+ .name = "audioinjector-octo-soundcard",
++ .owner = THIS_MODULE,
+ .dai_link = audioinjector_octo_dai,
+ .num_links = ARRAY_SIZE(audioinjector_octo_dai),
+
--- /dev/null
+From f364e0eb8f973e1aa24a3c451d18e84247a8efcd Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 8 Nov 2023 10:57:45 +0000
+Subject: [PATCH] drivers: media: imx708: Adjust broken line correction
+ parameter
+
+In full-resolution mode, the LPF_INTENSITY_EN and LPF_INTENSITY
+registers control Quad Bayer Re-mosaic broken line correction.
+Expose this as a module parameter "qbc_adjust": zero disables
+the correction and values in the range 2 to 5 set its strength.
+
+There is a trade-off between coloured and monochrome patterns.
+The previous fixed value 4 could produce ladder/spots artefacts
+in coloured textures. The new default value 2 may suit a wider
+range of scenes.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/media/i2c/imx708.c | 50 ++++++++++++++++++++++++++++++++------
+ 1 file changed, 42 insertions(+), 8 deletions(-)
+
+--- a/drivers/media/i2c/imx708.c
++++ b/drivers/media/i2c/imx708.c
+@@ -20,6 +20,14 @@
+ #include <media/v4l2-fwnode.h>
+ #include <media/v4l2-mediabus.h>
+
++/*
++ * Parameter to adjust Quad Bayer re-mosaic broken line correction
++ * strength, used in full-resolution mode only. Set zero to disable.
++ */
++static int qbc_adjust = 2;
++module_param(qbc_adjust, int, 0644);
++MODULE_PARM_DESC(qbc_adjust, "Quad Bayer broken line correction strength [0,2-5]");
++
+ #define IMX708_REG_VALUE_08BIT 1
+ #define IMX708_REG_VALUE_16BIT 2
+
+@@ -99,11 +107,17 @@
+
+ /* HDR exposure ratio (long:med == med:short) */
+ #define IMX708_HDR_EXPOSURE_RATIO 4
+-#define IMX708_REG_MID_EXPOSURE 0x3116
+-#define IMX708_REG_SHT_EXPOSURE 0x0224
++#define IMX708_REG_MID_EXPOSURE 0x3116
++#define IMX708_REG_SHT_EXPOSURE 0x0224
+ #define IMX708_REG_MID_ANALOG_GAIN 0x3118
+ #define IMX708_REG_SHT_ANALOG_GAIN 0x0216
+
++/* QBC Re-mosaic broken line correction registers */
++#define IMX708_LPF_INTENSITY_EN 0xC428
++#define IMX708_LPF_INTENSITY_ENABLED 0x00
++#define IMX708_LPF_INTENSITY_DISABLED 0x01
++#define IMX708_LPF_INTENSITY 0xC429
++
+ /*
+ * Metadata buffer holds a variety of data, all sent with the same VC/DT (0x12).
+ * It comprises two scanlines (of up to 5760 bytes each, for 4608 pixels)
+@@ -171,6 +185,9 @@ struct imx708_mode {
+
+ /* HDR flag, used for checking if the current mode is HDR */
+ bool hdr;
++
++ /* Quad Bayer Re-mosaic flag */
++ bool remosaic;
+ };
+
+ /* Default PDAF pixel correction gains */
+@@ -363,8 +380,6 @@ static const struct imx708_reg mode_4608
+ {0x341f, 0x20},
+ {0x3420, 0x00},
+ {0x3421, 0xd8},
+- {0xC428, 0x00},
+- {0xC429, 0x04},
+ {0x3366, 0x00},
+ {0x3367, 0x00},
+ {0x3368, 0x00},
+@@ -677,7 +692,8 @@ static const struct imx708_mode supporte
+ .pixel_rate = 595200000,
+ .exposure_lines_min = 8,
+ .exposure_lines_step = 1,
+- .hdr = false
++ .hdr = false,
++ .remosaic = true
+ },
+ {
+ /* regular 2x2 binned. */
+@@ -699,7 +715,8 @@ static const struct imx708_mode supporte
+ .pixel_rate = 585600000,
+ .exposure_lines_min = 4,
+ .exposure_lines_step = 2,
+- .hdr = false
++ .hdr = false,
++ .remosaic = false
+ },
+ {
+ /* 2x2 binned and cropped for 720p. */
+@@ -721,7 +738,8 @@ static const struct imx708_mode supporte
+ .pixel_rate = 566400000,
+ .exposure_lines_min = 4,
+ .exposure_lines_step = 2,
+- .hdr = false
++ .hdr = false,
++ .remosaic = false
+ },
+ };
+
+@@ -746,7 +764,8 @@ static const struct imx708_mode supporte
+ .pixel_rate = 777600000,
+ .exposure_lines_min = 8 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO,
+ .exposure_lines_step = 2 * IMX708_HDR_EXPOSURE_RATIO * IMX708_HDR_EXPOSURE_RATIO,
+- .hdr = true
++ .hdr = true,
++ .remosaic = false
+ }
+ };
+
+@@ -1515,6 +1534,21 @@ static int imx708_start_streaming(struct
+ return ret;
+ }
+
++ /* Quad Bayer re-mosaic adjustments (for full-resolution mode only) */
++ if (imx708->mode->remosaic && qbc_adjust > 0) {
++ imx708_write_reg(imx708, IMX708_LPF_INTENSITY,
++ IMX708_REG_VALUE_08BIT, qbc_adjust);
++ imx708_write_reg(imx708,
++ IMX708_LPF_INTENSITY_EN,
++ IMX708_REG_VALUE_08BIT,
++ IMX708_LPF_INTENSITY_ENABLED);
++ } else {
++ imx708_write_reg(imx708,
++ IMX708_LPF_INTENSITY_EN,
++ IMX708_REG_VALUE_08BIT,
++ IMX708_LPF_INTENSITY_DISABLED);
++ }
++
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(imx708->sd.ctrl_handler);
+ if (ret)
--- /dev/null
+From 65407c54fb4119e528b70b329448269657e0941e Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 8 Nov 2023 10:05:05 +0000
+Subject: [PATCH] drivers: media: cfe: Don't confuse MHz and Mbps
+
+The driver was interchaning these units when talking about link rate.
+Fix this to avoid confusion. Apart from the logging message change,
+there is no function change in this commit.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 8 ++++----
+ drivers/media/platform/raspberrypi/rp1_cfe/dphy.c | 10 +++++-----
+ drivers/media/platform/raspberrypi/rp1_cfe/dphy.h | 2 +-
+ 3 files changed, 10 insertions(+), 10 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -983,7 +983,7 @@ static void cfe_buffer_queue(struct vb2_
+ spin_unlock_irqrestore(&cfe->state_lock, flags);
+ }
+
+-static u64 sensor_link_frequency(struct cfe_device *cfe)
++static u64 sensor_link_rate(struct cfe_device *cfe)
+ {
+ struct v4l2_mbus_framefmt *source_fmt;
+ struct v4l2_subdev_state *state;
+@@ -1028,11 +1028,11 @@ static u64 sensor_link_frequency(struct
+
+ /* x2 for DDR. */
+ link_freq *= 2;
+- cfe_info("Using a link frequency of %lld Hz\n", link_freq);
++ cfe_info("Using a link rate of %lld Mbps\n", link_freq / (1000 * 1000));
+ return link_freq;
+
+ err:
+- cfe_err("Unable to determine sensor link frequency, using 999 MHz\n");
++ cfe_err("Unable to determine sensor link rate, using 999 Mbps\n");
+ return 999 * 1000000UL;
+ }
+
+@@ -1104,7 +1104,7 @@ static int cfe_start_streaming(struct vb
+ }
+
+ cfe_dbg("Configuring CSI-2 block\n");
+- cfe->csi2.dphy.dphy_freq = sensor_link_frequency(cfe) / 1000000UL;
++ cfe->csi2.dphy.dphy_rate = sensor_link_rate(cfe) / 1000000UL;
+ csi2_open_rx(&cfe->csi2);
+
+ cfe_dbg("Starting sensor streaming\n");
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.c
+@@ -96,7 +96,7 @@ static uint8_t dphy_transaction(struct d
+ return get_tstdout(dphy);
+ }
+
+-static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t freq_mhz)
++static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps)
+ {
+ /* See Table 5-1 on page 65 of dphy databook */
+ static const u16 hsfreqrange_table[][2] = {
+@@ -116,11 +116,11 @@ static void dphy_set_hsfreqrange(struct
+ };
+ unsigned int i;
+
+- if (freq_mhz < 80 || freq_mhz > 1500)
+- dphy_err("DPHY: Frequency %u MHz out of range\n", freq_mhz);
++ if (mbps < 80 || mbps > 1500)
++ dphy_err("DPHY: Datarate %u Mbps out of range\n", mbps);
+
+ for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
+- if (freq_mhz <= hsfreqrange_table[i][0])
++ if (mbps <= hsfreqrange_table[i][0])
+ break;
+ }
+
+@@ -139,7 +139,7 @@ static void dphy_init(struct dphy_data *
+ set_tstclr(dphy, 0);
+ usleep_range(15, 20);
+
+- dphy_set_hsfreqrange(dphy, dphy->dphy_freq);
++ dphy_set_hsfreqrange(dphy, dphy->dphy_rate);
+
+ usleep_range(5, 10);
+ dw_csi2_host_write(dphy, PHY_SHUTDOWNZ, 1);
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/dphy.h
+@@ -15,7 +15,7 @@ struct dphy_data {
+
+ void __iomem *base;
+
+- u32 dphy_freq;
++ u32 dphy_rate;
+ u32 num_lanes;
+ };
+
--- /dev/null
+From 6137fb168c08bd8c41c8421bf26f09ed29479f08 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 15 Nov 2023 08:25:11 +0000
+Subject: [PATCH] overlays: imx296: Fix cam port override for regulators
+
+The override was missing/incorrect for the regulator labels.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/imx296-overlay.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/imx296-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx296-overlay.dts
+@@ -97,8 +97,9 @@
+ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
++ <®_frag>, "target:0=",<&cam0_reg>,
+ <&imx296>, "clocks:0=",<&cam0_clk>,
+- <&imx296>, "VANA-supply:0=",<&cam0_reg>;
++ <&imx296>, "avdd-supply:0=",<&cam0_reg>;
+ clock-frequency = <&clk_over>, "clock-frequency:0";
+ };
+ };
--- /dev/null
+From 7443b602cb503b42dd0ae8e957e26decb420d632 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 10:15:15 +0000
+Subject: [PATCH] overlays: ov5647: Regularise vcm node label name
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -15,7 +15,7 @@
+
+ #include "ov5647.dtsi"
+
+- vcm: ad5398@c {
++ vcm_node: ad5398@c {
+ compatible = "adi,ad5398";
+ reg = <0x0c>;
+ status = "disabled";
+@@ -78,8 +78,8 @@
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+ <&cam_node>, "avdd-supply:0=",<&cam0_reg>;
+- vcm = <&vcm>, "status=okay",
+- <&cam_node>,"lens-focus:0=", <&vcm>;
++ vcm = <&vcm_node>, "status=okay",
++ <&cam_node>,"lens-focus:0=", <&vcm_node>;
+ };
+ };
+
--- /dev/null
+From d484bef133af9c87d64899fc1e1d0be2a7c7785b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 10:16:10 +0000
+Subject: [PATCH] overlays: ov5647: cam0 mode should use cam0_reg
+
+When the cam0 parameter is used, the vcm should be updated to refer to
+the cam0 regulator.
+
+See: https://github.com/raspberrypi/linux/issues/5722
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/ov5647-overlay.dts | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/overlays/ov5647-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ov5647-overlay.dts
+@@ -77,7 +77,8 @@
+ <®_frag>, "target:0=",<&cam0_reg>,
+ <&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>,
++ <&vcm_node>, "VANA-supply:0=",<&cam0_reg>;
+ vcm = <&vcm_node>, "status=okay",
+ <&cam_node>,"lens-focus:0=", <&vcm_node>;
+ };
--- /dev/null
+From 0924b74687bd195b98f223814ff88b4227654e85 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 14:46:14 +0000
+Subject: [PATCH] w1: Disable kernel log spam
+
+See: https://forums.raspberrypi.com/viewtopic.php?p=2159344
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/w1/w1.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/w1/w1.c
++++ b/drivers/w1/w1.c
+@@ -742,8 +742,10 @@ int w1_attach_slave_device(struct w1_mas
+ atomic_set(&sl->refcnt, 1);
+ atomic_inc(&sl->master->refcnt);
+ dev->slave_count++;
++#if 0
+ dev_info(&dev->dev, "Attaching one wire slave %02x.%012llx crc %02x\n",
+ rn->family, (unsigned long long)rn->id, rn->crc);
++#endif
+
+ /* slave modules need to be loaded in a context with unlocked mutex */
+ mutex_unlock(&dev->mutex);
--- /dev/null
+From be8ca764e0530ec8ac18ca03c49e3cda13562d3a Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:21:05 +0000
+Subject: [PATCH] include: uapi: mbus: Add a media bus format enum for 16-bit
+ mono output
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ include/uapi/linux/media-bus-format.h | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/include/uapi/linux/media-bus-format.h
++++ b/include/uapi/linux/media-bus-format.h
+@@ -95,6 +95,7 @@
+ #define MEDIA_BUS_FMT_YUYV12_2X12 0x201e
+ #define MEDIA_BUS_FMT_YVYU12_2X12 0x201f
+ #define MEDIA_BUS_FMT_Y14_1X14 0x202d
++#define MEDIA_BUS_FMT_Y16_1X16 0x202e
+ #define MEDIA_BUS_FMT_UYVY8_1X16 0x200f
+ #define MEDIA_BUS_FMT_VYUY8_1X16 0x2010
+ #define MEDIA_BUS_FMT_YUYV8_1X16 0x2011
--- /dev/null
+From 9abab2e8e5cfbeae6e1b33cc3a5ed773e4e31774 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:25:39 +0000
+Subject: [PATCH] include: uapi: v4l2: Add additional pixel formats for use
+ with PiSP
+
+Add the following formats:
+
+- V4L2_PIX_FMT_RGB48/V4L2_PIX_FMT_BGR48
+ 48-bit RGB where each colour sample is 16-bits.
+
+- V4L2_PIX_FMT_PISP_COMP1_MONO/V4L2_PIX_FMT_PISP_COMP2_MONO
+ 16-bit to 8-bit pisp compressed monochrome pixel format.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/v4l2-core/v4l2-ioctl.c | 8 ++++++--
+ include/uapi/linux/videodev2.h | 6 ++++++
+ 2 files changed, 12 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/v4l2-core/v4l2-ioctl.c
++++ b/drivers/media/v4l2-core/v4l2-ioctl.c
+@@ -1304,6 +1304,8 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_BGRX32: descr = "32-bit XBGR 8-8-8-8"; break;
+ case V4L2_PIX_FMT_RGBA32: descr = "32-bit RGBA 8-8-8-8"; break;
+ case V4L2_PIX_FMT_RGBX32: descr = "32-bit RGBX 8-8-8-8"; break;
++ case V4L2_PIX_FMT_BGR48: descr = "48-bit BGR 16-16-16"; break;
++ case V4L2_PIX_FMT_RGB48: descr = "48-bit RGB 16-16-16"; break;
+ case V4L2_PIX_FMT_GREY: descr = "8-bit Greyscale"; break;
+ case V4L2_PIX_FMT_Y4: descr = "4-bit Greyscale"; break;
+ case V4L2_PIX_FMT_Y6: descr = "6-bit Greyscale"; break;
+@@ -1510,11 +1512,13 @@ static void v4l_fill_fmtdesc(struct v4l2
+ case V4L2_PIX_FMT_PISP_COMP1_RGGB:
+ case V4L2_PIX_FMT_PISP_COMP1_GRBG:
+ case V4L2_PIX_FMT_PISP_COMP1_GBRG:
+- case V4L2_PIX_FMT_PISP_COMP1_BGGR: descr = "PiSP Bayer Comp 1"; break;
++ case V4L2_PIX_FMT_PISP_COMP1_BGGR:
++ case V4L2_PIX_FMT_PISP_COMP1_MONO: descr = "PiSP Bayer Comp 1"; break;
+ case V4L2_PIX_FMT_PISP_COMP2_RGGB:
+ case V4L2_PIX_FMT_PISP_COMP2_GRBG:
+ case V4L2_PIX_FMT_PISP_COMP2_GBRG:
+- case V4L2_PIX_FMT_PISP_COMP2_BGGR: descr = "PiSP Bayer Comp 2"; break;
++ case V4L2_PIX_FMT_PISP_COMP2_BGGR:
++ case V4L2_PIX_FMT_PISP_COMP2_MONO: descr = "PiSP Bayer Comp 2"; break;
+ default:
+ if (fmt->description[0])
+ return;
+--- a/include/uapi/linux/videodev2.h
++++ b/include/uapi/linux/videodev2.h
+@@ -582,6 +582,10 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_ARGB32 v4l2_fourcc('B', 'A', '2', '4') /* 32 ARGB-8-8-8-8 */
+ #define V4L2_PIX_FMT_XRGB32 v4l2_fourcc('B', 'X', '2', '4') /* 32 XRGB-8-8-8-8 */
+
++/* RGB formats (6 bytes per pixel) */
++#define V4L2_PIX_FMT_BGR48 v4l2_fourcc('B', 'G', 'R', '6') /* 16 BGR-16-16-16 */
++#define V4L2_PIX_FMT_RGB48 v4l2_fourcc('R', 'G', 'B', '6') /* 16 RGB-16-16-16 */
++
+ /* Grey formats */
+ #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */
+ #define V4L2_PIX_FMT_Y4 v4l2_fourcc('Y', '0', '4', ' ') /* 4 Greyscale */
+@@ -799,10 +803,12 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_PISP_COMP1_GRBG v4l2_fourcc('P', 'C', '1', 'G')
+ #define V4L2_PIX_FMT_PISP_COMP1_GBRG v4l2_fourcc('P', 'C', '1', 'g')
+ #define V4L2_PIX_FMT_PISP_COMP1_BGGR v4l2_fourcc('P', 'C', '1', 'B')
++#define V4L2_PIX_FMT_PISP_COMP1_MONO v4l2_fourcc('P', 'C', '1', 'M')
+ #define V4L2_PIX_FMT_PISP_COMP2_RGGB v4l2_fourcc('P', 'C', '2', 'R')
+ #define V4L2_PIX_FMT_PISP_COMP2_GRBG v4l2_fourcc('P', 'C', '2', 'G')
+ #define V4L2_PIX_FMT_PISP_COMP2_GBRG v4l2_fourcc('P', 'C', '2', 'g')
+ #define V4L2_PIX_FMT_PISP_COMP2_BGGR v4l2_fourcc('P', 'C', '2', 'B')
++#define V4L2_PIX_FMT_PISP_COMP2_MONO v4l2_fourcc('P', 'C', '2', 'M')
+
+ /* SDR formats - used only for Software Defined Radio devices */
+ #define V4L2_SDR_FMT_CU8 v4l2_fourcc('C', 'U', '0', '8') /* IQ u8 */
--- /dev/null
+From 88d06a674009ad5b77234537527a800e6e0e88a3 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:28:55 +0000
+Subject: [PATCH] drivers: media: cfe: Add 16-bit and compressed mono format
+ support
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe_fmts.h | 14 ++++++++++----
+ 1 file changed, 10 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -249,28 +249,34 @@ static const struct cfe_fmt formats[] =
+ .code = MEDIA_BUS_FMT_Y10_1X10,
+ .depth = 10,
+ .csi_dt = 0x2b,
+- .remap = { V4L2_PIX_FMT_Y16 },
++ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y12P,
+ .code = MEDIA_BUS_FMT_Y12_1X12,
+ .depth = 12,
+ .csi_dt = 0x2c,
+- .remap = { V4L2_PIX_FMT_Y16 },
++ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y14P,
+ .code = MEDIA_BUS_FMT_Y14_1X14,
+ .depth = 14,
+ .csi_dt = 0x2d,
+- .remap = { V4L2_PIX_FMT_Y16 },
++ .remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_Y16,
++ .code = MEDIA_BUS_FMT_Y16_1X16,
+ .depth = 16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
+ },
+-
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO,
++ .code = MEDIA_BUS_FMT_Y16_1X16,
++ .depth = 8,
++ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ },
+ /* Embedded data format */
+ {
+ .fourcc = V4L2_META_FMT_SENSOR_DATA,
--- /dev/null
+From 2affda8d2b172aa0fd22778983d983fc9522e621 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Thu, 16 Nov 2023 14:29:47 +0000
+Subject: [PATCH] drivers: media: pisp_be: Add mono and 48-bit RGB pixel format
+ support
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../raspberrypi/pisp_be/pisp_be_formats.h | 45 +++++++++++++++++++
+ 1 file changed, 45 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_formats.h
+@@ -234,6 +234,24 @@ static const struct pisp_be_format suppo
+ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
+ .colorspace_default = V4L2_COLORSPACE_SRGB,
+ },
++ {
++ .fourcc = V4L2_PIX_FMT_RGB48,
++ .align = 64,
++ .bit_depth = 48,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SRGB,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_BGR48,
++ .align = 64,
++ .bit_depth = 48,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_ALL_SRGB,
++ .colorspace_default = V4L2_COLORSPACE_SRGB,
++ },
+ /* Bayer formats - 8-bit */
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+@@ -457,6 +475,33 @@ static const struct pisp_be_format suppo
+ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
+ .colorspace_default = V4L2_COLORSPACE_RAW,
+ },
++ /* Greyscale Formats */
++ {
++ .fourcc = V4L2_PIX_FMT_GREY,
++ .bit_depth = 8,
++ .align = 32,
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_Y16,
++ .bit_depth = 16,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
++ {
++ .fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO,
++ .bit_depth = 8,
++ .align = 32,
++ .plane_factor = { P3(1.0) },
++ .num_planes = 1,
++ .colorspace_mask = V4L2_COLORSPACE_MASK_RAW,
++ .colorspace_default = V4L2_COLORSPACE_RAW,
++ },
+ /* Opaque BE format for HW verification. */
+ {
+ .fourcc = V4L2_PIX_FMT_RPI_BE,
--- /dev/null
+From 52545628c07be2fd1c9df598a17130d92f12da23 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 20 Nov 2023 15:17:34 +0000
+Subject: [PATCH] ASoC: dwc: Remove check in set_bclk_ratio handling
+
+A check added to dw_i2s_set_bclk_ratio that the data format is
+consistent with the ratio seems reasonable but breaks when the
+ratio is changed before the format. Remove the check - it is
+unnecessary.
+
+See: https://github.com/raspberrypi/linux/issues/5724
+Fixes: 9c6694c24f26 ("ASOC: dwc: Fix 16-bit audio handling")
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -427,11 +427,8 @@ static int dw_i2s_set_bclk_ratio(struct
+ unsigned int ratio)
+ {
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+- struct i2s_clk_config_data *config = &dev->config;
+
+ dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio);
+- if (ratio < config->data_width * 2)
+- return -EINVAL;
+
+ switch (ratio) {
+ case 32:
--- /dev/null
+From 5a0aa24b8ff58ceaf98c62670156bef7f48ed32b Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 21 Nov 2023 15:08:38 +0000
+Subject: [PATCH] overlays: README: Fix cut-and-paste errors
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -1673,7 +1673,7 @@ Params: 24db_digital_gain Allow ga
+ responsibility of the user to ensure that
+ the Digital volume control is set to a value
+ that does not result in clipping/distortion!)
+- slave Force DAC+ Pro into slave mode, using Pi as
++ slave Force AMP100 into slave mode, using Pi as
+ master for bit clock and frame clock.
+ leds_off If set to 'true' the onboard indicator LEDs
+ are switched off at all times.
+@@ -1713,7 +1713,7 @@ Params: 24db_digital_gain Allow ga
+ responsibility of the user to ensure that
+ the Digital volume control is set to a value
+ that does not result in clipping/distortion!)
+- slave Force DAC+ Pro into slave mode, using Pi as
++ slave Force DAC+ into slave mode, using Pi as
+ master for bit clock and frame clock.
+ leds_off If set to 'true' the onboard indicator LEDs
+ are switched off at all times.
+@@ -1736,7 +1736,7 @@ Params: 24db_digital_gain Allow ga
+ responsibility of the user to ensure that
+ the Digital volume control is set to a value
+ that does not result in clipping/distortion!)
+- slave Force DAC+ Pro into slave mode, using Pi as
++ slave Force DAC+ADC into slave mode, using Pi as
+ master for bit clock and frame clock.
+ leds_off If set to 'true' the onboard indicator LEDs
+ are switched off at all times.
--- /dev/null
+From e60fbc34aa98b3ba2c9338ad628fc8d8137e9065 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 14 Nov 2023 18:36:19 +0000
+Subject: [PATCH] media/i2c: ov7251: Switch from V4L2_CID_GAIN to
+ V4L2_CID_ANALOGUE_GAIN
+
+The mainline driver has implemented analogue gain using the control
+V4L2_CID_GAIN instead of V4L2_CID_ANALOGUE_GAIN.
+
+libcamera requires V4L2_CID_ANALOGUE_GAIN, and therefore fails.
+
+Update the driver to use V4L2_CID_ANALOGUE_GAIN.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/i2c/ov7251.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/i2c/ov7251.c
++++ b/drivers/media/i2c/ov7251.c
+@@ -1063,7 +1063,7 @@ static int ov7251_s_ctrl(struct v4l2_ctr
+ case V4L2_CID_EXPOSURE:
+ ret = ov7251_set_exposure(ov7251, ctrl->val);
+ break;
+- case V4L2_CID_GAIN:
++ case V4L2_CID_ANALOGUE_GAIN:
+ ret = ov7251_set_gain(ov7251, ctrl->val);
+ break;
+ case V4L2_CID_TEST_PATTERN:
+@@ -1588,7 +1588,7 @@ static int ov7251_init_ctrls(struct ov72
+ ov7251->exposure = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_EXPOSURE, 1, 32, 1, 32);
+ ov7251->gain = v4l2_ctrl_new_std(&ov7251->ctrls, &ov7251_ctrl_ops,
+- V4L2_CID_GAIN, 16, 1023, 1, 16);
++ V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 16);
+ v4l2_ctrl_new_std_menu_items(&ov7251->ctrls, &ov7251_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov7251_test_pattern_menu) - 1,
--- /dev/null
+From 444884f7b62bfe5ef313dd1d47f81a40e695ab0b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 17 Nov 2023 14:43:44 +0000
+Subject: [PATCH] drm/vc4: Drop planes that are completely off-screen
+
+It is permitted for a plane to be configured such that none
+of it is on-screen via either negative dest rectangle X,Y
+offset, or just an offset that is greater than the crtc
+dimensions.
+
+These planes were resized via drm_atomic_helper_check_plane_state
+such that the source rectangle had a zero width or height, but
+they still created a dlist entry even though they contributed
+no pixels. In the case of vc6_plane_mode_set, that it could result
+in negative values being written into registers, which caused
+incorrect behaviour.
+
+Drop planes that result in a source width or height of 0 pixels
+to avoid the incorrect rendering.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1108,6 +1108,12 @@ static int vc4_plane_mode_set(struct drm
+ width = vc4_state->src_w[0] >> 16;
+ height = vc4_state->src_h[0] >> 16;
+
++ if (!width || !height) {
++ /* 0 source size probably means the plane is offscreen */
++ vc4_state->dlist_initialized = 1;
++ return 0;
++ }
++
+ /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
+ * and 4:4:4, scl1 should be set to scl0 so both channels of
+ * the scaler do the same thing. For YUV, the Y plane needs
+@@ -1623,6 +1629,12 @@ static int vc6_plane_mode_set(struct drm
+ width = vc4_state->src_w[0] >> 16;
+ height = vc4_state->src_h[0] >> 16;
+
++ if (!width || !height) {
++ /* 0 source size probably means the plane is offscreen */
++ vc4_state->dlist_initialized = 1;
++ return 0;
++ }
++
+ /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
+ * and 4:4:4, scl1 should be set to scl0 so both channels of
+ * the scaler do the same thing. For YUV, the Y plane needs
+@@ -1994,6 +2006,9 @@ int vc4_plane_atomic_check(struct drm_pl
+ if (ret)
+ return ret;
+
++ if (!vc4_state->src_w[0] || !vc4_state->src_h[0])
++ return 0;
++
+ ret = vc4_plane_allocate_lbm(new_plane_state);
+ if (ret)
+ return ret;
--- /dev/null
+From 91ad217f93232fb3a0b52487fec67860fb29e93a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 17 Nov 2023 14:50:11 +0000
+Subject: [PATCH] drm/bridge: display-connector: Select DRM_KMS_HELPER
+
+Commit 7cd70656d128 ("drm/bridge: display-connector: implement
+bus fmts callbacks") added use of drm_atomic_helper_bridge_*
+functions, but didn't select the dependency of DRM_KMS_HELPER.
+If nothing else selected that dependency it resulted in a
+build failure.
+
+Select the missing dependency.
+
+Fixes: 7cd70656d128 ("drm/bridge: display-connector: implement bus fmts callbacks")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/bridge/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/bridge/Kconfig
++++ b/drivers/gpu/drm/bridge/Kconfig
+@@ -67,6 +67,7 @@ config DRM_CROS_EC_ANX7688
+ config DRM_DISPLAY_CONNECTOR
+ tristate "Display connector support"
+ depends on OF
++ select DRM_KMS_HELPER
+ help
+ Driver for display connectors with support for DDC and hot-plug
+ detection. Most display controllers handle display connectors
--- /dev/null
+From 51712a6493bf8824419f51ca7950e7d88f48b699 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 22 Nov 2023 18:36:54 +0000
+Subject: [PATCH] drm: vc4: Free the dlist alloc immediately if it never hit
+ the hw
+
+atomic_check creates a state, and allocates the dlist memory for
+it such that atomic_flush can not fail.
+
+On destroy that dlist allocation was being put in the stale list,
+even though it had never been programmed into the hardware,
+therefore doing lots of atomic_checks could consume all the dlist
+memory and fail.
+
+If the dlist has never been programmed into the hardware, then
+free it immediately.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_hvs.c | 6 +++++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -696,6 +696,7 @@ struct vc4_hvs_dlist_allocation {
+ struct drm_mm_node mm_node;
+ unsigned int channel;
+ u8 target_frame_count;
++ bool dlist_programmed;
+ };
+
+ struct vc4_crtc_state {
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -697,8 +697,11 @@ void vc4_hvs_mark_dlist_entry_stale(stru
+ * Kunit tests run with a mock device and we consider any hardware
+ * access a test failure. Let's free the dlist allocation right away if
+ * we're running under kunit, we won't risk a dlist corruption anyway.
++ *
++ * Likewise if the allocation was only checked and never programmed, we
++ * can destroy the allocation immediately.
+ */
+- if (kunit_get_current_test()) {
++ if (kunit_get_current_test() || !alloc->dlist_programmed) {
+ spin_lock_irqsave(&hvs->mm_lock, flags);
+ vc4_hvs_free_dlist_entry_locked(hvs, alloc);
+ spin_unlock_irqrestore(&hvs->mm_lock, flags);
+@@ -1201,6 +1204,7 @@ static void vc4_hvs_install_dlist(struct
+ return;
+
+ WARN_ON(!vc4_state->mm);
++ vc4_state->mm->dlist_programmed = true;
+
+ if (vc4->gen >= VC4_GEN_6)
+ HVS_WRITE(SCALER6_DISPX_LPTRS(vc4_state->assigned_channel),
--- /dev/null
+From 2a6c3115f4142e23ca10c984d7f65ac8fb901cc2 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 8 Nov 2023 15:50:39 +0000
+Subject: [PATCH] input: edt-ft5x06: Include I2C details in names for the
+ devices
+
+libinput uses the input device name alone. If you have two
+identical input devices, then there is no way to differentiate
+between them, and in the case of touchscreens that means no
+way to associate them with the appropriate display device.
+
+Add the I2C bus and address to the start of the input device
+name so that the name is always unique within the system.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/edt-ft5x06.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -67,6 +67,7 @@
+ #define TOUCH_EVENT_RESERVED 0x03
+
+ #define EDT_NAME_LEN 23
++#define EDT_NAME_PREFIX_LEN 8
+ #define EDT_SWITCH_MODE_RETRIES 10
+ #define EDT_SWITCH_MODE_DELAY 5 /* msec */
+ #define EDT_RAW_DATA_RETRIES 100
+@@ -134,7 +135,7 @@ struct edt_ft5x06_ts_data {
+ int max_support_points;
+ unsigned int known_ids;
+
+- char name[EDT_NAME_LEN];
++ char name[EDT_NAME_PREFIX_LEN + EDT_NAME_LEN];
+ char fw_version[EDT_NAME_LEN];
+ int init_td_status;
+
+@@ -965,6 +966,9 @@ static int edt_ft5x06_ts_identify(struct
+ char *model_name = tsdata->name;
+ char *fw_version = tsdata->fw_version;
+
++ snprintf(model_name, EDT_NAME_PREFIX_LEN, "%s ", dev_name(&client->dev));
++ model_name += strlen(model_name);
++
+ /* see what we find if we assume it is a M06 *
+ * if we get less than EDT_NAME_LEN, we don't want
+ * to have garbage in there
--- /dev/null
+From a420bbde05f8a6691b0c3e0830092e443365aaa7 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 8 Nov 2023 16:20:27 +0000
+Subject: [PATCH] input: goodix: Include I2C details in names for the devices
+
+libinput uses the input device name alone. If you have two
+identical input devices, then there is no way to differentiate
+between them, and in the case of touchscreens that means no
+way to associate them with the appropriate display device.
+
+Add the I2C bus and address to the start of the input device
+name so that the name is always unique within the system.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/goodix.c | 5 ++++-
+ drivers/input/touchscreen/goodix.h | 3 +++
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/input/touchscreen/goodix.c
++++ b/drivers/input/touchscreen/goodix.c
+@@ -1211,7 +1211,10 @@ static int goodix_configure_dev(struct g
+ return -ENOMEM;
+ }
+
+- ts->input_dev->name = "Goodix Capacitive TouchScreen";
++ snprintf(ts->name, GOODIX_NAME_MAX_LEN, "%s Goodix Capacitive TouchScreen",
++ dev_name(&ts->client->dev));
++
++ ts->input_dev->name = ts->name;
+ ts->input_dev->phys = "input/ts";
+ ts->input_dev->id.bustype = BUS_I2C;
+ ts->input_dev->id.vendor = 0x0416;
+--- a/drivers/input/touchscreen/goodix.h
++++ b/drivers/input/touchscreen/goodix.h
+@@ -57,6 +57,8 @@
+ #define GOODIX_CONFIG_MAX_LENGTH 240
+ #define GOODIX_MAX_KEYS 7
+
++#define GOODIX_NAME_MAX_LEN 38
++
+ enum goodix_irq_pin_access_method {
+ IRQ_PIN_ACCESS_NONE,
+ IRQ_PIN_ACCESS_GPIO,
+@@ -91,6 +93,7 @@ struct goodix_ts_data {
+ enum gpiod_flags gpiod_rst_flags;
+ char id[GOODIX_ID_MAX_LEN + 1];
+ char cfg_name[64];
++ char name[GOODIX_NAME_MAX_LEN];
+ u16 version;
+ bool reset_controller_at_probe;
+ bool load_cfg_from_disk;
--- /dev/null
+From a984fda6b2c24dbf1ca21924f99c8f9418f5765e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 22 Nov 2023 19:17:44 +0000
+Subject: [PATCH] drm: vc4: Block swiotlb bounce buffers being imported as
+ dmabuf
+
+The dmabuf import already checks that the backing buffer is contiguous
+and rejects it if it isn't. vc4 also requires that the buffer is
+in the bottom 1GB of RAM, and this is all correctly defined via
+dma-ranges.
+
+However the kernel silently uses swiotlb to bounce dma buffers
+around if they are in the wrong region. This relies on dma sync
+functions to be called in order to copy the data to/from the
+bounce buffer.
+
+DRM is based on all memory allocations being coherent with the
+GPU so that any updates to a framebuffer will be acted on without
+the need for any additional update. This is fairly fundamentally
+incompatible with needing to call dma_sync_ to handle the bounce
+buffer copies, and therefore we have to detect and reject mappings
+that use bounce buffers.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.c | 26 ++++++++++++++++++++++++--
+ 1 file changed, 24 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.c
++++ b/drivers/gpu/drm/vc4/vc4_drv.c
+@@ -29,6 +29,7 @@
+ #include <linux/of_platform.h>
+ #include <linux/platform_device.h>
+ #include <linux/pm_runtime.h>
++#include <linux/dma-direct.h>
+
+ #include <drm/drm_aperture.h>
+ #include <drm/drm_atomic_helper.h>
+@@ -175,6 +176,19 @@ static void vc4_close(struct drm_device
+ kfree(vc4file);
+ }
+
++struct drm_gem_object *
++vc4_prime_import_sg_table(struct drm_device *dev,
++ struct dma_buf_attachment *attach,
++ struct sg_table *sgt)
++{
++ phys_addr_t phys = dma_to_phys(dev->dev, sg_dma_address(sgt->sgl));
++
++ if (is_swiotlb_buffer(dev->dev, phys))
++ return ERR_PTR(-EINVAL);
++
++ return drm_gem_dma_prime_import_sg_table(dev, attach, sgt);
++}
++
+ DEFINE_DRM_GEM_FOPS(vc4_drm_fops);
+
+ static const struct drm_ioctl_desc vc4_drm_ioctls[] = {
+@@ -211,7 +225,11 @@ const struct drm_driver vc4_drm_driver =
+
+ .gem_create_object = vc4_create_object,
+
+- DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vc4_bo_dumb_create),
++ .dumb_create = vc4_bo_dumb_create,
++ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
++ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
++ .gem_prime_import_sg_table = vc4_prime_import_sg_table,
++ .gem_prime_mmap = drm_gem_prime_mmap,
+
+ .ioctls = vc4_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(vc4_drm_ioctls),
+@@ -234,7 +252,11 @@ const struct drm_driver vc5_drm_driver =
+ .debugfs_init = vc4_debugfs_init,
+ #endif
+
+- DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(vc5_dumb_create),
++ .dumb_create = vc5_dumb_create,
++ .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
++ .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
++ .gem_prime_import_sg_table = vc4_prime_import_sg_table,
++ .gem_prime_mmap = drm_gem_prime_mmap,
+
+ .fops = &vc4_drm_fops,
+
--- /dev/null
+From 77a01f7da77446277139d8e9ce63f078cbe1ecfe Mon Sep 17 00:00:00 2001
+From: Kenny <aSmig+github@romhat.net>
+Date: Wed, 22 Nov 2023 16:22:37 -0800
+Subject: [PATCH] overlays: i2c-sensor: Add adt7410 support
+
+See https://github.com/raspberrypi/linux/pull/5738
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 12 ++++++++----
+ .../boot/dts/overlays/i2c-sensor-common.dtsi | 18 +++++++++++++++++-
+ 2 files changed, 25 insertions(+), 5 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2144,10 +2144,14 @@ Name: i2c-sensor
+ Info: Adds support for a number of I2C barometric pressure, temperature,
+ light level and chemical sensors on i2c_arm
+ Load: dtoverlay=i2c-sensor,<param>=<val>
+-Params: addr Set the address for the BH1750, BME280, BME680,
+- BMP280, BMP380, CCS811, DS1621, HDC100X, JC42,
+- LM75, MCP980x, MPU6050, MPU9250, MS5637, MS5803,
+- MS5805, MS5837, MS8607, SHT3x or TMP102
++Params: addr Set the address for the ADT7410, BH1750, BME280,
++ BME680, BMP280, BMP380, CCS811, DS1621, HDC100X,
++ JC42, LM75, MCP980x, MPU6050, MPU9250, MS5637,
++ MS5803, MS5805, MS5837, MS8607, SHT3x or TMP102
++
++ adt7410 Select the Analog Devices ADT7410 and ADT7420
++ temperature sensors
++ Valid address 0x48-0x4b, default 0x48
+
+ aht10 Select the Aosong AHT10 temperature and humidity
+ sensor
+--- a/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
++++ b/arch/arm/boot/dts/overlays/i2c-sensor-common.dtsi
+@@ -508,6 +508,21 @@
+ };
+ };
+
++ fragment@34 {
++ target = <&i2cbus>;
++ __dormant__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ adt7410: adt7410@48 {
++ compatible = "adi,adt7410", "adi,adt7420";
++ reg = <0x48>;
++ status = "okay";
++ };
++ };
++ };
++
+ __overrides__ {
+ bme280 = <0>,"+0";
+ bmp085 = <0>,"+1";
+@@ -543,6 +558,7 @@
+ mpu9250 = <0>,"+29";
+ bno055 = <0>,"+31";
+ sht4x = <0>,"+32";
++ adt7410 = <0>,"+34";
+
+ addr = <&bme280>,"reg:0", <&bmp280>,"reg:0", <&tmp102>,"reg:0",
+ <&lm75>,"reg:0", <&hdc100x>,"reg:0", <&sht3x>,"reg:0",
+@@ -552,7 +568,7 @@
+ <&ms5837>,"reg:0", <&ms8607>,"reg:0",
+ <&mpu6050>,"reg:0", <&mpu9250>,"reg:0",
+ <&bno055>,"reg:0", <&sht4x>,"reg:0",
+- <&bmp380>,"reg:0";
++ <&bmp380>,"reg:0", <&adt7410>,"reg:0";
+ int_pin = <&max30102>, "interrupts:0",
+ <&mpu6050>, "interrupts:0",
+ <&mpu9250>, "interrupts:0";
--- /dev/null
+From 7c185b18a1c6a6cd6ab8805fef03a4a8c9931656 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Fri, 24 Nov 2023 13:57:09 +0000
+Subject: [PATCH] overlays: hat_map: Add pisound mapping
+
+See: https://github.com/raspberrypi/linux/issues/5741
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/hat_map.dts | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/hat_map.dts
++++ b/arch/arm/boot/dts/overlays/hat_map.dts
+@@ -6,6 +6,11 @@
+ overlay = "iqaudio-codec";
+ };
+
++ pisound {
++ uuid = [ a7ee5d28 da03 41f5 bbd7 20438a4bec5d ];
++ overlay = "pisound";
++ };
++
+ recalbox-rgbdual {
+ uuid = [ 1c955808 681f 4bbc a2ef b7ea47cd388e ];
+ overlay = "recalboxrgbdual";
--- /dev/null
+From bc76ab2e772242d0d92d38b2a5648485ee1a1b44 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michael=20B=C3=BCchler?= <michael.buechler@posteo.net>
+Date: Sun, 19 Nov 2023 01:22:20 +0100
+Subject: [PATCH] drm/vc4: Set TV margins on the composite connector state
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The Raspberry Pi bootloader can pass a set of margin values to the
+kernel cmdline as part of the video= option. These should result in
+black borders on the output to compensate for TVs where the visible area
+on the screen is smaller than the full extents of the video image.
+
+With the VC4 driver this currently works on an HDMI connector, but not
+on a composite video connector.
+
+The TV margins from the kernel cmdline are available in the
+drm_cmdline_mode structure of the composite video connector. Apply them
+to the connector state in the connector reset function. This is how it
+is implemented for the VC4 HDMI connector.
+
+Signed-off-by: Michael Büchler <michael.buechler@posteo.net>
+---
+ drivers/gpu/drm/vc4/vc4_vec.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/gpu/drm/vc4/vc4_vec.c
++++ b/drivers/gpu/drm/vc4/vc4_vec.c
+@@ -436,6 +436,9 @@ static void vc4_vec_connector_reset(stru
+ /* preserve TV standard */
+ if (connector->state)
+ connector->state->tv.mode = vc4_vec_get_default_mode(connector);
++
++ /* apply TV margins from the cmdline_mode */
++ drm_atomic_helper_connector_tv_reset(connector);
+ }
+
+ static int vc4_vec_connector_atomic_check(struct drm_connector *conn,
+@@ -483,6 +486,8 @@ static int vc4_vec_connector_init(struct
+
+ drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs);
+
++ drm_connector_attach_tv_margin_properties(connector);
++
+ drm_object_attach_property(&connector->base,
+ dev->mode_config.tv_mode_property,
+ vc4_vec_get_default_mode(connector));
--- /dev/null
+From 63d8c0f5185169058384142547655fc038aae0bf Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 9 Aug 2023 21:04:40 +0100
+Subject: [PATCH] drm: panel: jdi-lt070me05000: Add prepare_upstream_first flag
+
+The panel driver wants to send DCS commands from the prepare
+hook, therefore the DSI host wants to be pre_enabled first.
+Set the flag to achieve this.
+
+https://forums.raspberrypi.com/viewtopic.php?t=354708
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/panel/panel-jdi-lt070me05000.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
++++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c
+@@ -437,6 +437,7 @@ static int jdi_panel_add(struct jdi_pane
+ return ret;
+ }
+
++ jdi->base.prepare_upstream_first = true;
+ drm_panel_init(&jdi->base, &jdi->dsi->dev, &jdi_panel_funcs,
+ DRM_MODE_CONNECTOR_DSI);
+
--- /dev/null
+From 76b1bbf3ec3be0afdc768863ab7e9bbd2734b97b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Fri, 24 Nov 2023 14:29:57 +0000
+Subject: [PATCH] drivers: media: cfe: Find the source pads on the sensor
+ entity
+
+The driver was assuming that pad 0 on the sensor entity was the
+appropriate source pad, but this isn't necessarily the case.
+With video-mux, it has the sink pads first, and then the source
+pad as the last one.
+
+Iterate through the sensor pads to find the relevant source pads.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 15 ++++++++++++---
+ 1 file changed, 12 insertions(+), 3 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -1826,7 +1826,7 @@ static void cfe_unregister_nodes(struct
+
+ static int cfe_link_node_pads(struct cfe_device *cfe)
+ {
+- unsigned int i;
++ unsigned int i, source_pad = 0;
+ int ret;
+
+ for (i = 0; i < CSI2_NUM_CHANNELS; i++) {
+@@ -1835,14 +1835,23 @@ static int cfe_link_node_pads(struct cfe
+ if (!check_state(cfe, NODE_REGISTERED, i))
+ continue;
+
+- if (i < cfe->sensor->entity.num_pads) {
++ /* Find next source pad */
++ while (source_pad < cfe->sensor->entity.num_pads &&
++ !(cfe->sensor->entity.pads[source_pad].flags &
++ MEDIA_PAD_FL_SOURCE))
++ source_pad++;
++
++ if (source_pad < cfe->sensor->entity.num_pads) {
+ /* Sensor -> CSI2 */
+- ret = media_create_pad_link(&cfe->sensor->entity, i,
++ ret = media_create_pad_link(&cfe->sensor->entity, source_pad,
+ &cfe->csi2.sd.entity, i,
+ MEDIA_LNK_FL_IMMUTABLE |
+ MEDIA_LNK_FL_ENABLED);
+ if (ret)
+ return ret;
++
++ /* Dealt with that source_pad, look at the next one next time */
++ source_pad++;
+ }
+
+ /* CSI2 channel # -> /dev/video# */
--- /dev/null
+From 08c5904ad00ffc54d37058ead19814f8a85f6f39 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 27 Nov 2023 14:50:58 +0000
+Subject: [PATCH] dtoverlays: Add option for cam0 to camera-mux-Nport overlays
+
+Seeing as the mux can be connected to either CAM/DISP1 or
+CAM/DISP0 on a Pi5, add a cam0 override to allow configuration
+of which is used. Default (as with all camera overlays) is CAM/DISP1.
+
+The overlay does NOT update the camera regulator used by all the
+sensors as doing so would be a nightmare. The Arducam mulitplexer
+boards these overlays are initially supporting seem to tie the
+regulator GPIO for all the sensors high anyway.
+If it was viewed as necessary, then creating an additional
+regulator that listed cam[01]_reg as the parent should work.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 4 ++++
+ arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts | 7 +++++--
+ arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts | 7 +++++--
+ 3 files changed, 14 insertions(+), 4 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -823,6 +823,8 @@ Params: cam0-arducam-64mp Select A
+ cam1-ov9281 Select OV9281 for camera on port 1
+ cam1-imx290-clk-freq Set clock frequency for an IMX290 on port 1
+
++ cam0 Connect the mux to CAM0 port (default is CAM1)
++
+
+ Name: camera-mux-4port
+ Info: Configures a 4 port camera multiplexer
+@@ -878,6 +880,8 @@ Params: cam0-arducam-64mp Select A
+ cam3-ov9281 Select OV9281 for camera on port 3
+ cam3-imx290-clk-freq Set clock frequency for an IMX290 on port 3
+
++ cam0 Connect the mux to CAM0 port (default is CAM1)
++
+
+ Name: cap1106
+ Info: Enables the ability to use the cap1106 touch sensor as a keyboard
+--- a/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
+@@ -77,7 +77,7 @@
+ };
+
+ /* Mux define */
+- fragment@200 {
++ i2c_frag: fragment@200 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -294,7 +294,7 @@
+ };
+ };
+
+- fragment@201 {
++ csi_frag: fragment@201 {
+ target = <&csi1>;
+ __overlay__ {
+ status = "okay";
+@@ -501,5 +501,8 @@
+ <&imx290_0>,"clock-frequency:0";
+ cam1-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+ <&imx290_1>,"clock-frequency:0";
++
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&csi_frag>, "target:0=",<&csi0>;
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
+@@ -135,7 +135,7 @@
+ };
+
+ /* Mux define */
+- fragment@200 {
++ i2c_frag: fragment@200 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+ #address-cells = <1>;
+@@ -552,7 +552,7 @@
+ };
+ };
+
+- fragment@201 {
++ csi_frag: fragment@201 {
+ target = <&csi1>;
+ __overlay__ {
+ status = "okay";
+@@ -872,5 +872,8 @@
+ <&imx290_2>,"clock-frequency:0";
+ cam3-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+ <&imx290_3>,"clock-frequency:0";
++
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&csi_frag>, "target:0=",<&csi0>;
+ };
+ };
--- /dev/null
+From 6fac5d5ed6d023733ef7304afc88c8d89467a204 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 27 Nov 2023 12:16:04 +0000
+Subject: [PATCH] ASoC: dwc: Permit sample rates up to 384kHz
+
+The BCM2835 I2S block advertises clock rates up to 384kHz, and there's
+no reason why RP1's DWC I2S block shouldn't do the same.
+
+See: https://github.com/raspberrypi/linux/issues/5748
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -667,7 +667,7 @@ static int dw_configure_dai_by_dt(struct
+ if (WARN_ON(idx >= ARRAY_SIZE(bus_widths)))
+ return -EINVAL;
+
+- ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_192000);
++ ret = dw_configure_dai(dev, dw_i2s_dai, SNDRV_PCM_RATE_8000_384000);
+ if (ret < 0)
+ return ret;
+
--- /dev/null
+From cbc65d9c95088d1437a5a84c7d6628b8d27a86f1 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 28 Nov 2023 12:14:03 +0000
+Subject: [PATCH] ASoC: dwc: Fix full-duplex mode
+
+Configuration of the DMA register was carelessly zeroing bits that may
+used by a stream in the other direction. Preserve them instead.
+
+See: https://github.com/raspberrypi/linux/issues/5741
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 20 +++++++++++++-------
+ 1 file changed, 13 insertions(+), 7 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -189,10 +189,17 @@ static void dw_i2s_config(struct dw_i2s_
+ {
+ struct i2s_clk_config_data *config = &dev->config;
+ u32 ch_reg;
+- u32 dmacr = 0;
++ u32 dmacr;
+
+ i2s_disable_channels(dev, stream);
+
++ dmacr = i2s_read_reg(dev->i2s_base, DMACR);
++
++ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
++ dmacr &= ~(DMACR_DMAEN_TXCH0 * 0xf);
++ else
++ dmacr &= ~(DMACR_DMAEN_RXCH0 * 0xf);
++
+ for (ch_reg = 0; ch_reg < (config->chan_nr / 2); ch_reg++) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ i2s_write_reg(dev->i2s_base, TCR(ch_reg),
+@@ -210,10 +217,6 @@ static void dw_i2s_config(struct dw_i2s_
+ dmacr |= (DMACR_DMAEN_RXCH0 << ch_reg);
+ }
+ }
+- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+- dmacr |= DMACR_DMAEN_TX;
+- else if (stream == SNDRV_PCM_STREAM_CAPTURE)
+- dmacr |= DMACR_DMAEN_RX;
+
+ i2s_write_reg(dev->i2s_base, DMACR, dmacr);
+ }
+@@ -319,10 +322,13 @@ static int dw_i2s_startup(struct snd_pcm
+
+ dw_i2s_config(dev, substream->stream);
+ dmacr = i2s_read_reg(dev->i2s_base, DMACR);
+- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
++ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dma_data = &dev->play_dma_data;
+- else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
++ dmacr |= DMACR_DMAEN_TX;
++ } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ dma_data = &dev->capture_dma_data;
++ dmacr |= DMACR_DMAEN_RX;
++ }
+
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)dma_data);
+ i2s_write_reg(dev->i2s_base, DMACR, dmacr);
--- /dev/null
+From a7ac27fbac1aa3fb8316cf1ff6dd2f81109d46d2 Mon Sep 17 00:00:00 2001
+From: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Date: Fri, 29 Sep 2023 17:05:55 +0200
+Subject: [PATCH] ASoC: pcm512x: Adds bindings for TAS575x devices
+
+commit 736b884a7b68c4eeb66dbf75b97c8ec9b9eeff7f upstream.
+
+The TAS5754/6 power amplifiers use the same pcm512x driver with
+only minor restictions described in the bindings document.
+
+Signed-off-by: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Reviewed-by: Rob Herring <robh@kernel.org>
+Link: https://lore.kernel.org/r/20230929150555.405388-1-joerg.hifiberry@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ Documentation/devicetree/bindings/sound/pcm512x.txt | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/Documentation/devicetree/bindings/sound/pcm512x.txt
++++ b/Documentation/devicetree/bindings/sound/pcm512x.txt
+@@ -1,12 +1,12 @@
+-PCM512x audio CODECs
++PCM512x and TAS575x audio CODECs/amplifiers
+
+ These devices support both I2C and SPI (configured with pin strapping
+-on the board).
++on the board). The TAS575x devices only support I2C.
+
+ Required properties:
+
+- - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141" or
+- "ti,pcm5142"
++ - compatible : One of "ti,pcm5121", "ti,pcm5122", "ti,pcm5141",
++ "ti,pcm5142", "ti,tas5754" or "ti,tas5756"
+
+ - reg : the I2C address of the device for I2C, the chip select
+ number for SPI.
+@@ -25,6 +25,7 @@ Optional properties:
+ through <6>. The device will be configured for clock input on the
+ given pll-in pin and PLL output on the given pll-out pin. An
+ external connection from the pll-out pin to the SCLK pin is assumed.
++ Caution: the TAS-desvices only support gpios 1,2 and 3
+
+ Examples:
+
--- /dev/null
+From a535dd07aff73f3a6eb40174ab5dc413d05f36a1 Mon Sep 17 00:00:00 2001
+From: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Date: Fri, 29 Sep 2023 17:07:20 +0200
+Subject: [PATCH] ASoC: Adds support for TAS575x to the pcm512x driver
+
+commit 1f817805262c2c34142291da376d4932d3c493bc upstream.
+
+Enables the existing pcm512x driver to control the almost
+compatible TAS5754 and -76 amplifers. Both amplifiers support
+only an I2C interface and the internal PLL must be always
+on to provide necessary clocks to the amplifier section.
+Tested on TAS5756 with support from Andreas Arbesser-Krasser
+from Texas Instruments <a-krasser@ti.com>
+
+Signed-off-by: Joerg Schambacher <joerg.hifiberry@gmail.com>
+Link: https://lore.kernel.org/r/20230929150722.405415-1-joerg.hifiberry@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+---
+ sound/soc/codecs/pcm512x-i2c.c | 4 ++++
+ sound/soc/codecs/pcm512x.c | 36 +++++++++++++++++++++++++++++++---
+ 2 files changed, 37 insertions(+), 3 deletions(-)
+
+--- a/sound/soc/codecs/pcm512x-i2c.c
++++ b/sound/soc/codecs/pcm512x-i2c.c
+@@ -39,6 +39,8 @@ static const struct i2c_device_id pcm512
+ { "pcm5122", },
+ { "pcm5141", },
+ { "pcm5142", },
++ { "tas5754", },
++ { "tas5756", },
+ { }
+ };
+ MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
+@@ -49,6 +51,8 @@ static const struct of_device_id pcm512x
+ { .compatible = "ti,pcm5122", },
+ { .compatible = "ti,pcm5141", },
+ { .compatible = "ti,pcm5142", },
++ { .compatible = "ti,tas5754", },
++ { .compatible = "ti,tas5756", },
+ { }
+ };
+ MODULE_DEVICE_TABLE(of, pcm512x_of_match);
+--- a/sound/soc/codecs/pcm512x.c
++++ b/sound/soc/codecs/pcm512x.c
+@@ -48,6 +48,7 @@ struct pcm512x_priv {
+ int mute;
+ struct mutex mutex;
+ unsigned int bclk_ratio;
++ int force_pll_on;
+ };
+
+ /*
+@@ -1258,10 +1259,34 @@ static int pcm512x_hw_params(struct snd_
+ return ret;
+ }
+
+- ret = regmap_update_bits(pcm512x->regmap, PCM512x_PLL_EN,
+- PCM512x_PLLE, 0);
++ if (!pcm512x->force_pll_on) {
++ ret = regmap_update_bits(pcm512x->regmap,
++ PCM512x_PLL_EN, PCM512x_PLLE, 0);
++ } else {
++ /* provide minimum PLL config for TAS575x clocking
++ * and leave PLL enabled
++ */
++ ret = regmap_write(pcm512x->regmap,
++ PCM512x_PLL_COEFF_0, 0x01);
++ if (ret != 0) {
++ dev_err(component->dev,
++ "Failed to set pll coefficient: %d\n", ret);
++ return ret;
++ }
++ ret = regmap_write(pcm512x->regmap,
++ PCM512x_PLL_COEFF_1, 0x04);
++ if (ret != 0) {
++ dev_err(component->dev,
++ "Failed to set pll coefficient: %d\n", ret);
++ return ret;
++ }
++ ret = regmap_write(pcm512x->regmap,
++ PCM512x_PLL_EN, 0x01);
++ dev_dbg(component->dev, "Enabling PLL for TAS575x\n");
++ }
++
+ if (ret != 0) {
+- dev_err(component->dev, "Failed to disable pll: %d\n", ret);
++ dev_err(component->dev, "Failed to set pll mode: %d\n", ret);
+ return ret;
+ }
+ }
+@@ -1659,6 +1684,11 @@ int pcm512x_probe(struct device *dev, st
+ ret = -EINVAL;
+ goto err_pm;
+ }
++
++ if (!strcmp(np->name, "tas5756") ||
++ !strcmp(np->name, "tas5754"))
++ pcm512x->force_pll_on = 1;
++ dev_dbg(dev, "Device ID: %s\n", np->name);
+ }
+ #endif
+
--- /dev/null
+From 0a0084b8621682ad96d001e798aa239b11a3cf00 Mon Sep 17 00:00:00 2001
+From: Timon Skerutsch <kernel@diodes-delight.com>
+Date: Mon, 13 Nov 2023 22:53:12 +0100
+Subject: [PATCH] drm/panel: add panel-dsi
+
+Equivalent to panel-dpi for configuring a simple DSI panel with
+device tree side timings and bus settings.
+Motiviation is the same as for panel-dpi of wanting to support
+new simple panels without needing to patch the kernel.
+
+Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
+---
+ drivers/gpu/drm/panel/panel-simple.c | 123 ++++++++++++++++++++++++++-
+ 1 file changed, 122 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/panel/panel-simple.c
++++ b/drivers/gpu/drm/panel/panel-simple.c
+@@ -40,6 +40,7 @@
+ #include <drm/drm_edid.h>
+ #include <drm/drm_mipi_dsi.h>
+ #include <drm/drm_panel.h>
++#include <drm/drm_of.h>
+
+ /**
+ * struct panel_desc - Describes a simple panel.
+@@ -4660,6 +4661,9 @@ static const struct panel_desc_dsi osd10
+ .lanes = 4,
+ };
+
++// for panels using generic panel-dsi binding
++static struct panel_desc_dsi panel_dsi;
++
+ static const struct of_device_id dsi_of_match[] = {
+ {
+ .compatible = "auo,b080uan01",
+@@ -4683,14 +4687,118 @@ static const struct of_device_id dsi_of_
+ .compatible = "osddisplays,osd101t2045-53ts",
+ .data = &osd101t2045_53ts
+ }, {
++ /* Must be the last entry */
++ .compatible = "panel-dsi",
++ .data = &panel_dsi,
++ }, {
+ /* sentinel */
+ }
+ };
+ MODULE_DEVICE_TABLE(of, dsi_of_match);
+
++
++/* Checks for DSI panel definition in device-tree, analog to panel_dpi */
++static int panel_dsi_dt_probe(struct device *dev,
++ struct panel_desc_dsi *desc_dsi)
++{
++ struct panel_desc *desc;
++ struct display_timing *timing;
++ const struct device_node *np;
++ const char *dsi_color_format;
++ const char *dsi_mode_flags;
++ struct property *prop;
++ int dsi_lanes, ret;
++
++ np = dev->of_node;
++
++ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
++ if (!desc)
++ return -ENOMEM;
++
++ timing = devm_kzalloc(dev, sizeof(*timing), GFP_KERNEL);
++ if (!timing)
++ return -ENOMEM;
++
++ ret = of_get_display_timing(np, "panel-timing", timing);
++ if (ret < 0) {
++ dev_err(dev, "%pOF: no panel-timing node found for \"panel-dsi\" binding\n",
++ np);
++ return ret;
++ }
++
++ desc->timings = timing;
++ desc->num_timings = 1;
++
++ of_property_read_u32(np, "width-mm", &desc->size.width);
++ of_property_read_u32(np, "height-mm", &desc->size.height);
++
++ dsi_lanes = drm_of_get_data_lanes_count_ep(np, 0, 0, 1, 4);
++
++ if (dsi_lanes < 0) {
++ dev_err(dev, "%pOF: no or too many data-lanes defined", np);
++ return dsi_lanes;
++ }
++
++ desc_dsi->lanes = dsi_lanes;
++
++ of_property_read_string(np, "dsi-color-format", &dsi_color_format);
++ if (!strcmp(dsi_color_format, "RGB888")) {
++ desc_dsi->format = MIPI_DSI_FMT_RGB888;
++ desc->bpc = 8;
++ } else if (!strcmp(dsi_color_format, "RGB565")) {
++ desc_dsi->format = MIPI_DSI_FMT_RGB565;
++ desc->bpc = 6;
++ } else if (!strcmp(dsi_color_format, "RGB666")) {
++ desc_dsi->format = MIPI_DSI_FMT_RGB666;
++ desc->bpc = 6;
++ } else if (!strcmp(dsi_color_format, "RGB666_PACKED")) {
++ desc_dsi->format = MIPI_DSI_FMT_RGB666_PACKED;
++ desc->bpc = 6;
++ } else {
++ dev_err(dev, "%pOF: no valid dsi-color-format defined", np);
++ return -EINVAL;
++ }
++
++
++ of_property_for_each_string(np, "mode", prop, dsi_mode_flags) {
++ if (!strcmp(dsi_mode_flags, "MODE_VIDEO"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_BURST"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_BURST;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_SYNC_PULSE"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_SYNC_PULSE;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_AUTO_VERT"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_AUTO_VERT;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_HSE"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_HSE;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HFP"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HFP;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HBP"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HBP;
++ else if (!strcmp(dsi_mode_flags, "MODE_VIDEO_NO_HSA"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VIDEO_NO_HSA;
++ else if (!strcmp(dsi_mode_flags, "MODE_VSYNC_FLUSH"))
++ desc_dsi->flags |= MIPI_DSI_MODE_VSYNC_FLUSH;
++ else if (!strcmp(dsi_mode_flags, "MODE_NO_EOT_PACKET"))
++ desc_dsi->flags |= MIPI_DSI_MODE_NO_EOT_PACKET;
++ else if (!strcmp(dsi_mode_flags, "CLOCK_NON_CONTINUOUS"))
++ desc_dsi->flags |= MIPI_DSI_CLOCK_NON_CONTINUOUS;
++ else if (!strcmp(dsi_mode_flags, "MODE_LPM"))
++ desc_dsi->flags |= MIPI_DSI_MODE_LPM;
++ else if (!strcmp(dsi_mode_flags, "HS_PKT_END_ALIGNED"))
++ desc_dsi->flags |= MIPI_DSI_HS_PKT_END_ALIGNED;
++ }
++
++ desc->connector_type = DRM_MODE_CONNECTOR_DSI;
++ desc_dsi->desc = *desc;
++
++ return 0;
++}
++
+ static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
+ {
+ const struct panel_desc_dsi *desc;
++ struct panel_desc_dsi *dt_desc;
+ const struct of_device_id *id;
+ int err;
+
+@@ -4698,7 +4806,20 @@ static int panel_simple_dsi_probe(struct
+ if (!id)
+ return -ENODEV;
+
+- desc = id->data;
++ if (id->data == &panel_dsi) {
++ /* Handle the generic panel-dsi binding */
++ dt_desc = devm_kzalloc(&dsi->dev, sizeof(*dt_desc), GFP_KERNEL);
++ if (!dt_desc)
++ return -ENOMEM;
++
++ err = panel_dsi_dt_probe(&dsi->dev, dt_desc);
++ if (err < 0)
++ return err;
++
++ desc = dt_desc;
++ } else {
++ desc = id->data;
++ }
+
+ err = panel_simple_probe(&dsi->dev, &desc->desc);
+ if (err < 0)
--- /dev/null
+From e14ceeadfa5b9d8a41e73edfa416bbe92dd5b20d Mon Sep 17 00:00:00 2001
+From: Timon Skerutsch <kernel@diodes-delight.com>
+Date: Mon, 13 Nov 2023 22:53:22 +0100
+Subject: [PATCH] dt-bindings: display: panel-dsi bindings
+
+Bindings for the panel-dsi specific additions to panel-simple.
+Allow for DSI specific bus settings and panel timing
+to be define in devicetree. Very similar to panel-dpi.
+
+Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
+---
+ .../bindings/display/panel/panel-dsi.yaml | 118 ++++++++++++++++++
+ 1 file changed, 118 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/display/panel/panel-dsi.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/display/panel/panel-dsi.yaml
+@@ -0,0 +1,118 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/display/panel/panel-dsi.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: Generic MIPI DSI Panel
++
++maintainers:
++ - Timon Skerutsch <kernel@diodes-delight.com>
++
++allOf:
++ - $ref: panel-common.yaml#
++
++properties:
++ compatible:
++ description:
++ Shall contain a panel specific compatible and "panel-dsi"
++ in that order.
++ items:
++ - {}
++ - const: panel-dsi
++
++ dsi-color-format:
++ description: |
++ The color format used by the panel. Only DSI supported formats are allowed.
++ enum:
++ - RGB888
++ - RGB666
++ - RGB666_PACKED
++ - RGB565
++
++ port:
++ $ref: /schemas/graph.yaml#/$defs/port-base
++ unevaluatedProperties: false
++ description:
++ Panel MIPI DSI input
++
++ properties:
++ endpoint:
++ $ref: /schemas/media/video-interfaces.yaml#
++ unevaluatedProperties: false
++
++ properties:
++ data-lanes: true
++
++ required:
++ - data-lanes
++
++ mode:
++ description: |
++ DSI mode flags. See DSI Specs for details.
++ These are driver independent features of the DSI bus.
++ items:
++ - const: MODE_VIDEO
++ - const: MODE_VIDEO_BURST
++ - const: MODE_VIDEO_SYNC_PULSE
++ - const: MODE_VIDEO_AUTO_VERT
++ - const: MODE_VIDEO_HSE
++ - const: MODE_VIDEO_NO_HFP
++ - const: MODE_VIDEO_NO_HBP
++ - const: MODE_VIDEO_NO_HSA
++ - const: MODE_VSYNC_FLUSH
++ - const: MODE_NO_EOT_PACKET
++ - const: CLOCK_NON_CONTINUOUS
++ - const: MODE_LPM
++ - const: HS_PKT_END_ALIGNED
++
++ reg: true
++ backlight: true
++ enable-gpios: true
++ width-mm: true
++ height-mm: true
++ panel-timing: true
++ power-supply: true
++ reset-gpios: true
++ ddc-i2c-bus: true
++
++required:
++ - panel-timing
++ - reg
++ - power-supply
++ - dsi-color-format
++ - port
++
++additionalProperties: false
++
++examples:
++ - |
++ panel {
++ compatible = "panel-mfgr,generic-dsi-panel","panel-dsi";
++ power-supply = <&vcc_supply>;
++ backlight = <&backlight>;
++ dsi-color-format = "RGB888";
++ reg = <0>;
++ mode = "MODE_VIDEO", "MODE_VIDEO_BURST", "MODE_NO_EOT_PACKET";
++
++ port {
++ panel_dsi_port: endpoint {
++ data-lanes = <1 2>;
++ remote-endpoint = <&dsi_out>;
++ };
++ };
++
++ panel-timing {
++ clock-frequency = <9200000>;
++ hactive = <800>;
++ vactive = <480>;
++ hfront-porch = <8>;
++ hback-porch = <4>;
++ hsync-len = <41>;
++ vback-porch = <2>;
++ vfront-porch = <4>;
++ vsync-len = <10>;
++ };
++ };
++
++...
--- /dev/null
+From ccf75f2a6f4045484c4539f7d47264f8f6b8453c Mon Sep 17 00:00:00 2001
+From: Timon Skerutsch <kernel@diodes-delight.com>
+Date: Mon, 13 Nov 2023 22:52:35 +0100
+Subject: [PATCH] overlays: example overlay for using panel-dsi on RPi
+
+Analog to the generic panel-dpi overlay to use panel-dsi with dtparam
+to not require a panel specific overlay for simple use cases that
+do not require setting more niche DSI modes.
+
+Signed-off-by: Timon Skerutsch <kernel@diodes-delight.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 31 +++++
+ .../overlays/vc4-kms-dsi-generic-overlay.dts | 106 ++++++++++++++++++
+ 3 files changed, 138 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/vc4-kms-dsi-generic-overlay.dts
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -293,6 +293,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ vc4-kms-dpi-hyperpixel4sq.dtbo \
+ vc4-kms-dpi-panel.dtbo \
+ vc4-kms-dsi-7inch.dtbo \
++ vc4-kms-dsi-generic.dtbo \
+ vc4-kms-dsi-lt070me05000.dtbo \
+ vc4-kms-dsi-lt070me05000-v2.dtbo \
+ vc4-kms-dsi-waveshare-panel.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -4785,6 +4785,37 @@ Params: sizex Touchscr
+ the default DSI1 and i2c_csi_dsi).
+
+
++Name: vc4-kms-dsi-generic
++Info: Enable a generic DSI display under KMS.
++ Default timings are for a 840x480 RGB888 panel.
++ Requires vc4-kms-v3d to be loaded.
++Load: dtoverlay=vc4-kms-dsi-generic,<param>=<val>
++Params: clock-frequency Display clock frequency (Hz)
++ hactive Horizontal active pixels
++ hfp Horizontal front porch
++ hsync Horizontal sync pulse width
++ hbp Horizontal back porch
++ vactive Vertical active lines
++ vfp Vertical front porch
++ vsync Vertical sync pulse width
++ vbp Vertical back porch
++ width-mm Define the screen width in mm
++ height-mm Define the screen height in mm
++ rgb565 Change to RGB565 output
++ rgb666 Change to RGB666 output
++ rgb666p Change to RGB666 output with pixel packing
++ rgb888 Change to RGB888 output, this is the default
++ one-lane Use one DSI lane for data transmission
++ This is the default
++ two-lane Use two DSI lanes for data transmission
++ three-lane Use three DSI lanes for data transmission
++ Only supported on Pi5 and CM
++ four-lane Use four DSI lanes for data transmission
++ Only supported on Pi5 and CM
++ dsi0 Switch DSI port to DSI0
++ Only supported on Pi5 and CM
++
++
+ Name: vc4-kms-dsi-lt070me05000
+ Info: Enable a JDI LT070ME05000 DSI display on DSI1.
+ Note that this is a 4 lane DSI device, so it will only work on a Compute
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/vc4-kms-dsi-generic-overlay.dts
+@@ -0,0 +1,106 @@
++/dts-v1/;
++/plugin/;
++
++/ {
++ compatible = "brcm,bcm2835";
++
++ dsi_frag: fragment@0 {
++ target = <&dsi1>;
++ __overlay__{
++ status = "okay";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ port {
++ dsi_out:endpoint {
++ remote-endpoint = <&panel_dsi_port>;
++ };
++ };
++ panel: panel-dsi-generic@0 {
++ // See panel-dsi.yaml binding
++ // Using dummy name for panel model
++ compatible = "Generic,panel-dsi","panel-dsi";
++ reg = <0>;
++ power-supply = <0>;
++ backlight = <0>;
++ dsi-color-format = "RGB888";
++ mode = "MODE_VIDEO";
++ width-mm = <0>;
++ height-mm = <0>;
++
++ port {
++ panel_dsi_port: endpoint {
++ data-lanes = <1>;
++ remote-endpoint = <&dsi_out>;
++ };
++ };
++
++ timing: panel-timing {
++ clock-frequency = <30000000>;
++ hactive = <840>;
++ vactive = <480>;
++ hback-porch = <44>;
++ hfront-porch = <46>;
++ hsync-len = <2>;
++ vback-porch = <18>;
++ vfront-porch = <16>;
++ vsync-len = <2>;
++ };
++ };
++ };
++ };
++
++ fragment@1 {
++ target = <&panel_dsi_port>;
++ __dormant__ {
++ data-lanes = <1>;
++ };
++ };
++
++ fragment@2 {
++ target = <&panel_dsi_port>;
++ __dormant__ {
++ data-lanes = <1 2>;
++ };
++ };
++
++ fragment@3 {
++ target = <&panel_dsi_port>;
++ __dormant__ {
++ data-lanes = <1 2 3>;
++ };
++ };
++
++ fragment@4 {
++ target = <&panel_dsi_port>;
++ __dormant__ {
++ data-lanes = <1 2 3 4>;
++ };
++ };
++
++ __overrides__ {
++ dsi0 = <&dsi_frag>, "target:0=",<&dsi0>;
++
++ clock-frequency = <&timing>, "clock-frequency:0";
++ hactive = <&timing>, "hactive:0";
++ hfp = <&timing>, "hfront-porch:0";
++ hsync = <&timing>, "hsync-len:0";
++ hbp = <&timing>, "hback-porch:0";
++ vactive = <&timing>, "vactive:0";
++ vfp = <&timing>, "vfront-porch:0";
++ vsync = <&timing>, "vsync-len:0";
++ vbp = <&timing>, "vback-porch:0";
++
++ width-mm = <&panel>, "width-mm:0";
++ height-mm = <&panel>, "height-mm:0";
++
++ rgb565 = <&panel>, "dsi-color-format=RGB565";
++ rgb666p = <&panel>, "dsi-color-format=RGB666_PACKED";
++ rgb666 = <&panel>, "dsi-color-format=RGB666";
++ rgb888 = <&panel>, "dsi-color-format=RGB888";
++ one-lane = <0>,"+1";
++ two-lane = <0>,"+2";
++ three-lane = <0>,"+3";
++ four-lane = <0>,"+4";
++ };
++
++};
--- /dev/null
+From a477a6351575aa173f9f82857f5797e384fbc704 Mon Sep 17 00:00:00 2001
+From: JinShil <slavo5150@yahoo.com>
+Date: Tue, 28 Nov 2023 17:05:44 +0900
+Subject: [PATCH] overlays: ADS1115: allow specification of the i2c bus
+
+---
+ arch/arm/boot/dts/overlays/README | 10 +++
+ .../arm/boot/dts/overlays/ads1115-overlay.dts | 80 +++++++++++++------
+ 2 files changed, 66 insertions(+), 24 deletions(-)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -489,6 +489,16 @@ Params: addr I2C bus
+ cha_gain Set the gain of the Programmable Gain
+ Amplifier for this channel. (Default 1 sets the
+ full scale of the channel to 4.096 Volts)
++ i2c0 Choose the I2C0 bus on GPIOs 0&1
++ i2c_csi_dsi Choose the 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)
+
+ Channel parameters can be set for each enabled channel.
+ A maximum of 4 channels can be enabled (letters a thru d).
+--- a/arch/arm/boot/dts/overlays/ads1115-overlay.dts
++++ b/arch/arm/boot/dts/overlays/ads1115-overlay.dts
+@@ -9,23 +9,6 @@
+ compatible = "brcm,bcm2835";
+
+ fragment@0 {
+- target = <&i2c_arm>;
+- __overlay__ {
+- #address-cells = <1>;
+- #size-cells = <0>;
+- status = "okay";
+-
+- ads1115: ads1115@48 {
+- compatible = "ti,ads1115";
+- status = "okay";
+- #address-cells = <1>;
+- #size-cells = <0>;
+- reg = <0x48>;
+- };
+- };
+- };
+-
+- fragment@1 {
+ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+@@ -39,7 +22,7 @@
+ };
+ };
+
+- fragment@2 {
++ fragment@1 {
+ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+@@ -53,7 +36,7 @@
+ };
+ };
+
+- fragment@3 {
++ fragment@2 {
+ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+@@ -67,7 +50,7 @@
+ };
+ };
+
+- fragment@4 {
++ fragment@3 {
+ target = <&ads1115>;
+ __dormant__ {
+ #address-cells = <1>;
+@@ -81,23 +64,72 @@
+ };
+ };
+
++ fragment@4 {
++ target = <&i2cbus>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ ads1115: ads1115@48 {
++ compatible = "ti,ads1115";
++ status = "okay";
++ #address-cells = <1>;
++ #size-cells = <0>;
++ reg = <0x48>;
++ };
++ };
++ };
++
++ frag100: fragment@100 {
++ target = <&i2c1>;
++ i2cbus: __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@101 {
++ target = <&i2c0if>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
++ fragment@102 {
++ target = <&i2c0mux>;
++ __dormant__ {
++ status = "okay";
++ };
++ };
++
+ __overrides__ {
+ addr = <&ads1115>,"reg:0";
+- cha_enable = <0>,"=1";
++ cha_enable = <0>,"=0";
+ cha_cfg = <&channel_a>,"reg:0";
+ cha_gain = <&channel_a>,"ti,gain:0";
+ cha_datarate = <&channel_a>,"ti,datarate:0";
+- chb_enable = <0>,"=2";
++ chb_enable = <0>,"=1";
+ chb_cfg = <&channel_b>,"reg:0";
+ chb_gain = <&channel_b>,"ti,gain:0";
+ chb_datarate = <&channel_b>,"ti,datarate:0";
+- chc_enable = <0>,"=3";
++ chc_enable = <0>,"=2";
+ chc_cfg = <&channel_c>,"reg:0";
+ chc_gain = <&channel_c>,"ti,gain:0";
+ chc_datarate = <&channel_c>,"ti,datarate:0";
+- chd_enable = <0>,"=4";
++ chd_enable = <0>,"=3";
+ chd_cfg = <&channel_d>,"reg:0";
+ chd_gain = <&channel_d>,"ti,gain:0";
+ chd_datarate = <&channel_d>,"ti,datarate:0";
++ i2c0 = <&frag100>, "target:0=",<&i2c0>;
++ i2c_csi_dsi = <&frag100>, "target:0=",<&i2c_csi_dsi>,
++ <0>,"+101+102";
++ i2c3 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c3";
++ i2c4 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c4";
++ i2c5 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c5";
++ i2c6 = <&frag100>, "target?=0",
++ <&frag100>, "target-path=i2c6";
+ };
+ };
--- /dev/null
+From 82069a7a02632aa60fa5c69415bf891ede7d6fd4 Mon Sep 17 00:00:00 2001
+From: Jonathan Bell <jonathan@raspberrypi.com>
+Date: Tue, 5 Dec 2023 16:55:17 +0000
+Subject: [PATCH] dts: bcm2712: put usb under /axi not /soc
+
+On 2712, the DWC USB controller is no longer attached to the Videocore
+30-bit bus with its associated aliases, and can see the bottom 4GB of
+RAM directly.
+
+Ideally it should make use of IOMMU6 but for now software bounce buffers
+get it working.
+
+Signed-off-by: Jonathan Bell <jonathan@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 26 +++++++++++++-------------
+ 1 file changed, 13 insertions(+), 13 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -133,19 +133,6 @@
+ status = "disabled";
+ };
+
+- usb: usb@7c480000 {
+- compatible = "brcm,bcm2835-usb";
+- reg = <0x7c480000 0x10000>;
+- interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
+- #address-cells = <1>;
+- #size-cells = <0>;
+- clocks = <&clk_usb>;
+- clock-names = "otg";
+- phys = <&usbphy>;
+- phy-names = "usb2-phy";
+- status = "disabled";
+- };
+-
+ mop: mop@7c500000 {
+ compatible = "brcm,bcm2712-mop";
+ reg = <0x7c500000 0x20>;
+@@ -1145,6 +1132,19 @@
+ reg = <0x10 0x00400018 0x0 0x18>;
+ };
+
++ usb: usb@480000 {
++ compatible = "brcm,bcm2835-usb";
++ reg = <0x10 0x00480000 0x0 0x10000>;
++ interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ clocks = <&clk_usb>;
++ clock-names = "otg";
++ phys = <&usbphy>;
++ phy-names = "usb2-phy";
++ status = "disabled";
++ };
++
+ rpivid: codec@800000 {
+ compatible = "raspberrypi,rpivid-vid-decoder";
+ reg = <0x10 0x00800000 0x0 0x10000>, /* HEVC */
--- /dev/null
+From 01139e4e9141d031c6f4f00371e5eb52fa78839e Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 5 Dec 2023 18:28:19 +0000
+Subject: [PATCH] drm/vc4: Correct HVS muxing setup for the moplet
+
+The moplet registers as VC4_ENCODER_TYPE_TXP1 and can be
+fed from mux output 2 of HVS channel 1.
+
+Correct the option which checked for VC4_ENCODER_TYPE_TXP0
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_kms.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_kms.c
++++ b/drivers/gpu/drm/vc4/vc4_kms.c
+@@ -365,7 +365,7 @@ static void vc6_hvs_pv_muxing_commit(str
+ mux = 0;
+ break;
+
+- case VC4_ENCODER_TYPE_TXP0:
++ case VC4_ENCODER_TYPE_TXP1:
+ mux = 2;
+ break;
+
--- /dev/null
+From cc948130d3e1c70ef21ae9963b56e0d500cef70b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 5 Dec 2023 18:29:34 +0000
+Subject: [PATCH] drm/vc4: Mop and moplet have different register offsets for
+ high addr
+
+MOP uses register offset 0x24 for the high bits of the address,
+whilst Moplet uses 0x1c.
+
+Handle this difference between the block types.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_drv.h | 1 +
+ drivers/gpu/drm/vc4/vc4_txp.c | 8 ++++++--
+ 2 files changed, 7 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_drv.h
++++ b/drivers/gpu/drm/vc4/vc4_drv.h
+@@ -564,6 +564,7 @@ struct vc4_crtc_data {
+ struct vc4_txp_data {
+ struct vc4_crtc_data base;
+ enum vc4_encoder_type encoder_type;
++ unsigned int high_addr_ptr_reg;
+ unsigned int has_byte_enable:1;
+ unsigned int size_minus_one:1;
+ unsigned int supports_40bit_addresses:1;
+--- a/drivers/gpu/drm/vc4/vc4_txp.c
++++ b/drivers/gpu/drm/vc4/vc4_txp.c
+@@ -145,7 +145,8 @@
+ /* Number of lines received and committed to memory. */
+ #define TXP_PROGRESS 0x10
+
+-#define TXP_DST_PTR_HIGH 0x1c
++#define TXP_DST_PTR_HIGH_MOPLET 0x1c
++#define TXP_DST_PTR_HIGH_MOP 0x24
+
+ #define TXP_READ(offset) \
+ ({ \
+@@ -338,10 +339,11 @@ static void vc4_txp_connector_atomic_com
+
+ gem = drm_fb_dma_get_gem_obj(fb, 0);
+ addr = gem->dma_addr + fb->offsets[0];
++
+ TXP_WRITE(TXP_DST_PTR, lower_32_bits(addr));
+
+ if (txp_data->supports_40bit_addresses)
+- TXP_WRITE(TXP_DST_PTR_HIGH, upper_32_bits(addr) & 0xff);
++ TXP_WRITE(txp_data->high_addr_ptr_reg, upper_32_bits(addr) & 0xff);
+
+ TXP_WRITE(TXP_DST_PITCH, fb->pitches[0]);
+
+@@ -520,6 +522,7 @@ const struct vc4_txp_data bcm2712_mop_da
+ .hvs_output = 2,
+ },
+ .encoder_type = VC4_ENCODER_TYPE_TXP0,
++ .high_addr_ptr_reg = TXP_DST_PTR_HIGH_MOP,
+ .has_byte_enable = true,
+ .size_minus_one = true,
+ .supports_40bit_addresses = true,
+@@ -533,6 +536,7 @@ const struct vc4_txp_data bcm2712_moplet
+ .hvs_output = 4,
+ },
+ .encoder_type = VC4_ENCODER_TYPE_TXP1,
++ .high_addr_ptr_reg = TXP_DST_PTR_HIGH_MOPLET,
+ .size_minus_one = true,
+ .supports_40bit_addresses = true,
+ };
--- /dev/null
+From 36593e2e27769d635ef18301f25b5e219a23949a Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Tue, 5 Dec 2023 18:31:25 +0000
+Subject: [PATCH] arm: dt: bcm2712: Correct the size of the register range for
+ MOP
+
+The Mop covers 0x28 bytes of registers, so ensure the range is
+defined appropriately.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712.dtsi | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712.dtsi
++++ b/arch/arm/boot/dts/bcm2712.dtsi
+@@ -135,7 +135,7 @@
+
+ mop: mop@7c500000 {
+ compatible = "brcm,bcm2712-mop";
+- reg = <0x7c500000 0x20>;
++ reg = <0x7c500000 0x28>;
+ interrupt-parent = <&disp_intr>;
+ interrupts = <1>;
+ status = "disabled";
--- /dev/null
+From ae0e1b70f675f6ac7966e427f0d8f57812dbc312 Mon Sep 17 00:00:00 2001
+From: Hans de Goede <hdegoede@redhat.com>
+Date: Tue, 27 Jun 2023 14:51:04 +0200
+Subject: [PATCH] media: Add MIPI CCI register access helper functions
+
+The CSI2 specification specifies a standard method to access camera sensor
+registers called "Camera Control Interface (CCI)".
+
+This uses either 8 or 16 bit (big-endian wire order) register addresses
+and supports 8, 16, 24 or 32 bit (big-endian wire order) register widths.
+
+Currently a lot of Linux camera sensor drivers all have their own custom
+helpers for this, often copy and pasted from other drivers.
+
+Add a set of generic helpers for this so that all sensor drivers can
+switch to a single common implementation.
+
+These helpers take an extra optional "int *err" function parameter,
+this can be used to chain a bunch of register accesses together with
+only a single error check at the end, rather than needing to error
+check each individual register access. The first failing call will
+set the contents of err to a non 0 value and all other calls will
+then become no-ops.
+
+Link: https://lore.kernel.org/linux-media/59aefa7f-7bf9-6736-6040-39551329cd0a@redhat.com/
+
+Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
+Tested-by: Tommaso Merciai <tomm.merciai@gmail.com>
+Reviewed-by: Tommaso Merciai <tomm.merciai@gmail.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
+(cherry picked from commit 613cbb91e9cee7cf5a61f0816d2acab7bc117407)
+---
+ Documentation/driver-api/media/v4l2-cci.rst | 5 +
+ Documentation/driver-api/media/v4l2-core.rst | 1 +
+ drivers/media/v4l2-core/Kconfig | 9 +
+ drivers/media/v4l2-core/Makefile | 1 +
+ drivers/media/v4l2-core/v4l2-cci.c | 166 +++++++++++++++++++
+ include/media/v4l2-cci.h | 125 ++++++++++++++
+ 6 files changed, 307 insertions(+)
+ create mode 100644 Documentation/driver-api/media/v4l2-cci.rst
+ create mode 100644 drivers/media/v4l2-core/v4l2-cci.c
+ create mode 100644 include/media/v4l2-cci.h
+
+--- /dev/null
++++ b/Documentation/driver-api/media/v4l2-cci.rst
+@@ -0,0 +1,5 @@
++.. SPDX-License-Identifier: GPL-2.0
++
++V4L2 CCI kAPI
++^^^^^^^^^^^^^
++.. kernel-doc:: include/media/v4l2-cci.h
+--- a/Documentation/driver-api/media/v4l2-core.rst
++++ b/Documentation/driver-api/media/v4l2-core.rst
+@@ -22,6 +22,7 @@ Video4Linux devices
+ v4l2-mem2mem
+ v4l2-async
+ v4l2-fwnode
++ v4l2-cci
+ v4l2-rect
+ v4l2-tuner
+ v4l2-common
+--- a/drivers/media/v4l2-core/Kconfig
++++ b/drivers/media/v4l2-core/Kconfig
+@@ -74,6 +74,15 @@ config V4L2_FWNODE
+ config V4L2_ASYNC
+ tristate
+
++config V4L2_CCI
++ tristate
++
++config V4L2_CCI_I2C
++ tristate
++ depends on I2C
++ select REGMAP_I2C
++ select V4L2_CCI
++
+ # Used by drivers that need Videobuf modules
+ config VIDEOBUF_GEN
+ tristate
+--- a/drivers/media/v4l2-core/Makefile
++++ b/drivers/media/v4l2-core/Makefile
+@@ -25,6 +25,7 @@ videodev-$(CONFIG_VIDEO_V4L2_I2C) += v4l
+ # (e. g. LC_ALL=C sort Makefile)
+
+ obj-$(CONFIG_V4L2_ASYNC) += v4l2-async.o
++obj-$(CONFIG_V4L2_CCI) += v4l2-cci.o
+ obj-$(CONFIG_V4L2_FLASH_LED_CLASS) += v4l2-flash-led-class.o
+ obj-$(CONFIG_V4L2_FWNODE) += v4l2-fwnode.o
+ obj-$(CONFIG_V4L2_H264) += v4l2-h264.o
+--- /dev/null
++++ b/drivers/media/v4l2-core/v4l2-cci.c
+@@ -0,0 +1,166 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * MIPI Camera Control Interface (CCI) register access helpers.
++ *
++ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
++ */
++
++#include <linux/bitfield.h>
++#include <linux/delay.h>
++#include <linux/dev_printk.h>
++#include <linux/module.h>
++#include <linux/regmap.h>
++#include <linux/types.h>
++
++#include <asm/unaligned.h>
++
++#include <media/v4l2-cci.h>
++
++int cci_read(struct regmap *map, u32 reg, u64 *val, int *err)
++{
++ unsigned int len;
++ u8 buf[8];
++ int ret;
++
++ if (err && *err)
++ return *err;
++
++ len = FIELD_GET(CCI_REG_WIDTH_MASK, reg);
++ reg = FIELD_GET(CCI_REG_ADDR_MASK, reg);
++
++ ret = regmap_bulk_read(map, reg, buf, len);
++ if (ret) {
++ dev_err(regmap_get_device(map), "Error reading reg 0x%4x: %d\n",
++ reg, ret);
++ goto out;
++ }
++
++ switch (len) {
++ case 1:
++ *val = buf[0];
++ break;
++ case 2:
++ *val = get_unaligned_be16(buf);
++ break;
++ case 3:
++ *val = get_unaligned_be24(buf);
++ break;
++ case 4:
++ *val = get_unaligned_be32(buf);
++ break;
++ case 8:
++ *val = get_unaligned_be64(buf);
++ break;
++ default:
++ dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
++ len, reg);
++ ret = -EINVAL;
++ break;
++ }
++
++out:
++ if (ret && err)
++ *err = ret;
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(cci_read);
++
++int cci_write(struct regmap *map, u32 reg, u64 val, int *err)
++{
++ unsigned int len;
++ u8 buf[8];
++ int ret;
++
++ if (err && *err)
++ return *err;
++
++ len = FIELD_GET(CCI_REG_WIDTH_MASK, reg);
++ reg = FIELD_GET(CCI_REG_ADDR_MASK, reg);
++
++ switch (len) {
++ case 1:
++ buf[0] = val;
++ break;
++ case 2:
++ put_unaligned_be16(val, buf);
++ break;
++ case 3:
++ put_unaligned_be24(val, buf);
++ break;
++ case 4:
++ put_unaligned_be32(val, buf);
++ break;
++ case 8:
++ put_unaligned_be64(val, buf);
++ break;
++ default:
++ dev_err(regmap_get_device(map), "Error invalid reg-width %u for reg 0x%04x\n",
++ len, reg);
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ret = regmap_bulk_write(map, reg, buf, len);
++ if (ret)
++ dev_err(regmap_get_device(map), "Error writing reg 0x%4x: %d\n",
++ reg, ret);
++
++out:
++ if (ret && err)
++ *err = ret;
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(cci_write);
++
++int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err)
++{
++ u64 readval;
++ int ret;
++
++ ret = cci_read(map, reg, &readval, err);
++ if (ret)
++ return ret;
++
++ val = (readval & ~mask) | (val & mask);
++
++ return cci_write(map, reg, val, err);
++}
++EXPORT_SYMBOL_GPL(cci_update_bits);
++
++int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
++ unsigned int num_regs, int *err)
++{
++ unsigned int i;
++ int ret;
++
++ for (i = 0; i < num_regs; i++) {
++ ret = cci_write(map, regs[i].reg, regs[i].val, err);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cci_multi_reg_write);
++
++#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
++struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
++ int reg_addr_bits)
++{
++ struct regmap_config config = {
++ .reg_bits = reg_addr_bits,
++ .val_bits = 8,
++ .reg_format_endian = REGMAP_ENDIAN_BIG,
++ .disable_locking = true,
++ };
++
++ return devm_regmap_init_i2c(client, &config);
++}
++EXPORT_SYMBOL_GPL(devm_cci_regmap_init_i2c);
++#endif
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Hans de Goede <hansg@kernel.org>");
++MODULE_DESCRIPTION("MIPI Camera Control Interface (CCI) support");
+--- /dev/null
++++ b/include/media/v4l2-cci.h
+@@ -0,0 +1,125 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++/*
++ * MIPI Camera Control Interface (CCI) register access helpers.
++ *
++ * Copyright (C) 2023 Hans de Goede <hansg@kernel.org>
++ */
++#ifndef _V4L2_CCI_H
++#define _V4L2_CCI_H
++
++#include <linux/types.h>
++
++struct i2c_client;
++struct regmap;
++
++/**
++ * struct cci_reg_sequence - An individual write from a sequence of CCI writes
++ *
++ * @reg: Register address, use CCI_REG#() macros to encode reg width
++ * @val: Register value
++ *
++ * Register/value pairs for sequences of writes.
++ */
++struct cci_reg_sequence {
++ u32 reg;
++ u64 val;
++};
++
++/*
++ * Macros to define register address with the register width encoded
++ * into the higher bits.
++ */
++#define CCI_REG_ADDR_MASK GENMASK(15, 0)
++#define CCI_REG_WIDTH_SHIFT 16
++#define CCI_REG_WIDTH_MASK GENMASK(19, 16)
++
++#define CCI_REG8(x) ((1 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG16(x) ((2 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG24(x) ((3 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG32(x) ((4 << CCI_REG_WIDTH_SHIFT) | (x))
++#define CCI_REG64(x) ((8 << CCI_REG_WIDTH_SHIFT) | (x))
++
++/**
++ * cci_read() - Read a value from a single CCI register
++ *
++ * @map: Register map to read from
++ * @reg: Register address to read, use CCI_REG#() macros to encode reg width
++ * @val: Pointer to store read value
++ * @err: Optional pointer to store errors, if a previous error is set
++ * then the read will be skipped
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_read(struct regmap *map, u32 reg, u64 *val, int *err);
++
++/**
++ * cci_write() - Write a value to a single CCI register
++ *
++ * @map: Register map to write to
++ * @reg: Register address to write, use CCI_REG#() macros to encode reg width
++ * @val: Value to be written
++ * @err: Optional pointer to store errors, if a previous error is set
++ * then the write will be skipped
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_write(struct regmap *map, u32 reg, u64 val, int *err);
++
++/**
++ * cci_update_bits() - Perform a read/modify/write cycle on
++ * a single CCI register
++ *
++ * @map: Register map to update
++ * @reg: Register address to update, use CCI_REG#() macros to encode reg width
++ * @mask: Bitmask to change
++ * @val: New value for bitmask
++ * @err: Optional pointer to store errors, if a previous error is set
++ * then the update will be skipped
++ *
++ * Note this uses read-modify-write to update the bits, atomicity with regards
++ * to other cci_*() register access functions is NOT guaranteed.
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_update_bits(struct regmap *map, u32 reg, u64 mask, u64 val, int *err);
++
++/**
++ * cci_multi_reg_write() - Write multiple registers to the device
++ *
++ * @map: Register map to write to
++ * @regs: Array of structures containing register-address, -value pairs to be
++ * written, register-addresses use CCI_REG#() macros to encode reg width
++ * @num_regs: Number of registers to write
++ * @err: Optional pointer to store errors, if a previous error is set
++ * then the write will be skipped
++ *
++ * Write multiple registers to the device where the set of register, value
++ * pairs are supplied in any order, possibly not all in a single range.
++ *
++ * Use of the CCI_REG#() macros to encode reg width is mandatory.
++ *
++ * For raw lists of register-address, -value pairs with only 8 bit
++ * wide writes regmap_multi_reg_write() can be used instead.
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++int cci_multi_reg_write(struct regmap *map, const struct cci_reg_sequence *regs,
++ unsigned int num_regs, int *err);
++
++#if IS_ENABLED(CONFIG_V4L2_CCI_I2C)
++/**
++ * devm_cci_regmap_init_i2c() - Create regmap to use with cci_*() register
++ * access functions
++ *
++ * @client: i2c_client to create the regmap for
++ * @reg_addr_bits: register address width to use (8 or 16)
++ *
++ * Note the memory for the created regmap is devm() managed, tied to the client.
++ *
++ * Return: %0 on success or a negative error code on failure.
++ */
++struct regmap *devm_cci_regmap_init_i2c(struct i2c_client *client,
++ int reg_addr_bits);
++#endif
++
++#endif
--- /dev/null
+From 3d108604ca669b83bb4918c4f5f0a02ddef84972 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Date: Sun, 1 Oct 2023 13:20:12 +0200
+Subject: [PATCH] media: dt-bindings: Add OmniVision OV64A40
+
+Add bindings for OmniVision OV64A40.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ .../bindings/media/i2c/ovti,ov64a40.yaml | 98 +++++++++++++++++++
+ 1 file changed, 98 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
+@@ -0,0 +1,98 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/ovti,ov64a40.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: OmniVision OV64A40 Image Sensor
++
++maintainers:
++ - Jacopo Mondi <jacopo.mondi@ideasonboard.com>
++
++allOf:
++ - $ref: /schemas/media/video-interface-devices.yaml#
++
++properties:
++ compatible:
++ const: ovti,ov64a40
++
++ reg:
++ maxItems: 1
++
++ clocks:
++ maxItems: 1
++
++ avdd-supply:
++ description: Analog voltage supply, 2.8 volts
++
++ dvdd-supply:
++ description: Digital core voltage supply, 1.1 volts
++
++ dovdd-supply:
++ description: Digital I/O voltage supply, 1.8 volts
++
++ powerdown-gpios:
++ maxItems: 1
++
++ reset-gpios:
++ maxItems: 1
++
++ port:
++ $ref: /schemas/graph.yaml#/$defs/port-base
++ additionalProperties: false
++
++ properties:
++ endpoint:
++ $ref: /schemas/media/video-interfaces.yaml#
++ additionalProperties: false
++
++ properties:
++ bus-type:
++ enum:
++ - 1 # MIPI CSI-2 C-PHY
++ - 4 # MIPI CSI-2 D-PHY
++ data-lanes: true
++ link-frequencies: true
++ clock-noncontinuous: true
++ remote-endpoint: true
++
++required:
++ - compatible
++ - reg
++ - clocks
++ - port
++
++unevaluatedProperties: false
++
++examples:
++ - |
++ #include <dt-bindings/gpio/gpio.h>
++
++ i2c {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ camera@36 {
++ compatible = "ovti,ov64a40";
++ reg = <0x36>;
++ clocks = <&camera_clk>;
++ dovdd-supply = <&vgen4_reg>;
++ avdd-supply = <&vgen3_reg>;
++ dvdd-supply = <&vgen2_reg>;
++ powerdown-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
++ reset-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>;
++ rotation = <180>;
++ orientation = <2>;
++
++ port {
++ endpoint {
++ remote-endpoint = <&mipi_csi2_in>;
++ bus-type = <4>;
++ data-lanes = <1 2 3 4>;
++ link-frequencies = /bits/ 64 <456000000>;
++ };
++ };
++ };
++ };
++
++...
--- /dev/null
+From 31c2999e543c245f7b96af3e73cd18e1036bfe7b Mon Sep 17 00:00:00 2001
+From: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Date: Thu, 14 Sep 2023 17:03:24 +0100
+Subject: [PATCH] media: dt-bindings: i2c: Add Rohm BU64754 bindings
+
+Add YAML device tree bindings for the ROHM BU64754 VCM Motor Driver for
+Camera Autofocus.
+
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ .../bindings/media/i2c/rohm,bu64754.yaml | 48 +++++++++++++++++++
+ MAINTAINERS | 7 +++
+ 2 files changed, 55 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/media/i2c/rohm,bu64754.yaml
+
+--- /dev/null
++++ b/Documentation/devicetree/bindings/media/i2c/rohm,bu64754.yaml
+@@ -0,0 +1,48 @@
++# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
++# Copyright (C) 2023 Ideas on Board Oy.
++%YAML 1.2
++---
++$id: http://devicetree.org/schemas/media/i2c/rohm,bu64754.yaml#
++$schema: http://devicetree.org/meta-schemas/core.yaml#
++
++title: ROHM BU64754 Actuator Driver for Camera Autofocus
++
++maintainers:
++ - Kieran Bingham <kieran.bingham@ideasonboard.com>
++
++description: |
++ The BU64754GWZ is an actuator driver IC which can control the actuator
++ position precisely using an internal Hall Sensor.
++
++properties:
++ compatible:
++ items:
++ - enum:
++ - rohm,bu64754
++
++ reg:
++ maxItems: 1
++
++ vdd-supply:
++ description:
++ Definition of the regulator used as VDD power supply to the driver.
++
++required:
++ - compatible
++ - reg
++
++additionalProperties: false
++
++examples:
++ - |
++ i2c {
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ lens@76 {
++ compatible = "rohm,bu64754";
++ reg = <0x76>;
++ vdd-supply = <&cam1_reg>;
++ };
++ };
++...
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -17895,6 +17895,13 @@ S: Maintained
+ F: Documentation/devicetree/bindings/iio/light/bh1750.yaml
+ F: drivers/iio/light/bh1750.c
+
++ROHM BU64754 MOTOR DRIVER FOR CAMERA AUTOFOCUS
++M: Kieran Bingham <kieran.bingham@ideasonboard.com>
++L: linux-media@vger.kernel.org
++S: Maintained
++T: git git://linuxtv.org/media_tree.git
++F: Documentation/devicetree/bindings/media/i2c/rohm,bu64754.yaml
++
+ ROHM MULTIFUNCTION BD9571MWV-M PMIC DEVICE DRIVERS
+ M: Marek Vasut <marek.vasut+renesas@gmail.com>
+ L: linux-kernel@vger.kernel.org
--- /dev/null
+From 87e3fcaad3017bdc91a7a79d2d1c874422ef87b0 Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Date: Thu, 20 Jul 2023 11:44:40 +0200
+Subject: [PATCH] media: i2c: Add driver for OmniVision OV64A40
+
+Add a driver for the OmniVision OV64A40 image sensor.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ MAINTAINERS | 8 +
+ drivers/media/i2c/Kconfig | 14 +
+ drivers/media/i2c/Makefile | 1 +
+ drivers/media/i2c/ov64a40.c | 3694 +++++++++++++++++++++++++++++++++++
+ 4 files changed, 3717 insertions(+)
+ create mode 100644 drivers/media/i2c/ov64a40.c
+
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15321,6 +15321,14 @@ S: Maintained
+ T: git git://linuxtv.org/media_tree.git
+ F: drivers/media/i2c/ov5695.c
+
++OMNIVISION OV64A40 SENSOR DRIVER
++M: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
++L: linux-media@vger.kernel.org
++S: Maintained
++T: git git://linuxtv.org/media_tree.git
++F: Documentation/devicetree/bindings/media/i2c/ovti,ov64a40.yaml
++F: drivers/media/i2c/ov64a40.c
++
+ OMNIVISION OV7670 SENSOR DRIVER
+ L: linux-media@vger.kernel.org
+ S: Orphan
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -631,6 +631,20 @@ config VIDEO_OV5695
+ To compile this driver as a module, choose M here: the
+ module will be called ov5695.
+
++config VIDEO_OV64A40
++ tristate "OmniVision OV64A40 sensor support"
++ depends on I2C && VIDEO_DEV
++ select MEDIA_CONTROLLER
++ select VIDEO_V4L2_SUBDEV_API
++ select V4L2_FWNODE
++ select V4L2_CCI_I2C
++ help
++ This is a Video4Linux2 sensor driver for the OmniVision
++ OV64A40 camera.
++
++ To compile this driver as a module, choose M here: the
++ module will be called ov64a40.
++
+ config VIDEO_OV6650
+ tristate "OmniVision OV6650 sensor support"
+ depends on I2C && VIDEO_DEV
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -96,6 +96,7 @@ obj-$(CONFIG_VIDEO_OV5670) += ov5670.o
+ obj-$(CONFIG_VIDEO_OV5675) += ov5675.o
+ obj-$(CONFIG_VIDEO_OV5693) += ov5693.o
+ obj-$(CONFIG_VIDEO_OV5695) += ov5695.o
++obj-$(CONFIG_VIDEO_OV64A40) += ov64a40.o
+ obj-$(CONFIG_VIDEO_OV6650) += ov6650.o
+ obj-$(CONFIG_VIDEO_OV7251) += ov7251.o
+ obj-$(CONFIG_VIDEO_OV7640) += ov7640.o
+--- /dev/null
++++ b/drivers/media/i2c/ov64a40.c
+@@ -0,0 +1,3694 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * V4L2 sensor driver for OmniVision OV64A40
++ *
++ * Copyright (C) 2023 Ideas On Board Oy
++ * Copyright (C) 2023 Arducam
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio/consumer.h>
++#include <linux/i2c.h>
++#include <linux/mod_devicetable.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++
++#include <media/v4l2-cci.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-event.h>
++#include <media/v4l2-fwnode.h>
++#include <media/v4l2-mediabus.h>
++#include <media/v4l2-subdev.h>
++
++#define OV64A40_XCLK_FREQ 24000000
++
++#define OV64A40_NATIVE_WIDTH 9286
++#define OV64A40_NATIVE_HEIGHT 6976
++#define OV64A40_PIXEL_ARRAY_TOP 0
++#define OV64A40_PIXEL_ARRAY_LEFT 0
++#define OV64A40_PIXEL_ARRAY_WIDTH 9248
++#define OV64A40_PIXEL_ARRAY_HEIGHT 6944
++
++#define OV64A40_PIXEL_RATE 300000000
++
++#define OV64A40_LINK_FREQ_360M 360000000
++#define OV64A40_LINK_FREQ_456M 456000000
++
++#define OV64A40_PLL1_PRE_DIV0 CCI_REG8(0x0301)
++#define OV64A40_PLL1_PRE_DIV CCI_REG8(0x0303)
++#define OV64A40_PLL1_MULTIPLIER CCI_REG16(0x0304)
++#define OV64A40_PLL1_M_DIV CCI_REG8(0x0307)
++#define OV64A40_PLL2_SEL_BAK_SA1 CCI_REG8(0x0320)
++#define OV64A40_PLL2_PRE_DIV CCI_REG8(0x0323)
++#define OV64A40_PLL2_MULTIPLIER CCI_REG16(0x0324)
++#define OV64A40_PLL2_PRE_DIV0 CCI_REG8(0x0326)
++#define OV64A40_PLL2_DIVDAC CCI_REG8(0x0329)
++#define OV64A40_PLL2_DIVSP CCI_REG8(0x032d)
++#define OV64A40_PLL2_DACPREDIV CCI_REG8(0x032e)
++
++/* TODO: validate vblank_min, it's not characterized in the datasheet. */
++#define OV64A40_VBLANK_MIN 128
++#define OV64A40_VTS_MAX 0xffffff
++
++#define OV64A40_REG_MEC_LONG_EXPO CCI_REG24(0x3500)
++#define OV64A40_EXPOSURE_MIN 16
++#define OV64A40_EXPOSURE_MARGIN 32
++
++#define OV64A40_REG_MEC_LONG_GAIN CCI_REG16(0x3508)
++#define OV64A40_ANA_GAIN_MIN 0x80
++#define OV64A40_ANA_GAIN_MAX 0x7ff
++#define OV64A40_ANA_GAIN_DEFAULT 0x80
++
++#define OV64A40_REG_TIMING_CTRL0 CCI_REG16(0x3800)
++#define OV64A40_REG_TIMING_CTRL2 CCI_REG16(0x3802)
++#define OV64A40_REG_TIMING_CTRL4 CCI_REG16(0x3804)
++#define OV64A40_REG_TIMING_CTRL6 CCI_REG16(0x3806)
++#define OV64A40_REG_TIMING_CTRL8 CCI_REG16(0x3808)
++#define OV64A40_REG_TIMING_CTRLA CCI_REG16(0x380a)
++#define OV64A40_REG_TIMING_CTRLC CCI_REG16(0x380c)
++#define OV64A40_REG_TIMING_CTRLE CCI_REG16(0x380e)
++#define OV64A40_REG_TIMING_CTRL10 CCI_REG16(0x3810)
++#define OV64A40_REG_TIMING_CTRL12 CCI_REG16(0x3812)
++
++/*
++ * Careful: a typo in the datasheet calls this register
++ * OV64A40_REG_TIMING_CTRL20.
++ */
++#define OV64A40_REG_TIMING_CTRL14 CCI_REG8(0x3814)
++#define OV64A40_REG_TIMING_CTRL15 CCI_REG8(0x3815)
++#define OV64A40_ODD_INC_SHIFT 4
++#define OV64A40_SKIPPING_CONFIG(_odd, _even) \
++ (((_odd) << OV64A40_ODD_INC_SHIFT) | (_even))
++
++#define OV64A40_REG_TIMING_CTRL_20 CCI_REG8(0x3820)
++#define OV64A40_TIMING_CTRL_20_VFLIP BIT(2)
++#define OV64A40_TIMING_CTRL_20_VBIN BIT(1)
++
++#define OV64A40_REG_TIMING_CTRL_21 CCI_REG8(0x3821)
++#define OV64A40_TIMING_CTRL_21_HBIN BIT(4)
++#define OV64A40_TIMING_CTRL_21_HFLIP BIT(2)
++#define OV64A40_TIMING_CTRL_21_DSPEED BIT(0)
++#define OV64A40_TIMING_CTRL_21_HBIN_CONF \
++ (OV64A40_TIMING_CTRL_21_HBIN | \
++ OV64A40_TIMING_CTRL_21_DSPEED)
++
++#define OV64A40_REG_TIMINGS_VTS_HIGH CCI_REG8(0x3840)
++#define OV64A40_REG_TIMINGS_VTS_MID CCI_REG8(0x380e)
++#define OV64A40_REG_TIMINGS_VTS_LOW CCI_REG8(0x380f)
++
++/* The test pattern control is weirdly named PRE_ISP_2325_D2V2_TOP_1 in TRM. */
++#define OV64A40_REG_TEST_PATTERN CCI_REG8(0x50c1)
++#define OV64A40_TEST_PATTERN_DISABLED 0x00
++#define OV64A40_TEST_PATTERN_TYPE1 BIT(0)
++#define OV64A40_TEST_PATTERN_TYPE2 (BIT(4) | BIT(0))
++#define OV64A40_TEST_PATTERN_TYPE3 (BIT(5) | BIT(0))
++#define OV64A40_TEST_PATTERN_TYPE4 (BIT(5) | BIT(4) | BIT(0))
++
++#define OV64A40_REG_CHIP_ID CCI_REG24(0x300a)
++#define OV64A40_CHIP_ID 0x566441
++
++#define OV64A40_REG_SMIA CCI_REG8(0x0100)
++#define OV64A40_REG_SMIA_STREAMING BIT(0)
++
++enum ov64a40_link_freq_ids {
++ OV64A40_LINK_FREQ_456M_ID,
++ OV64A40_LINK_FREQ_360M_ID,
++ OV64A40_NUM_LINK_FREQ,
++};
++
++static const char * const ov64a40_supply_names[] = {
++ /* Supplies can be enabled in any order */
++ "avdd", /* Analog (2.8V) supply */
++ "dovdd", /* Digital Core (1.8V) supply */
++ "dvdd", /* IF (1.1V) supply */
++};
++
++static const char * const ov64a40_test_pattern_menu[] = {
++ "Disabled",
++ "Type1",
++ "Type2",
++ "Type3",
++ "Type4",
++};
++
++static const int ov64a40_test_pattern_val[] = {
++ OV64A40_TEST_PATTERN_DISABLED,
++ OV64A40_TEST_PATTERN_TYPE1,
++ OV64A40_TEST_PATTERN_TYPE2,
++ OV64A40_TEST_PATTERN_TYPE3,
++ OV64A40_TEST_PATTERN_TYPE4,
++};
++
++static const unsigned int ov64a40_mbus_codes[] = {
++ MEDIA_BUS_FMT_SBGGR10_1X10,
++ MEDIA_BUS_FMT_SGRBG10_1X10,
++ MEDIA_BUS_FMT_SGBRG10_1X10,
++ MEDIA_BUS_FMT_SRGGB10_1X10,
++};
++
++static const struct cci_reg_sequence ov64a40_init[] = {
++ { CCI_REG8(0x0103), 0x01 }, { CCI_REG8(0x0301), 0x88 },
++ { CCI_REG8(0x0304), 0x00 }, { CCI_REG8(0x0305), 0x96 },
++ { CCI_REG8(0x0306), 0x03 }, { CCI_REG8(0x0307), 0x00 },
++ { CCI_REG8(0x0345), 0x2c }, { CCI_REG8(0x034a), 0x02 },
++ { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x0350), 0xc0 },
++ { CCI_REG8(0x0360), 0x09 }, { CCI_REG8(0x3012), 0x31 },
++ { CCI_REG8(0x3015), 0xf0 }, { CCI_REG8(0x3017), 0xf0 },
++ { CCI_REG8(0x301d), 0xf6 }, { CCI_REG8(0x301e), 0xf1 },
++ { CCI_REG8(0x3022), 0xf0 }, { CCI_REG8(0x3400), 0x08 },
++ { CCI_REG8(0x3608), 0x41 }, { CCI_REG8(0x3421), 0x02 },
++ { CCI_REG8(0x3500), 0x00 }, { CCI_REG8(0x3501), 0x00 },
++ { CCI_REG8(0x3502), 0x18 }, { CCI_REG8(0x3504), 0x0c },
++ { CCI_REG8(0x3508), 0x01 }, { CCI_REG8(0x3509), 0x00 },
++ { CCI_REG8(0x350a), 0x01 }, { CCI_REG8(0x350b), 0x00 },
++ { CCI_REG8(0x350b), 0x00 }, { CCI_REG8(0x3540), 0x00 },
++ { CCI_REG8(0x3541), 0x00 }, { CCI_REG8(0x3542), 0x08 },
++ { CCI_REG8(0x3548), 0x01 }, { CCI_REG8(0x3549), 0xa0 },
++ { CCI_REG8(0x3549), 0x00 }, { CCI_REG8(0x3549), 0x00 },
++ { CCI_REG8(0x3549), 0x00 }, { CCI_REG8(0x3580), 0x00 },
++ { CCI_REG8(0x3581), 0x00 }, { CCI_REG8(0x3582), 0x04 },
++ { CCI_REG8(0x3588), 0x01 }, { CCI_REG8(0x3589), 0xf0 },
++ { CCI_REG8(0x3589), 0x00 }, { CCI_REG8(0x3589), 0x00 },
++ { CCI_REG8(0x3589), 0x00 }, { CCI_REG8(0x360d), 0x83 },
++ { CCI_REG8(0x3616), 0xa0 }, { CCI_REG8(0x3617), 0x31 },
++ { CCI_REG8(0x3623), 0x10 }, { CCI_REG8(0x3633), 0x03 },
++ { CCI_REG8(0x3634), 0x03 }, { CCI_REG8(0x3635), 0x77 },
++ { CCI_REG8(0x3640), 0x19 }, { CCI_REG8(0x3641), 0x80 },
++ { CCI_REG8(0x364d), 0x0f }, { CCI_REG8(0x3680), 0x80 },
++ { CCI_REG8(0x3682), 0x00 }, { CCI_REG8(0x3683), 0x00 },
++ { CCI_REG8(0x3684), 0x07 }, { CCI_REG8(0x3688), 0x01 },
++ { CCI_REG8(0x3689), 0x08 }, { CCI_REG8(0x368a), 0x26 },
++ { CCI_REG8(0x368b), 0xc8 }, { CCI_REG8(0x368e), 0x70 },
++ { CCI_REG8(0x368f), 0x00 }, { CCI_REG8(0x3692), 0x04 },
++ { CCI_REG8(0x3693), 0x00 }, { CCI_REG8(0x3696), 0xd1 },
++ { CCI_REG8(0x3697), 0xe0 }, { CCI_REG8(0x3698), 0x80 },
++ { CCI_REG8(0x3699), 0x2b }, { CCI_REG8(0x369a), 0x00 },
++ { CCI_REG8(0x369d), 0x00 }, { CCI_REG8(0x369e), 0x14 },
++ { CCI_REG8(0x369f), 0x20 }, { CCI_REG8(0x36a5), 0x80 },
++ { CCI_REG8(0x36a6), 0x00 }, { CCI_REG8(0x36a7), 0x00 },
++ { CCI_REG8(0x36a8), 0x00 }, { CCI_REG8(0x36b5), 0x17 },
++ { CCI_REG8(0x3701), 0x30 }, { CCI_REG8(0x3706), 0x2b },
++ { CCI_REG8(0x3709), 0x8d }, { CCI_REG8(0x370b), 0x4f },
++ { CCI_REG8(0x3711), 0x00 }, { CCI_REG8(0x3712), 0x01 },
++ { CCI_REG8(0x3713), 0x00 }, { CCI_REG8(0x3720), 0x08 },
++ { CCI_REG8(0x3727), 0x22 }, { CCI_REG8(0x3728), 0x01 },
++ { CCI_REG8(0x375e), 0x00 }, { CCI_REG8(0x3760), 0x08 },
++ { CCI_REG8(0x3761), 0x10 }, { CCI_REG8(0x3762), 0x08 },
++ { CCI_REG8(0x3765), 0x10 }, { CCI_REG8(0x3766), 0x18 },
++ { CCI_REG8(0x376a), 0x08 }, { CCI_REG8(0x376b), 0x00 },
++ { CCI_REG8(0x376d), 0x1b }, { CCI_REG8(0x3791), 0x2b },
++ { CCI_REG8(0x3793), 0x2b }, { CCI_REG8(0x3795), 0x2b },
++ { CCI_REG8(0x3797), 0x4f }, { CCI_REG8(0x3799), 0x4f },
++ { CCI_REG8(0x379b), 0x4f }, { CCI_REG8(0x37a0), 0x22 },
++ { CCI_REG8(0x37da), 0x04 }, { CCI_REG8(0x37f9), 0x02 },
++ { CCI_REG8(0x37fa), 0x02 }, { CCI_REG8(0x37fb), 0x02 },
++ { CCI_REG8(0x3814), 0x11 }, { CCI_REG8(0x3815), 0x11 },
++ { CCI_REG8(0x3820), 0x40 }, { CCI_REG8(0x3821), 0x04 },
++ { CCI_REG8(0x3822), 0x00 }, { CCI_REG8(0x3823), 0x04 },
++ { CCI_REG8(0x3827), 0x08 }, { CCI_REG8(0x3828), 0x00 },
++ { CCI_REG8(0x382a), 0x81 }, { CCI_REG8(0x382e), 0x70 },
++ { CCI_REG8(0x3837), 0x10 }, { CCI_REG8(0x3839), 0x00 },
++ { CCI_REG8(0x383b), 0x00 }, { CCI_REG8(0x383c), 0x00 },
++ { CCI_REG8(0x383d), 0x10 }, { CCI_REG8(0x383f), 0x00 },
++ { CCI_REG8(0x384c), 0x02 }, { CCI_REG8(0x384d), 0x8c },
++ { CCI_REG8(0x3852), 0x00 }, { CCI_REG8(0x3856), 0x10 },
++ { CCI_REG8(0x3857), 0x10 }, { CCI_REG8(0x3858), 0x20 },
++ { CCI_REG8(0x3859), 0x20 }, { CCI_REG8(0x3894), 0x00 },
++ { CCI_REG8(0x3895), 0x00 }, { CCI_REG8(0x3896), 0x00 },
++ { CCI_REG8(0x3897), 0x00 }, { CCI_REG8(0x3900), 0x40 },
++ { CCI_REG8(0x3aed), 0x6e }, { CCI_REG8(0x3af1), 0x73 },
++ { CCI_REG8(0x3d86), 0x12 }, { CCI_REG8(0x3d87), 0x30 },
++ { CCI_REG8(0x3d8c), 0xab }, { CCI_REG8(0x3d8d), 0xb0 },
++ { CCI_REG8(0x3f00), 0x12 }, { CCI_REG8(0x3f00), 0x12 },
++ { CCI_REG8(0x3f00), 0x12 }, { CCI_REG8(0x3f01), 0x03 },
++ { CCI_REG8(0x4009), 0x01 }, { CCI_REG8(0x400e), 0xc6 },
++ { CCI_REG8(0x400f), 0x00 }, { CCI_REG8(0x4010), 0x28 },
++ { CCI_REG8(0x4011), 0x01 }, { CCI_REG8(0x4012), 0x0c },
++ { CCI_REG8(0x4015), 0x00 }, { CCI_REG8(0x4016), 0x1f },
++ { CCI_REG8(0x4017), 0x00 }, { CCI_REG8(0x4018), 0x07 },
++ { CCI_REG8(0x401a), 0x40 }, { CCI_REG8(0x4028), 0x01 },
++ { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4506), 0x01 },
++ { CCI_REG8(0x4508), 0x00 }, { CCI_REG8(0x4509), 0x35 },
++ { CCI_REG8(0x450a), 0x08 }, { CCI_REG8(0x450c), 0x00 },
++ { CCI_REG8(0x450d), 0x20 }, { CCI_REG8(0x450e), 0x00 },
++ { CCI_REG8(0x450f), 0x20 }, { CCI_REG8(0x451e), 0x00 },
++ { CCI_REG8(0x451f), 0x00 }, { CCI_REG8(0x4523), 0x00 },
++ { CCI_REG8(0x4526), 0x00 }, { CCI_REG8(0x4527), 0x18 },
++ { CCI_REG8(0x4580), 0x01 }, { CCI_REG8(0x4583), 0x00 },
++ { CCI_REG8(0x4584), 0x00 }, { CCI_REG8(0x45c0), 0xa1 },
++ { CCI_REG8(0x4602), 0x08 }, { CCI_REG8(0x4603), 0x05 },
++ { CCI_REG8(0x4606), 0x12 }, { CCI_REG8(0x4607), 0x30 },
++ { CCI_REG8(0x460b), 0x00 }, { CCI_REG8(0x460d), 0x00 },
++ { CCI_REG8(0x4640), 0x00 }, { CCI_REG8(0x4641), 0x24 },
++ { CCI_REG8(0x4643), 0x08 }, { CCI_REG8(0x4645), 0x14 },
++ { CCI_REG8(0x4648), 0x0a }, { CCI_REG8(0x4649), 0x06 },
++ { CCI_REG8(0x464a), 0x00 }, { CCI_REG8(0x464b), 0x30 },
++ { CCI_REG8(0x4800), 0x04 }, { CCI_REG8(0x4802), 0x02 },
++ { CCI_REG8(0x480b), 0x10 }, { CCI_REG8(0x480c), 0x80 },
++ { CCI_REG8(0x480e), 0x04 }, { CCI_REG8(0x480f), 0x32 },
++ { CCI_REG8(0x481b), 0x12 }, { CCI_REG8(0x4833), 0x30 },
++ { CCI_REG8(0x4837), 0x08 }, { CCI_REG8(0x484b), 0x27 },
++ { CCI_REG8(0x4850), 0x42 }, { CCI_REG8(0x4851), 0xaa },
++ { CCI_REG8(0x4860), 0x01 }, { CCI_REG8(0x4861), 0xec },
++ { CCI_REG8(0x4862), 0x25 }, { CCI_REG8(0x4888), 0x00 },
++ { CCI_REG8(0x4889), 0x03 }, { CCI_REG8(0x488c), 0x60 },
++ { CCI_REG8(0x4910), 0x28 }, { CCI_REG8(0x4911), 0x01 },
++ { CCI_REG8(0x4912), 0x0c }, { CCI_REG8(0x491a), 0x40 },
++ { CCI_REG8(0x4915), 0x00 }, { CCI_REG8(0x4916), 0x0f },
++ { CCI_REG8(0x4917), 0x00 }, { CCI_REG8(0x4918), 0x07 },
++ { CCI_REG8(0x4a10), 0x28 }, { CCI_REG8(0x4a11), 0x01 },
++ { CCI_REG8(0x4a12), 0x0c }, { CCI_REG8(0x4a1a), 0x40 },
++ { CCI_REG8(0x4a15), 0x00 }, { CCI_REG8(0x4a16), 0x0f },
++ { CCI_REG8(0x4a17), 0x00 }, { CCI_REG8(0x4a18), 0x07 },
++ { CCI_REG8(0x4d00), 0x04 }, { CCI_REG8(0x4d01), 0x5a },
++ { CCI_REG8(0x4d02), 0xbb }, { CCI_REG8(0x4d03), 0x84 },
++ { CCI_REG8(0x4d04), 0xd1 }, { CCI_REG8(0x4d05), 0x68 },
++ { CCI_REG8(0xc4fa), 0x10 }, { CCI_REG8(0x3b56), 0x0a },
++ { CCI_REG8(0x3b57), 0x0a }, { CCI_REG8(0x3b58), 0x0c },
++ { CCI_REG8(0x3b59), 0x10 }, { CCI_REG8(0x3a1d), 0x30 },
++ { CCI_REG8(0x3a1e), 0x30 }, { CCI_REG8(0x3a21), 0x30 },
++ { CCI_REG8(0x3a22), 0x30 }, { CCI_REG8(0x3992), 0x02 },
++ { CCI_REG8(0x399e), 0x02 }, { CCI_REG8(0x39fb), 0x30 },
++ { CCI_REG8(0x39fc), 0x30 }, { CCI_REG8(0x39fd), 0x30 },
++ { CCI_REG8(0x39fe), 0x30 }, { CCI_REG8(0x3a6d), 0x83 },
++ { CCI_REG8(0x3a5e), 0x83 }, { CCI_REG8(0xc500), 0x12 },
++ { CCI_REG8(0xc501), 0x12 }, { CCI_REG8(0xc502), 0x12 },
++ { CCI_REG8(0xc503), 0x12 }, { CCI_REG8(0xc505), 0x12 },
++ { CCI_REG8(0xc506), 0x12 }, { CCI_REG8(0xc507), 0x12 },
++ { CCI_REG8(0xc508), 0x12 }, { CCI_REG8(0x3a77), 0x12 },
++ { CCI_REG8(0x3a73), 0x12 }, { CCI_REG8(0x3a7b), 0x12 },
++ { CCI_REG8(0x3a7f), 0x12 }, { CCI_REG8(0x3b2e), 0x13 },
++ { CCI_REG8(0x3b29), 0x13 }, { CCI_REG8(0xc439), 0x13 },
++ { CCI_REG8(0xc469), 0x13 }, { CCI_REG8(0xc41c), 0x89 },
++ { CCI_REG8(0x3618), 0x80 }, { CCI_REG8(0xc514), 0x51 },
++ { CCI_REG8(0xc515), 0x2c }, { CCI_REG8(0xc516), 0x16 },
++ { CCI_REG8(0xc517), 0x0d }, { CCI_REG8(0x3615), 0x7f },
++ { CCI_REG8(0x3632), 0x99 }, { CCI_REG8(0x3642), 0x00 },
++ { CCI_REG8(0x3645), 0x80 }, { CCI_REG8(0x3702), 0x2a },
++ { CCI_REG8(0x3703), 0x2a }, { CCI_REG8(0x3708), 0x2f },
++ { CCI_REG8(0x3721), 0x15 }, { CCI_REG8(0x3744), 0x28 },
++ { CCI_REG8(0x3991), 0x0c }, { CCI_REG8(0x371d), 0x24 },
++ { CCI_REG8(0x371f), 0x0c }, { CCI_REG8(0x374b), 0x03 },
++ { CCI_REG8(0x37d0), 0x00 }, { CCI_REG8(0x391d), 0x55 },
++ { CCI_REG8(0x391e), 0x52 }, { CCI_REG8(0x399d), 0x0c },
++ { CCI_REG8(0x3a2f), 0x01 }, { CCI_REG8(0x3a30), 0x01 },
++ { CCI_REG8(0x3a31), 0x01 }, { CCI_REG8(0x3a32), 0x01 },
++ { CCI_REG8(0x3a34), 0x01 }, { CCI_REG8(0x3a35), 0x01 },
++ { CCI_REG8(0x3a36), 0x01 }, { CCI_REG8(0x3a37), 0x01 },
++ { CCI_REG8(0x3a43), 0x01 }, { CCI_REG8(0x3a44), 0x01 },
++ { CCI_REG8(0x3a45), 0x01 }, { CCI_REG8(0x3a46), 0x01 },
++ { CCI_REG8(0x3a48), 0x01 }, { CCI_REG8(0x3a49), 0x01 },
++ { CCI_REG8(0x3a4a), 0x01 }, { CCI_REG8(0x3a4b), 0x01 },
++ { CCI_REG8(0x3a50), 0x14 }, { CCI_REG8(0x3a54), 0x14 },
++ { CCI_REG8(0x3a60), 0x20 }, { CCI_REG8(0x3a6f), 0x20 },
++ { CCI_REG8(0x3ac5), 0x01 }, { CCI_REG8(0x3ac6), 0x01 },
++ { CCI_REG8(0x3ac7), 0x01 }, { CCI_REG8(0x3ac8), 0x01 },
++ { CCI_REG8(0x3ac9), 0x01 }, { CCI_REG8(0x3aca), 0x01 },
++ { CCI_REG8(0x3acb), 0x01 }, { CCI_REG8(0x3acc), 0x01 },
++ { CCI_REG8(0x3acd), 0x01 }, { CCI_REG8(0x3ace), 0x01 },
++ { CCI_REG8(0x3acf), 0x01 }, { CCI_REG8(0x3ad0), 0x01 },
++ { CCI_REG8(0x3ad1), 0x01 }, { CCI_REG8(0x3ad2), 0x01 },
++ { CCI_REG8(0x3ad3), 0x01 }, { CCI_REG8(0x3ad4), 0x01 },
++ { CCI_REG8(0x3add), 0x1f }, { CCI_REG8(0x3adf), 0x24 },
++ { CCI_REG8(0x3aef), 0x1f }, { CCI_REG8(0x3af0), 0x24 },
++ { CCI_REG8(0x3b92), 0x08 }, { CCI_REG8(0x3b93), 0x08 },
++ { CCI_REG8(0x3b94), 0x08 }, { CCI_REG8(0x3b95), 0x08 },
++ { CCI_REG8(0x3be7), 0x1e }, { CCI_REG8(0x3be8), 0x26 },
++ { CCI_REG8(0xc44a), 0x20 }, { CCI_REG8(0xc44c), 0x20 },
++ { CCI_REG8(0xc483), 0x00 }, { CCI_REG8(0xc484), 0x00 },
++ { CCI_REG8(0xc485), 0x00 }, { CCI_REG8(0xc486), 0x00 },
++ { CCI_REG8(0xc487), 0x01 }, { CCI_REG8(0xc488), 0x01 },
++ { CCI_REG8(0xc489), 0x01 }, { CCI_REG8(0xc48a), 0x01 },
++ { CCI_REG8(0xc4c1), 0x00 }, { CCI_REG8(0xc4c2), 0x00 },
++ { CCI_REG8(0xc4c3), 0x00 }, { CCI_REG8(0xc4c4), 0x00 },
++ { CCI_REG8(0xc4c6), 0x10 }, { CCI_REG8(0xc4c7), 0x10 },
++ { CCI_REG8(0xc4c8), 0x10 }, { CCI_REG8(0xc4c9), 0x10 },
++ { CCI_REG8(0xc4ca), 0x10 }, { CCI_REG8(0xc4cb), 0x10 },
++ { CCI_REG8(0xc4cc), 0x10 }, { CCI_REG8(0xc4cd), 0x10 },
++ { CCI_REG8(0xc4ea), 0x07 }, { CCI_REG8(0xc4eb), 0x07 },
++ { CCI_REG8(0xc4ec), 0x07 }, { CCI_REG8(0xc4ed), 0x07 },
++ { CCI_REG8(0xc4ee), 0x07 }, { CCI_REG8(0xc4f6), 0x10 },
++ { CCI_REG8(0xc4f7), 0x10 }, { CCI_REG8(0xc4f8), 0x10 },
++ { CCI_REG8(0xc4f9), 0x10 }, { CCI_REG8(0xc518), 0x0e },
++ { CCI_REG8(0xc519), 0x0e }, { CCI_REG8(0xc51a), 0x0e },
++ { CCI_REG8(0xc51b), 0x0e }, { CCI_REG8(0xc51c), 0x0e },
++ { CCI_REG8(0xc51d), 0x0e }, { CCI_REG8(0xc51e), 0x0e },
++ { CCI_REG8(0xc51f), 0x0e }, { CCI_REG8(0xc520), 0x0e },
++ { CCI_REG8(0xc521), 0x0e }, { CCI_REG8(0xc522), 0x0e },
++ { CCI_REG8(0xc523), 0x0e }, { CCI_REG8(0xc524), 0x0e },
++ { CCI_REG8(0xc525), 0x0e }, { CCI_REG8(0xc526), 0x0e },
++ { CCI_REG8(0xc527), 0x0e }, { CCI_REG8(0xc528), 0x0e },
++ { CCI_REG8(0xc529), 0x0e }, { CCI_REG8(0xc52a), 0x0e },
++ { CCI_REG8(0xc52b), 0x0e }, { CCI_REG8(0xc52c), 0x0e },
++ { CCI_REG8(0xc52d), 0x0e }, { CCI_REG8(0xc52e), 0x0e },
++ { CCI_REG8(0xc52f), 0x0e }, { CCI_REG8(0xc530), 0x0e },
++ { CCI_REG8(0xc531), 0x0e }, { CCI_REG8(0xc532), 0x0e },
++ { CCI_REG8(0xc533), 0x0e }, { CCI_REG8(0xc534), 0x0e },
++ { CCI_REG8(0xc535), 0x0e }, { CCI_REG8(0xc536), 0x0e },
++ { CCI_REG8(0xc537), 0x0e }, { CCI_REG8(0xc538), 0x0e },
++ { CCI_REG8(0xc539), 0x0e }, { CCI_REG8(0xc53a), 0x0e },
++ { CCI_REG8(0xc53b), 0x0e }, { CCI_REG8(0xc53c), 0x0e },
++ { CCI_REG8(0xc53d), 0x0e }, { CCI_REG8(0xc53e), 0x0e },
++ { CCI_REG8(0xc53f), 0x0e }, { CCI_REG8(0xc540), 0x0e },
++ { CCI_REG8(0xc541), 0x0e }, { CCI_REG8(0xc542), 0x0e },
++ { CCI_REG8(0xc543), 0x0e }, { CCI_REG8(0xc544), 0x0e },
++ { CCI_REG8(0xc545), 0x0e }, { CCI_REG8(0xc546), 0x0e },
++ { CCI_REG8(0xc547), 0x0e }, { CCI_REG8(0xc548), 0x0e },
++ { CCI_REG8(0xc549), 0x0e }, { CCI_REG8(0xc57f), 0x22 },
++ { CCI_REG8(0xc580), 0x22 }, { CCI_REG8(0xc581), 0x22 },
++ { CCI_REG8(0xc582), 0x22 }, { CCI_REG8(0xc583), 0x22 },
++ { CCI_REG8(0xc584), 0x22 }, { CCI_REG8(0xc585), 0x22 },
++ { CCI_REG8(0xc586), 0x22 }, { CCI_REG8(0xc587), 0x22 },
++ { CCI_REG8(0xc588), 0x22 }, { CCI_REG8(0xc589), 0x22 },
++ { CCI_REG8(0xc58a), 0x22 }, { CCI_REG8(0xc58b), 0x22 },
++ { CCI_REG8(0xc58c), 0x22 }, { CCI_REG8(0xc58d), 0x22 },
++ { CCI_REG8(0xc58e), 0x22 }, { CCI_REG8(0xc58f), 0x22 },
++ { CCI_REG8(0xc590), 0x22 }, { CCI_REG8(0xc591), 0x22 },
++ { CCI_REG8(0xc592), 0x22 }, { CCI_REG8(0xc598), 0x22 },
++ { CCI_REG8(0xc599), 0x22 }, { CCI_REG8(0xc59a), 0x22 },
++ { CCI_REG8(0xc59b), 0x22 }, { CCI_REG8(0xc59c), 0x22 },
++ { CCI_REG8(0xc59d), 0x22 }, { CCI_REG8(0xc59e), 0x22 },
++ { CCI_REG8(0xc59f), 0x22 }, { CCI_REG8(0xc5a0), 0x22 },
++ { CCI_REG8(0xc5a1), 0x22 }, { CCI_REG8(0xc5a2), 0x22 },
++ { CCI_REG8(0xc5a3), 0x22 }, { CCI_REG8(0xc5a4), 0x22 },
++ { CCI_REG8(0xc5a5), 0x22 }, { CCI_REG8(0xc5a6), 0x22 },
++ { CCI_REG8(0xc5a7), 0x22 }, { CCI_REG8(0xc5a8), 0x22 },
++ { CCI_REG8(0xc5a9), 0x22 }, { CCI_REG8(0xc5aa), 0x22 },
++ { CCI_REG8(0xc5ab), 0x22 }, { CCI_REG8(0xc5b1), 0x2a },
++ { CCI_REG8(0xc5b2), 0x2a }, { CCI_REG8(0xc5b3), 0x2a },
++ { CCI_REG8(0xc5b4), 0x2a }, { CCI_REG8(0xc5b5), 0x2a },
++ { CCI_REG8(0xc5b6), 0x2a }, { CCI_REG8(0xc5b7), 0x2a },
++ { CCI_REG8(0xc5b8), 0x2a }, { CCI_REG8(0xc5b9), 0x2a },
++ { CCI_REG8(0xc5ba), 0x2a }, { CCI_REG8(0xc5bb), 0x2a },
++ { CCI_REG8(0xc5bc), 0x2a }, { CCI_REG8(0xc5bd), 0x2a },
++ { CCI_REG8(0xc5be), 0x2a }, { CCI_REG8(0xc5bf), 0x2a },
++ { CCI_REG8(0xc5c0), 0x2a }, { CCI_REG8(0xc5c1), 0x2a },
++ { CCI_REG8(0xc5c2), 0x2a }, { CCI_REG8(0xc5c3), 0x2a },
++ { CCI_REG8(0xc5c4), 0x2a }, { CCI_REG8(0xc5ca), 0x2a },
++ { CCI_REG8(0xc5cb), 0x2a }, { CCI_REG8(0xc5cc), 0x2a },
++ { CCI_REG8(0xc5cd), 0x2a }, { CCI_REG8(0xc5ce), 0x2a },
++ { CCI_REG8(0xc5cf), 0x2a }, { CCI_REG8(0xc5d0), 0x2a },
++ { CCI_REG8(0xc5d1), 0x2a }, { CCI_REG8(0xc5d2), 0x2a },
++ { CCI_REG8(0xc5d3), 0x2a }, { CCI_REG8(0xc5d4), 0x2a },
++ { CCI_REG8(0xc5d5), 0x2a }, { CCI_REG8(0xc5d6), 0x2a },
++ { CCI_REG8(0xc5d7), 0x2a }, { CCI_REG8(0xc5d8), 0x2a },
++ { CCI_REG8(0xc5d9), 0x2a }, { CCI_REG8(0xc5da), 0x2a },
++ { CCI_REG8(0xc5db), 0x2a }, { CCI_REG8(0xc5dc), 0x2a },
++ { CCI_REG8(0xc5dd), 0x2a }, { CCI_REG8(0xc5e8), 0x22 },
++ { CCI_REG8(0xc5ea), 0x22 }, { CCI_REG8(0x4540), 0x12 },
++ { CCI_REG8(0x4541), 0x30 }, { CCI_REG8(0x3d86), 0x12 },
++ { CCI_REG8(0x3d87), 0x30 }, { CCI_REG8(0x4606), 0x12 },
++ { CCI_REG8(0x4607), 0x30 }, { CCI_REG8(0x4648), 0x0a },
++ { CCI_REG8(0x4649), 0x06 }, { CCI_REG8(0x3220), 0x12 },
++ { CCI_REG8(0x3221), 0x30 }, { CCI_REG8(0x40c2), 0x12 },
++ { CCI_REG8(0x49c2), 0x12 }, { CCI_REG8(0x4ac2), 0x12 },
++ { CCI_REG8(0x40c3), 0x30 }, { CCI_REG8(0x49c3), 0x30 },
++ { CCI_REG8(0x4ac3), 0x30 }, { CCI_REG8(0x36b0), 0x12 },
++ { CCI_REG8(0x36b1), 0x30 }, { CCI_REG8(0x45cb), 0x12 },
++ { CCI_REG8(0x45cc), 0x30 }, { CCI_REG8(0x4585), 0x12 },
++ { CCI_REG8(0x4586), 0x30 }, { CCI_REG8(0x36b2), 0x12 },
++ { CCI_REG8(0x36b3), 0x30 }, { CCI_REG8(0x5a40), 0x75 },
++ { CCI_REG8(0x5a41), 0x75 }, { CCI_REG8(0x5a42), 0x75 },
++ { CCI_REG8(0x5a43), 0x75 }, { CCI_REG8(0x5a44), 0x75 },
++ { CCI_REG8(0x5a45), 0x75 }, { CCI_REG8(0x5a46), 0x75 },
++ { CCI_REG8(0x5a47), 0x75 }, { CCI_REG8(0x5a48), 0x75 },
++ { CCI_REG8(0x5a49), 0x75 }, { CCI_REG8(0x5a4a), 0x75 },
++ { CCI_REG8(0x5a4b), 0x75 }, { CCI_REG8(0x5a4c), 0x75 },
++ { CCI_REG8(0x5a4d), 0x75 }, { CCI_REG8(0x5a4e), 0x75 },
++ { CCI_REG8(0x5a4f), 0x75 }, { CCI_REG8(0x5a50), 0x75 },
++ { CCI_REG8(0x5a51), 0x75 }, { CCI_REG8(0x5a52), 0x75 },
++ { CCI_REG8(0x5a53), 0x75 }, { CCI_REG8(0x5a54), 0x75 },
++ { CCI_REG8(0x5a55), 0x75 }, { CCI_REG8(0x5a56), 0x75 },
++ { CCI_REG8(0x5a57), 0x75 }, { CCI_REG8(0x5a58), 0x75 },
++ { CCI_REG8(0x5a59), 0x75 }, { CCI_REG8(0x5a5a), 0x75 },
++ { CCI_REG8(0x5a5b), 0x75 }, { CCI_REG8(0x5a5c), 0x75 },
++ { CCI_REG8(0x5a5d), 0x75 }, { CCI_REG8(0x5a5e), 0x75 },
++ { CCI_REG8(0x5a5f), 0x75 }, { CCI_REG8(0x5a60), 0x75 },
++ { CCI_REG8(0x5a61), 0x75 }, { CCI_REG8(0x5a62), 0x75 },
++ { CCI_REG8(0x5a63), 0x75 }, { CCI_REG8(0x5a64), 0x75 },
++ { CCI_REG8(0x5a65), 0x75 }, { CCI_REG8(0x5a66), 0x75 },
++ { CCI_REG8(0x5a67), 0x75 }, { CCI_REG8(0x5a68), 0x75 },
++ { CCI_REG8(0x5a69), 0x75 }, { CCI_REG8(0x5a6a), 0x75 },
++ { CCI_REG8(0x5a6b), 0x75 }, { CCI_REG8(0x5a6c), 0x75 },
++ { CCI_REG8(0x5a6d), 0x75 }, { CCI_REG8(0x5a6e), 0x75 },
++ { CCI_REG8(0x5a6f), 0x75 }, { CCI_REG8(0x5a70), 0x75 },
++ { CCI_REG8(0x5a71), 0x75 }, { CCI_REG8(0x5a72), 0x75 },
++ { CCI_REG8(0x5a73), 0x75 }, { CCI_REG8(0x5a74), 0x75 },
++ { CCI_REG8(0x5a75), 0x75 }, { CCI_REG8(0x5a76), 0x75 },
++ { CCI_REG8(0x5a77), 0x75 }, { CCI_REG8(0x5a78), 0x75 },
++ { CCI_REG8(0x5a79), 0x75 }, { CCI_REG8(0x5a7a), 0x75 },
++ { CCI_REG8(0x5a7b), 0x75 }, { CCI_REG8(0x5a7c), 0x75 },
++ { CCI_REG8(0x5a7d), 0x75 }, { CCI_REG8(0x5a7e), 0x75 },
++ { CCI_REG8(0x5a7f), 0x75 }, { CCI_REG8(0x5a80), 0x75 },
++ { CCI_REG8(0x5a81), 0x75 }, { CCI_REG8(0x5a82), 0x75 },
++ { CCI_REG8(0x5a83), 0x75 }, { CCI_REG8(0x5a84), 0x75 },
++ { CCI_REG8(0x5a85), 0x75 }, { CCI_REG8(0x5a86), 0x75 },
++ { CCI_REG8(0x5a87), 0x75 }, { CCI_REG8(0x5a88), 0x75 },
++ { CCI_REG8(0x5a89), 0x75 }, { CCI_REG8(0x5a8a), 0x75 },
++ { CCI_REG8(0x5a8b), 0x75 }, { CCI_REG8(0x5a8c), 0x75 },
++ { CCI_REG8(0x5a8d), 0x75 }, { CCI_REG8(0x5a8e), 0x75 },
++ { CCI_REG8(0x5a8f), 0x75 }, { CCI_REG8(0x5a90), 0x75 },
++ { CCI_REG8(0x5a91), 0x75 }, { CCI_REG8(0x5a92), 0x75 },
++ { CCI_REG8(0x5a93), 0x75 }, { CCI_REG8(0x5a94), 0x75 },
++ { CCI_REG8(0x5a95), 0x75 }, { CCI_REG8(0x5a96), 0x75 },
++ { CCI_REG8(0x5a97), 0x75 }, { CCI_REG8(0x5a98), 0x75 },
++ { CCI_REG8(0x5a99), 0x75 }, { CCI_REG8(0x5a9a), 0x75 },
++ { CCI_REG8(0x5a9b), 0x75 }, { CCI_REG8(0x5a9c), 0x75 },
++ { CCI_REG8(0x5a9d), 0x75 }, { CCI_REG8(0x5a9e), 0x75 },
++ { CCI_REG8(0x5a9f), 0x75 }, { CCI_REG8(0x5aa0), 0x75 },
++ { CCI_REG8(0x5aa1), 0x75 }, { CCI_REG8(0x5aa2), 0x75 },
++ { CCI_REG8(0x5aa3), 0x75 }, { CCI_REG8(0x5aa4), 0x75 },
++ { CCI_REG8(0x5aa5), 0x75 }, { CCI_REG8(0x5aa6), 0x75 },
++ { CCI_REG8(0x5aa7), 0x75 }, { CCI_REG8(0x5aa8), 0x75 },
++ { CCI_REG8(0x5aa9), 0x75 }, { CCI_REG8(0x5aaa), 0x75 },
++ { CCI_REG8(0x5aab), 0x75 }, { CCI_REG8(0x5aac), 0x75 },
++ { CCI_REG8(0x5aad), 0x75 }, { CCI_REG8(0x5aae), 0x75 },
++ { CCI_REG8(0x5aaf), 0x75 }, { CCI_REG8(0x5ab0), 0x75 },
++ { CCI_REG8(0x5ab1), 0x75 }, { CCI_REG8(0x5ab2), 0x75 },
++ { CCI_REG8(0x5ab3), 0x75 }, { CCI_REG8(0x5ab4), 0x75 },
++ { CCI_REG8(0x5ab5), 0x75 }, { CCI_REG8(0x5ab6), 0x75 },
++ { CCI_REG8(0x5ab7), 0x75 }, { CCI_REG8(0x5ab8), 0x75 },
++ { CCI_REG8(0x5ab9), 0x75 }, { CCI_REG8(0x5aba), 0x75 },
++ { CCI_REG8(0x5abb), 0x75 }, { CCI_REG8(0x5abc), 0x75 },
++ { CCI_REG8(0x5abd), 0x75 }, { CCI_REG8(0x5abe), 0x75 },
++ { CCI_REG8(0x5abf), 0x75 }, { CCI_REG8(0x5ac0), 0x75 },
++ { CCI_REG8(0x5ac1), 0x75 }, { CCI_REG8(0x5ac2), 0x75 },
++ { CCI_REG8(0x5ac3), 0x75 }, { CCI_REG8(0x5ac4), 0x75 },
++ { CCI_REG8(0x5ac5), 0x75 }, { CCI_REG8(0x5ac6), 0x75 },
++ { CCI_REG8(0x5ac7), 0x75 }, { CCI_REG8(0x5ac8), 0x75 },
++ { CCI_REG8(0x5ac9), 0x75 }, { CCI_REG8(0x5aca), 0x75 },
++ { CCI_REG8(0x5acb), 0x75 }, { CCI_REG8(0x5acc), 0x75 },
++ { CCI_REG8(0x5acd), 0x75 }, { CCI_REG8(0x5ace), 0x75 },
++ { CCI_REG8(0x5acf), 0x75 }, { CCI_REG8(0x5ad0), 0x75 },
++ { CCI_REG8(0x5ad1), 0x75 }, { CCI_REG8(0x5ad2), 0x75 },
++ { CCI_REG8(0x5ad3), 0x75 }, { CCI_REG8(0x5ad4), 0x75 },
++ { CCI_REG8(0x5ad5), 0x75 }, { CCI_REG8(0x5ad6), 0x75 },
++ { CCI_REG8(0x5ad7), 0x75 }, { CCI_REG8(0x5ad8), 0x75 },
++ { CCI_REG8(0x5ad9), 0x75 }, { CCI_REG8(0x5ada), 0x75 },
++ { CCI_REG8(0x5adb), 0x75 }, { CCI_REG8(0x5adc), 0x75 },
++ { CCI_REG8(0x5add), 0x75 }, { CCI_REG8(0x5ade), 0x75 },
++ { CCI_REG8(0x5adf), 0x75 }, { CCI_REG8(0x5ae0), 0x75 },
++ { CCI_REG8(0x5ae1), 0x75 }, { CCI_REG8(0x5ae2), 0x75 },
++ { CCI_REG8(0x5ae3), 0x75 }, { CCI_REG8(0x5ae4), 0x75 },
++ { CCI_REG8(0x5ae5), 0x75 }, { CCI_REG8(0x5ae6), 0x75 },
++ { CCI_REG8(0x5ae7), 0x75 }, { CCI_REG8(0x5ae8), 0x75 },
++ { CCI_REG8(0x5ae9), 0x75 }, { CCI_REG8(0x5aea), 0x75 },
++ { CCI_REG8(0x5aeb), 0x75 }, { CCI_REG8(0x5aec), 0x75 },
++ { CCI_REG8(0x5aed), 0x75 }, { CCI_REG8(0x5aee), 0x75 },
++ { CCI_REG8(0x5aef), 0x75 }, { CCI_REG8(0x5af0), 0x75 },
++ { CCI_REG8(0x5af1), 0x75 }, { CCI_REG8(0x5af2), 0x75 },
++ { CCI_REG8(0x5af3), 0x75 }, { CCI_REG8(0x5af4), 0x75 },
++ { CCI_REG8(0x5af5), 0x75 }, { CCI_REG8(0x5af6), 0x75 },
++ { CCI_REG8(0x5af7), 0x75 }, { CCI_REG8(0x5af8), 0x75 },
++ { CCI_REG8(0x5af9), 0x75 }, { CCI_REG8(0x5afa), 0x75 },
++ { CCI_REG8(0x5afb), 0x75 }, { CCI_REG8(0x5afc), 0x75 },
++ { CCI_REG8(0x5afd), 0x75 }, { CCI_REG8(0x5afe), 0x75 },
++ { CCI_REG8(0x5aff), 0x75 }, { CCI_REG8(0x5b00), 0x75 },
++ { CCI_REG8(0x5b01), 0x75 }, { CCI_REG8(0x5b02), 0x75 },
++ { CCI_REG8(0x5b03), 0x75 }, { CCI_REG8(0x5b04), 0x75 },
++ { CCI_REG8(0x5b05), 0x75 }, { CCI_REG8(0x5b06), 0x75 },
++ { CCI_REG8(0x5b07), 0x75 }, { CCI_REG8(0x5b08), 0x75 },
++ { CCI_REG8(0x5b09), 0x75 }, { CCI_REG8(0x5b0a), 0x75 },
++ { CCI_REG8(0x5b0b), 0x75 }, { CCI_REG8(0x5b0c), 0x75 },
++ { CCI_REG8(0x5b0d), 0x75 }, { CCI_REG8(0x5b0e), 0x75 },
++ { CCI_REG8(0x5b0f), 0x75 }, { CCI_REG8(0x5b10), 0x75 },
++ { CCI_REG8(0x5b11), 0x75 }, { CCI_REG8(0x5b12), 0x75 },
++ { CCI_REG8(0x5b13), 0x75 }, { CCI_REG8(0x5b14), 0x75 },
++ { CCI_REG8(0x5b15), 0x75 }, { CCI_REG8(0x5b16), 0x75 },
++ { CCI_REG8(0x5b17), 0x75 }, { CCI_REG8(0x5b18), 0x75 },
++ { CCI_REG8(0x5b19), 0x75 }, { CCI_REG8(0x5b1a), 0x75 },
++ { CCI_REG8(0x5b1b), 0x75 }, { CCI_REG8(0x5b1c), 0x75 },
++ { CCI_REG8(0x5b1d), 0x75 }, { CCI_REG8(0x5b1e), 0x75 },
++ { CCI_REG8(0x5b1f), 0x75 }, { CCI_REG8(0x5b20), 0x75 },
++ { CCI_REG8(0x5b21), 0x75 }, { CCI_REG8(0x5b22), 0x75 },
++ { CCI_REG8(0x5b23), 0x75 }, { CCI_REG8(0x5b24), 0x75 },
++ { CCI_REG8(0x5b25), 0x75 }, { CCI_REG8(0x5b26), 0x75 },
++ { CCI_REG8(0x5b27), 0x75 }, { CCI_REG8(0x5b28), 0x75 },
++ { CCI_REG8(0x5b29), 0x75 }, { CCI_REG8(0x5b2a), 0x75 },
++ { CCI_REG8(0x5b2b), 0x75 }, { CCI_REG8(0x5b2c), 0x75 },
++ { CCI_REG8(0x5b2d), 0x75 }, { CCI_REG8(0x5b2e), 0x75 },
++ { CCI_REG8(0x5b2f), 0x75 }, { CCI_REG8(0x5b30), 0x75 },
++ { CCI_REG8(0x5b31), 0x75 }, { CCI_REG8(0x5b32), 0x75 },
++ { CCI_REG8(0x5b33), 0x75 }, { CCI_REG8(0x5b34), 0x75 },
++ { CCI_REG8(0x5b35), 0x75 }, { CCI_REG8(0x5b36), 0x75 },
++ { CCI_REG8(0x5b37), 0x75 }, { CCI_REG8(0x5b38), 0x75 },
++ { CCI_REG8(0x5b39), 0x75 }, { CCI_REG8(0x5b3a), 0x75 },
++ { CCI_REG8(0x5b3b), 0x75 }, { CCI_REG8(0x5b3c), 0x75 },
++ { CCI_REG8(0x5b3d), 0x75 }, { CCI_REG8(0x5b3e), 0x75 },
++ { CCI_REG8(0x5b3f), 0x75 }, { CCI_REG8(0x5b40), 0x75 },
++ { CCI_REG8(0x5b41), 0x75 }, { CCI_REG8(0x5b42), 0x75 },
++ { CCI_REG8(0x5b43), 0x75 }, { CCI_REG8(0x5b44), 0x75 },
++ { CCI_REG8(0x5b45), 0x75 }, { CCI_REG8(0x5b46), 0x75 },
++ { CCI_REG8(0x5b47), 0x75 }, { CCI_REG8(0x5b48), 0x75 },
++ { CCI_REG8(0x5b49), 0x75 }, { CCI_REG8(0x5b4a), 0x75 },
++ { CCI_REG8(0x5b4b), 0x75 }, { CCI_REG8(0x5b4c), 0x75 },
++ { CCI_REG8(0x5b4d), 0x75 }, { CCI_REG8(0x5b4e), 0x75 },
++ { CCI_REG8(0x5b4f), 0x75 }, { CCI_REG8(0x5b50), 0x75 },
++ { CCI_REG8(0x5b51), 0x75 }, { CCI_REG8(0x5b52), 0x75 },
++ { CCI_REG8(0x5b53), 0x75 }, { CCI_REG8(0x5b54), 0x75 },
++ { CCI_REG8(0x5b55), 0x75 }, { CCI_REG8(0x5b56), 0x75 },
++ { CCI_REG8(0x5b57), 0x75 }, { CCI_REG8(0x5b58), 0x75 },
++ { CCI_REG8(0x5b59), 0x75 }, { CCI_REG8(0x5b5a), 0x75 },
++ { CCI_REG8(0x5b5b), 0x75 }, { CCI_REG8(0x5b5c), 0x75 },
++ { CCI_REG8(0x5b5d), 0x75 }, { CCI_REG8(0x5b5e), 0x75 },
++ { CCI_REG8(0x5b5f), 0x75 }, { CCI_REG8(0x5b80), 0x75 },
++ { CCI_REG8(0x5b81), 0x75 }, { CCI_REG8(0x5b82), 0x75 },
++ { CCI_REG8(0x5b83), 0x75 }, { CCI_REG8(0x5b84), 0x75 },
++ { CCI_REG8(0x5b85), 0x75 }, { CCI_REG8(0x5b86), 0x75 },
++ { CCI_REG8(0x5b87), 0x75 }, { CCI_REG8(0x5b88), 0x75 },
++ { CCI_REG8(0x5b89), 0x75 }, { CCI_REG8(0x5b8a), 0x75 },
++ { CCI_REG8(0x5b8b), 0x75 }, { CCI_REG8(0x5b8c), 0x75 },
++ { CCI_REG8(0x5b8d), 0x75 }, { CCI_REG8(0x5b8e), 0x75 },
++ { CCI_REG8(0x5b8f), 0x75 }, { CCI_REG8(0x5b90), 0x75 },
++ { CCI_REG8(0x5b91), 0x75 }, { CCI_REG8(0x5b92), 0x75 },
++ { CCI_REG8(0x5b93), 0x75 }, { CCI_REG8(0x5b94), 0x75 },
++ { CCI_REG8(0x5b95), 0x75 }, { CCI_REG8(0x5b96), 0x75 },
++ { CCI_REG8(0x5b97), 0x75 }, { CCI_REG8(0x5b98), 0x75 },
++ { CCI_REG8(0x5b99), 0x75 }, { CCI_REG8(0x5b9a), 0x75 },
++ { CCI_REG8(0x5b9b), 0x75 }, { CCI_REG8(0x5b9c), 0x75 },
++ { CCI_REG8(0x5b9d), 0x75 }, { CCI_REG8(0x5b9e), 0x75 },
++ { CCI_REG8(0x5b9f), 0x75 }, { CCI_REG8(0x5ba0), 0x75 },
++ { CCI_REG8(0x5ba1), 0x75 }, { CCI_REG8(0x5ba2), 0x75 },
++ { CCI_REG8(0x5ba3), 0x75 }, { CCI_REG8(0x5ba4), 0x75 },
++ { CCI_REG8(0x5ba5), 0x75 }, { CCI_REG8(0x5ba6), 0x75 },
++ { CCI_REG8(0x5ba7), 0x75 }, { CCI_REG8(0x5ba8), 0x75 },
++ { CCI_REG8(0x5ba9), 0x75 }, { CCI_REG8(0x5baa), 0x75 },
++ { CCI_REG8(0x5bab), 0x75 }, { CCI_REG8(0x5bac), 0x75 },
++ { CCI_REG8(0x5bad), 0x75 }, { CCI_REG8(0x5bae), 0x75 },
++ { CCI_REG8(0x5baf), 0x75 }, { CCI_REG8(0x5bb0), 0x75 },
++ { CCI_REG8(0x5bb1), 0x75 }, { CCI_REG8(0x5bb2), 0x75 },
++ { CCI_REG8(0x5bb3), 0x75 }, { CCI_REG8(0x5bb4), 0x75 },
++ { CCI_REG8(0x5bb5), 0x75 }, { CCI_REG8(0x5bb6), 0x75 },
++ { CCI_REG8(0x5bb7), 0x75 }, { CCI_REG8(0x5bb8), 0x75 },
++ { CCI_REG8(0x5bb9), 0x75 }, { CCI_REG8(0x5bba), 0x75 },
++ { CCI_REG8(0x5bbb), 0x75 }, { CCI_REG8(0x5bbc), 0x75 },
++ { CCI_REG8(0x5bbd), 0x75 }, { CCI_REG8(0x5bbe), 0x75 },
++ { CCI_REG8(0x5bbf), 0x75 }, { CCI_REG8(0x5bc0), 0x75 },
++ { CCI_REG8(0x5bc1), 0x75 }, { CCI_REG8(0x5bc2), 0x75 },
++ { CCI_REG8(0x5bc3), 0x75 }, { CCI_REG8(0x5bc4), 0x75 },
++ { CCI_REG8(0x5bc5), 0x75 }, { CCI_REG8(0x5bc6), 0x75 },
++ { CCI_REG8(0x5bc7), 0x75 }, { CCI_REG8(0x5bc8), 0x75 },
++ { CCI_REG8(0x5bc9), 0x75 }, { CCI_REG8(0x5bca), 0x75 },
++ { CCI_REG8(0x5bcb), 0x75 }, { CCI_REG8(0x5bcc), 0x75 },
++ { CCI_REG8(0x5bcd), 0x75 }, { CCI_REG8(0x5bce), 0x75 },
++ { CCI_REG8(0x5bcf), 0x75 }, { CCI_REG8(0x5bd0), 0x75 },
++ { CCI_REG8(0x5bd1), 0x75 }, { CCI_REG8(0x5bd2), 0x75 },
++ { CCI_REG8(0x5bd3), 0x75 }, { CCI_REG8(0x5bd4), 0x75 },
++ { CCI_REG8(0x5bd5), 0x75 }, { CCI_REG8(0x5bd6), 0x75 },
++ { CCI_REG8(0x5bd7), 0x75 }, { CCI_REG8(0x5bd8), 0x75 },
++ { CCI_REG8(0x5bd9), 0x75 }, { CCI_REG8(0x5bda), 0x75 },
++ { CCI_REG8(0x5bdb), 0x75 }, { CCI_REG8(0x5bdc), 0x75 },
++ { CCI_REG8(0x5bdd), 0x75 }, { CCI_REG8(0x5bde), 0x75 },
++ { CCI_REG8(0x5bdf), 0x75 }, { CCI_REG8(0x5be0), 0x75 },
++ { CCI_REG8(0x5be1), 0x75 }, { CCI_REG8(0x5be2), 0x75 },
++ { CCI_REG8(0x5be3), 0x75 }, { CCI_REG8(0x5be4), 0x75 },
++ { CCI_REG8(0x5be5), 0x75 }, { CCI_REG8(0x5be6), 0x75 },
++ { CCI_REG8(0x5be7), 0x75 }, { CCI_REG8(0x5be8), 0x75 },
++ { CCI_REG8(0x5be9), 0x75 }, { CCI_REG8(0x5bea), 0x75 },
++ { CCI_REG8(0x5beb), 0x75 }, { CCI_REG8(0x5bec), 0x75 },
++ { CCI_REG8(0x5bed), 0x75 }, { CCI_REG8(0x5bee), 0x75 },
++ { CCI_REG8(0x5bef), 0x75 }, { CCI_REG8(0x5bf0), 0x75 },
++ { CCI_REG8(0x5bf1), 0x75 }, { CCI_REG8(0x5bf2), 0x75 },
++ { CCI_REG8(0x5bf3), 0x75 }, { CCI_REG8(0x5bf4), 0x75 },
++ { CCI_REG8(0x5bf5), 0x75 }, { CCI_REG8(0x5bf6), 0x75 },
++ { CCI_REG8(0x5bf7), 0x75 }, { CCI_REG8(0x5bf8), 0x75 },
++ { CCI_REG8(0x5bf9), 0x75 }, { CCI_REG8(0x5bfa), 0x75 },
++ { CCI_REG8(0x5bfb), 0x75 }, { CCI_REG8(0x5bfc), 0x75 },
++ { CCI_REG8(0x5bfd), 0x75 }, { CCI_REG8(0x5bfe), 0x75 },
++ { CCI_REG8(0x5bff), 0x75 }, { CCI_REG8(0x5c00), 0x75 },
++ { CCI_REG8(0x5c01), 0x75 }, { CCI_REG8(0x5c02), 0x75 },
++ { CCI_REG8(0x5c03), 0x75 }, { CCI_REG8(0x5c04), 0x75 },
++ { CCI_REG8(0x5c05), 0x75 }, { CCI_REG8(0x5c06), 0x75 },
++ { CCI_REG8(0x5c07), 0x75 }, { CCI_REG8(0x5c08), 0x75 },
++ { CCI_REG8(0x5c09), 0x75 }, { CCI_REG8(0x5c0a), 0x75 },
++ { CCI_REG8(0x5c0b), 0x75 }, { CCI_REG8(0x5c0c), 0x75 },
++ { CCI_REG8(0x5c0d), 0x75 }, { CCI_REG8(0x5c0e), 0x75 },
++ { CCI_REG8(0x5c0f), 0x75 }, { CCI_REG8(0x5c10), 0x75 },
++ { CCI_REG8(0x5c11), 0x75 }, { CCI_REG8(0x5c12), 0x75 },
++ { CCI_REG8(0x5c13), 0x75 }, { CCI_REG8(0x5c14), 0x75 },
++ { CCI_REG8(0x5c15), 0x75 }, { CCI_REG8(0x5c16), 0x75 },
++ { CCI_REG8(0x5c17), 0x75 }, { CCI_REG8(0x5c18), 0x75 },
++ { CCI_REG8(0x5c19), 0x75 }, { CCI_REG8(0x5c1a), 0x75 },
++ { CCI_REG8(0x5c1b), 0x75 }, { CCI_REG8(0x5c1c), 0x75 },
++ { CCI_REG8(0x5c1d), 0x75 }, { CCI_REG8(0x5c1e), 0x75 },
++ { CCI_REG8(0x5c1f), 0x75 }, { CCI_REG8(0x5c20), 0x75 },
++ { CCI_REG8(0x5c21), 0x75 }, { CCI_REG8(0x5c22), 0x75 },
++ { CCI_REG8(0x5c23), 0x75 }, { CCI_REG8(0x5c24), 0x75 },
++ { CCI_REG8(0x5c25), 0x75 }, { CCI_REG8(0x5c26), 0x75 },
++ { CCI_REG8(0x5c27), 0x75 }, { CCI_REG8(0x5c28), 0x75 },
++ { CCI_REG8(0x5c29), 0x75 }, { CCI_REG8(0x5c2a), 0x75 },
++ { CCI_REG8(0x5c2b), 0x75 }, { CCI_REG8(0x5c2c), 0x75 },
++ { CCI_REG8(0x5c2d), 0x75 }, { CCI_REG8(0x5c2e), 0x75 },
++ { CCI_REG8(0x5c2f), 0x75 }, { CCI_REG8(0x5c30), 0x75 },
++ { CCI_REG8(0x5c31), 0x75 }, { CCI_REG8(0x5c32), 0x75 },
++ { CCI_REG8(0x5c33), 0x75 }, { CCI_REG8(0x5c34), 0x75 },
++ { CCI_REG8(0x5c35), 0x75 }, { CCI_REG8(0x5c36), 0x75 },
++ { CCI_REG8(0x5c37), 0x75 }, { CCI_REG8(0x5c38), 0x75 },
++ { CCI_REG8(0x5c39), 0x75 }, { CCI_REG8(0x5c3a), 0x75 },
++ { CCI_REG8(0x5c3b), 0x75 }, { CCI_REG8(0x5c3c), 0x75 },
++ { CCI_REG8(0x5c3d), 0x75 }, { CCI_REG8(0x5c3e), 0x75 },
++ { CCI_REG8(0x5c3f), 0x75 }, { CCI_REG8(0x5c40), 0x75 },
++ { CCI_REG8(0x5c41), 0x75 }, { CCI_REG8(0x5c42), 0x75 },
++ { CCI_REG8(0x5c43), 0x75 }, { CCI_REG8(0x5c44), 0x75 },
++ { CCI_REG8(0x5c45), 0x75 }, { CCI_REG8(0x5c46), 0x75 },
++ { CCI_REG8(0x5c47), 0x75 }, { CCI_REG8(0x5c48), 0x75 },
++ { CCI_REG8(0x5c49), 0x75 }, { CCI_REG8(0x5c4a), 0x75 },
++ { CCI_REG8(0x5c4b), 0x75 }, { CCI_REG8(0x5c4c), 0x75 },
++ { CCI_REG8(0x5c4d), 0x75 }, { CCI_REG8(0x5c4e), 0x75 },
++ { CCI_REG8(0x5c4f), 0x75 }, { CCI_REG8(0x5c50), 0x75 },
++ { CCI_REG8(0x5c51), 0x75 }, { CCI_REG8(0x5c52), 0x75 },
++ { CCI_REG8(0x5c53), 0x75 }, { CCI_REG8(0x5c54), 0x75 },
++ { CCI_REG8(0x5c55), 0x75 }, { CCI_REG8(0x5c56), 0x75 },
++ { CCI_REG8(0x5c57), 0x75 }, { CCI_REG8(0x5c58), 0x75 },
++ { CCI_REG8(0x5c59), 0x75 }, { CCI_REG8(0x5c5a), 0x75 },
++ { CCI_REG8(0x5c5b), 0x75 }, { CCI_REG8(0x5c5c), 0x75 },
++ { CCI_REG8(0x5c5d), 0x75 }, { CCI_REG8(0x5c5e), 0x75 },
++ { CCI_REG8(0x5c5f), 0x75 }, { CCI_REG8(0x5c60), 0x75 },
++ { CCI_REG8(0x5c61), 0x75 }, { CCI_REG8(0x5c62), 0x75 },
++ { CCI_REG8(0x5c63), 0x75 }, { CCI_REG8(0x5c64), 0x75 },
++ { CCI_REG8(0x5c65), 0x75 }, { CCI_REG8(0x5c66), 0x75 },
++ { CCI_REG8(0x5c67), 0x75 }, { CCI_REG8(0x5c68), 0x75 },
++ { CCI_REG8(0x5c69), 0x75 }, { CCI_REG8(0x5c6a), 0x75 },
++ { CCI_REG8(0x5c6b), 0x75 }, { CCI_REG8(0x5c6c), 0x75 },
++ { CCI_REG8(0x5c6d), 0x75 }, { CCI_REG8(0x5c6e), 0x75 },
++ { CCI_REG8(0x5c6f), 0x75 }, { CCI_REG8(0x5c70), 0x75 },
++ { CCI_REG8(0x5c71), 0x75 }, { CCI_REG8(0x5c72), 0x75 },
++ { CCI_REG8(0x5c73), 0x75 }, { CCI_REG8(0x5c74), 0x75 },
++ { CCI_REG8(0x5c75), 0x75 }, { CCI_REG8(0x5c76), 0x75 },
++ { CCI_REG8(0x5c77), 0x75 }, { CCI_REG8(0x5c78), 0x75 },
++ { CCI_REG8(0x5c79), 0x75 }, { CCI_REG8(0x5c7a), 0x75 },
++ { CCI_REG8(0x5c7b), 0x75 }, { CCI_REG8(0x5c7c), 0x75 },
++ { CCI_REG8(0x5c7d), 0x75 }, { CCI_REG8(0x5c7e), 0x75 },
++ { CCI_REG8(0x5c7f), 0x75 }, { CCI_REG8(0x5c80), 0x75 },
++ { CCI_REG8(0x5c81), 0x75 }, { CCI_REG8(0x5c82), 0x75 },
++ { CCI_REG8(0x5c83), 0x75 }, { CCI_REG8(0x5c84), 0x75 },
++ { CCI_REG8(0x5c85), 0x75 }, { CCI_REG8(0x5c86), 0x75 },
++ { CCI_REG8(0x5c87), 0x75 }, { CCI_REG8(0x5c88), 0x75 },
++ { CCI_REG8(0x5c89), 0x75 }, { CCI_REG8(0x5c8a), 0x75 },
++ { CCI_REG8(0x5c8b), 0x75 }, { CCI_REG8(0x5c8c), 0x75 },
++ { CCI_REG8(0x5c8d), 0x75 }, { CCI_REG8(0x5c8e), 0x75 },
++ { CCI_REG8(0x5c8f), 0x75 }, { CCI_REG8(0x5c90), 0x75 },
++ { CCI_REG8(0x5c91), 0x75 }, { CCI_REG8(0x5c92), 0x75 },
++ { CCI_REG8(0x5c93), 0x75 }, { CCI_REG8(0x5c94), 0x75 },
++ { CCI_REG8(0x5c95), 0x75 }, { CCI_REG8(0x5c96), 0x75 },
++ { CCI_REG8(0x5c97), 0x75 }, { CCI_REG8(0x5c98), 0x75 },
++ { CCI_REG8(0x5c99), 0x75 }, { CCI_REG8(0x5c9a), 0x75 },
++ { CCI_REG8(0x5c9b), 0x75 }, { CCI_REG8(0x5c9c), 0x75 },
++ { CCI_REG8(0x5c9d), 0x75 }, { CCI_REG8(0x5c9e), 0x75 },
++ { CCI_REG8(0x5c9f), 0x75 }, { CCI_REG8(0x5ca0), 0x75 },
++ { CCI_REG8(0x5ca1), 0x75 }, { CCI_REG8(0x5ca2), 0x75 },
++ { CCI_REG8(0x5ca3), 0x75 }, { CCI_REG8(0x5ca4), 0x75 },
++ { CCI_REG8(0x5ca5), 0x75 }, { CCI_REG8(0x5ca6), 0x75 },
++ { CCI_REG8(0x5ca7), 0x75 }, { CCI_REG8(0x5ca8), 0x75 },
++ { CCI_REG8(0x5ca9), 0x75 }, { CCI_REG8(0x5caa), 0x75 },
++ { CCI_REG8(0x5cab), 0x75 }, { CCI_REG8(0x5cac), 0x75 },
++ { CCI_REG8(0x5cad), 0x75 }, { CCI_REG8(0x5cae), 0x75 },
++ { CCI_REG8(0x5caf), 0x75 }, { CCI_REG8(0x5cb0), 0x75 },
++ { CCI_REG8(0x5cb1), 0x75 }, { CCI_REG8(0x5cb2), 0x75 },
++ { CCI_REG8(0x5cb3), 0x75 }, { CCI_REG8(0x5cb4), 0x75 },
++ { CCI_REG8(0x5cb5), 0x75 }, { CCI_REG8(0x5cb6), 0x75 },
++ { CCI_REG8(0x5cb7), 0x75 }, { CCI_REG8(0x5cb8), 0x75 },
++ { CCI_REG8(0x5cb9), 0x75 }, { CCI_REG8(0x5cba), 0x75 },
++ { CCI_REG8(0x5cbb), 0x75 }, { CCI_REG8(0x5cbc), 0x75 },
++ { CCI_REG8(0x5cbd), 0x75 }, { CCI_REG8(0x5cbe), 0x75 },
++ { CCI_REG8(0x5cbf), 0x75 }, { CCI_REG8(0x5cc0), 0x75 },
++ { CCI_REG8(0x5cc1), 0x75 }, { CCI_REG8(0x5cc2), 0x75 },
++ { CCI_REG8(0x5cc3), 0x75 }, { CCI_REG8(0x5cc4), 0x75 },
++ { CCI_REG8(0x5cc5), 0x75 }, { CCI_REG8(0x5cc6), 0x75 },
++ { CCI_REG8(0x5cc7), 0x75 }, { CCI_REG8(0x5cc8), 0x75 },
++ { CCI_REG8(0x5cc9), 0x75 }, { CCI_REG8(0x5cca), 0x75 },
++ { CCI_REG8(0x5ccb), 0x75 }, { CCI_REG8(0x5ccc), 0x75 },
++ { CCI_REG8(0x5ccd), 0x75 }, { CCI_REG8(0x5cce), 0x75 },
++ { CCI_REG8(0x5ccf), 0x75 }, { CCI_REG8(0x5cd0), 0x75 },
++ { CCI_REG8(0x5cd1), 0x75 }, { CCI_REG8(0x5cd2), 0x75 },
++ { CCI_REG8(0x5cd3), 0x75 }, { CCI_REG8(0x5cd4), 0x75 },
++ { CCI_REG8(0x5cd5), 0x75 }, { CCI_REG8(0x5cd6), 0x75 },
++ { CCI_REG8(0x5cd7), 0x75 }, { CCI_REG8(0x5cd8), 0x75 },
++ { CCI_REG8(0x5cd9), 0x75 }, { CCI_REG8(0x5cda), 0x75 },
++ { CCI_REG8(0x5cdb), 0x75 }, { CCI_REG8(0x5cdc), 0x75 },
++ { CCI_REG8(0x5cdd), 0x75 }, { CCI_REG8(0x5cde), 0x75 },
++ { CCI_REG8(0x5cdf), 0x75 }, { CCI_REG8(0x5ce0), 0x75 },
++ { CCI_REG8(0x5ce1), 0x75 }, { CCI_REG8(0x5ce2), 0x75 },
++ { CCI_REG8(0x5ce3), 0x75 }, { CCI_REG8(0x5ce4), 0x75 },
++ { CCI_REG8(0x5ce5), 0x75 }, { CCI_REG8(0x5ce6), 0x75 },
++ { CCI_REG8(0x5ce7), 0x75 }, { CCI_REG8(0x5ce8), 0x75 },
++ { CCI_REG8(0x5ce9), 0x75 }, { CCI_REG8(0x5cea), 0x75 },
++ { CCI_REG8(0x5ceb), 0x75 }, { CCI_REG8(0x5cec), 0x75 },
++ { CCI_REG8(0x5ced), 0x75 }, { CCI_REG8(0x5cee), 0x75 },
++ { CCI_REG8(0x5cef), 0x75 }, { CCI_REG8(0x5cf0), 0x75 },
++ { CCI_REG8(0x5cf1), 0x75 }, { CCI_REG8(0x5cf2), 0x75 },
++ { CCI_REG8(0x5cf3), 0x75 }, { CCI_REG8(0x5cf4), 0x75 },
++ { CCI_REG8(0x5cf5), 0x75 }, { CCI_REG8(0x5cf6), 0x75 },
++ { CCI_REG8(0x5cf7), 0x75 }, { CCI_REG8(0x5cf8), 0x75 },
++ { CCI_REG8(0x5cf9), 0x75 }, { CCI_REG8(0x5cfa), 0x75 },
++ { CCI_REG8(0x5cfb), 0x75 }, { CCI_REG8(0x5cfc), 0x75 },
++ { CCI_REG8(0x5cfd), 0x75 }, { CCI_REG8(0x5cfe), 0x75 },
++ { CCI_REG8(0x5cff), 0x75 }, { CCI_REG8(0x5d00), 0x75 },
++ { CCI_REG8(0x5d01), 0x75 }, { CCI_REG8(0x5d02), 0x75 },
++ { CCI_REG8(0x5d03), 0x75 }, { CCI_REG8(0x5d04), 0x75 },
++ { CCI_REG8(0x5d05), 0x75 }, { CCI_REG8(0x5d06), 0x75 },
++ { CCI_REG8(0x5d07), 0x75 }, { CCI_REG8(0x5d08), 0x75 },
++ { CCI_REG8(0x5d09), 0x75 }, { CCI_REG8(0x5d0a), 0x75 },
++ { CCI_REG8(0x5d0b), 0x75 }, { CCI_REG8(0x5d0c), 0x75 },
++ { CCI_REG8(0x5d0d), 0x75 }, { CCI_REG8(0x5d0e), 0x75 },
++ { CCI_REG8(0x5d0f), 0x75 }, { CCI_REG8(0x5d10), 0x75 },
++ { CCI_REG8(0x5d11), 0x75 }, { CCI_REG8(0x5d12), 0x75 },
++ { CCI_REG8(0x5d13), 0x75 }, { CCI_REG8(0x5d14), 0x75 },
++ { CCI_REG8(0x5d15), 0x75 }, { CCI_REG8(0x5d16), 0x75 },
++ { CCI_REG8(0x5d17), 0x75 }, { CCI_REG8(0x5d18), 0x75 },
++ { CCI_REG8(0x5d19), 0x75 }, { CCI_REG8(0x5d1a), 0x75 },
++ { CCI_REG8(0x5d1b), 0x75 }, { CCI_REG8(0x5d1c), 0x75 },
++ { CCI_REG8(0x5d1d), 0x75 }, { CCI_REG8(0x5d1e), 0x75 },
++ { CCI_REG8(0x5d1f), 0x75 }, { CCI_REG8(0x5d20), 0x75 },
++ { CCI_REG8(0x5d21), 0x75 }, { CCI_REG8(0x5d22), 0x75 },
++ { CCI_REG8(0x5d23), 0x75 }, { CCI_REG8(0x5d24), 0x75 },
++ { CCI_REG8(0x5d25), 0x75 }, { CCI_REG8(0x5d26), 0x75 },
++ { CCI_REG8(0x5d27), 0x75 }, { CCI_REG8(0x5d28), 0x75 },
++ { CCI_REG8(0x5d29), 0x75 }, { CCI_REG8(0x5d2a), 0x75 },
++ { CCI_REG8(0x5d2b), 0x75 }, { CCI_REG8(0x5d2c), 0x75 },
++ { CCI_REG8(0x5d2d), 0x75 }, { CCI_REG8(0x5d2e), 0x75 },
++ { CCI_REG8(0x5d2f), 0x75 }, { CCI_REG8(0x5d30), 0x75 },
++ { CCI_REG8(0x5d31), 0x75 }, { CCI_REG8(0x5d32), 0x75 },
++ { CCI_REG8(0x5d33), 0x75 }, { CCI_REG8(0x5d34), 0x75 },
++ { CCI_REG8(0x5d35), 0x75 }, { CCI_REG8(0x5d36), 0x75 },
++ { CCI_REG8(0x5d37), 0x75 }, { CCI_REG8(0x5d38), 0x75 },
++ { CCI_REG8(0x5d39), 0x75 }, { CCI_REG8(0x5d3a), 0x75 },
++ { CCI_REG8(0x5d3b), 0x75 }, { CCI_REG8(0x5d3c), 0x75 },
++ { CCI_REG8(0x5d3d), 0x75 }, { CCI_REG8(0x5d3e), 0x75 },
++ { CCI_REG8(0x5d3f), 0x75 }, { CCI_REG8(0x5d40), 0x75 },
++ { CCI_REG8(0x5d41), 0x75 }, { CCI_REG8(0x5d42), 0x75 },
++ { CCI_REG8(0x5d43), 0x75 }, { CCI_REG8(0x5d44), 0x75 },
++ { CCI_REG8(0x5d45), 0x75 }, { CCI_REG8(0x5d46), 0x75 },
++ { CCI_REG8(0x5d47), 0x75 }, { CCI_REG8(0x5d48), 0x75 },
++ { CCI_REG8(0x5d49), 0x75 }, { CCI_REG8(0x5d4a), 0x75 },
++ { CCI_REG8(0x5d4b), 0x75 }, { CCI_REG8(0x5d4c), 0x75 },
++ { CCI_REG8(0x5d4d), 0x75 }, { CCI_REG8(0x5d4e), 0x75 },
++ { CCI_REG8(0x5d4f), 0x75 }, { CCI_REG8(0x5d50), 0x75 },
++ { CCI_REG8(0x5d51), 0x75 }, { CCI_REG8(0x5d52), 0x75 },
++ { CCI_REG8(0x5d53), 0x75 }, { CCI_REG8(0x5d54), 0x75 },
++ { CCI_REG8(0x5d55), 0x75 }, { CCI_REG8(0x5d56), 0x75 },
++ { CCI_REG8(0x5d57), 0x75 }, { CCI_REG8(0x5d58), 0x75 },
++ { CCI_REG8(0x5d59), 0x75 }, { CCI_REG8(0x5d5a), 0x75 },
++ { CCI_REG8(0x5d5b), 0x75 }, { CCI_REG8(0x5d5c), 0x75 },
++ { CCI_REG8(0x5d5d), 0x75 }, { CCI_REG8(0x5d5e), 0x75 },
++ { CCI_REG8(0x5d5f), 0x75 }, { CCI_REG8(0x5d60), 0x75 },
++ { CCI_REG8(0x5d61), 0x75 }, { CCI_REG8(0x5d62), 0x75 },
++ { CCI_REG8(0x5d63), 0x75 }, { CCI_REG8(0x5d64), 0x75 },
++ { CCI_REG8(0x5d65), 0x75 }, { CCI_REG8(0x5d66), 0x75 },
++ { CCI_REG8(0x5d67), 0x75 }, { CCI_REG8(0x5d68), 0x75 },
++ { CCI_REG8(0x5d69), 0x75 }, { CCI_REG8(0x5d6a), 0x75 },
++ { CCI_REG8(0x5d6b), 0x75 }, { CCI_REG8(0x5d6c), 0x75 },
++ { CCI_REG8(0x5d6d), 0x75 }, { CCI_REG8(0x5d6e), 0x75 },
++ { CCI_REG8(0x5d6f), 0x75 }, { CCI_REG8(0x5d70), 0x75 },
++ { CCI_REG8(0x5d71), 0x75 }, { CCI_REG8(0x5d72), 0x75 },
++ { CCI_REG8(0x5d73), 0x75 }, { CCI_REG8(0x5d74), 0x75 },
++ { CCI_REG8(0x5d75), 0x75 }, { CCI_REG8(0x5d76), 0x75 },
++ { CCI_REG8(0x5d77), 0x75 }, { CCI_REG8(0x5d78), 0x75 },
++ { CCI_REG8(0x5d79), 0x75 }, { CCI_REG8(0x5d7a), 0x75 },
++ { CCI_REG8(0x5d7b), 0x75 }, { CCI_REG8(0x5d7c), 0x75 },
++ { CCI_REG8(0x5d7d), 0x75 }, { CCI_REG8(0x5d7e), 0x75 },
++ { CCI_REG8(0x5d7f), 0x75 }, { CCI_REG8(0x5d80), 0x75 },
++ { CCI_REG8(0x5d81), 0x75 }, { CCI_REG8(0x5d82), 0x75 },
++ { CCI_REG8(0x5d83), 0x75 }, { CCI_REG8(0x5d84), 0x75 },
++ { CCI_REG8(0x5d85), 0x75 }, { CCI_REG8(0x5d86), 0x75 },
++ { CCI_REG8(0x5d87), 0x75 }, { CCI_REG8(0x5d88), 0x75 },
++ { CCI_REG8(0x5d89), 0x75 }, { CCI_REG8(0x5d8a), 0x75 },
++ { CCI_REG8(0x5d8b), 0x75 }, { CCI_REG8(0x5d8c), 0x75 },
++ { CCI_REG8(0x5d8d), 0x75 }, { CCI_REG8(0x5d8e), 0x75 },
++ { CCI_REG8(0x5d8f), 0x75 }, { CCI_REG8(0x5d90), 0x75 },
++ { CCI_REG8(0x5d91), 0x75 }, { CCI_REG8(0x5d92), 0x75 },
++ { CCI_REG8(0x5d93), 0x75 }, { CCI_REG8(0x5d94), 0x75 },
++ { CCI_REG8(0x5d95), 0x75 }, { CCI_REG8(0x5d96), 0x75 },
++ { CCI_REG8(0x5d97), 0x75 }, { CCI_REG8(0x5d98), 0x75 },
++ { CCI_REG8(0x5d99), 0x75 }, { CCI_REG8(0x5d9a), 0x75 },
++ { CCI_REG8(0x5d9b), 0x75 }, { CCI_REG8(0x5d9c), 0x75 },
++ { CCI_REG8(0x5d9d), 0x75 }, { CCI_REG8(0x5d9e), 0x75 },
++ { CCI_REG8(0x5d9f), 0x75 }, { CCI_REG8(0x5da0), 0x75 },
++ { CCI_REG8(0x5da1), 0x75 }, { CCI_REG8(0x5da2), 0x75 },
++ { CCI_REG8(0x5da3), 0x75 }, { CCI_REG8(0x5da4), 0x75 },
++ { CCI_REG8(0x5da5), 0x75 }, { CCI_REG8(0x5da6), 0x75 },
++ { CCI_REG8(0x5da7), 0x75 }, { CCI_REG8(0x5da8), 0x75 },
++ { CCI_REG8(0x5da9), 0x75 }, { CCI_REG8(0x5daa), 0x75 },
++ { CCI_REG8(0x5dab), 0x75 }, { CCI_REG8(0x5dac), 0x75 },
++ { CCI_REG8(0x5dad), 0x75 }, { CCI_REG8(0x5dae), 0x75 },
++ { CCI_REG8(0x5daf), 0x75 }, { CCI_REG8(0x5db0), 0x75 },
++ { CCI_REG8(0x5db1), 0x75 }, { CCI_REG8(0x5db2), 0x75 },
++ { CCI_REG8(0x5db3), 0x75 }, { CCI_REG8(0x5db4), 0x75 },
++ { CCI_REG8(0x5db5), 0x75 }, { CCI_REG8(0x5db6), 0x75 },
++ { CCI_REG8(0x5db7), 0x75 }, { CCI_REG8(0x5db8), 0x75 },
++ { CCI_REG8(0x5db9), 0x75 }, { CCI_REG8(0x5dba), 0x75 },
++ { CCI_REG8(0x5dbb), 0x75 }, { CCI_REG8(0x5dbc), 0x75 },
++ { CCI_REG8(0x5dbd), 0x75 }, { CCI_REG8(0x5dbe), 0x75 },
++ { CCI_REG8(0x5dbf), 0x75 }, { CCI_REG8(0x5dc0), 0x75 },
++ { CCI_REG8(0x5dc1), 0x75 }, { CCI_REG8(0x5dc2), 0x75 },
++ { CCI_REG8(0x5dc3), 0x75 }, { CCI_REG8(0x5dc4), 0x75 },
++ { CCI_REG8(0x5dc5), 0x75 }, { CCI_REG8(0x5dc6), 0x75 },
++ { CCI_REG8(0x5dc7), 0x75 }, { CCI_REG8(0x5dc8), 0x75 },
++ { CCI_REG8(0x5dc9), 0x75 }, { CCI_REG8(0x5dca), 0x75 },
++ { CCI_REG8(0x5dcb), 0x75 }, { CCI_REG8(0x5dcc), 0x75 },
++ { CCI_REG8(0x5dcd), 0x75 }, { CCI_REG8(0x5dce), 0x75 },
++ { CCI_REG8(0x5dcf), 0x75 }, { CCI_REG8(0x5dd0), 0x75 },
++ { CCI_REG8(0x5dd1), 0x75 }, { CCI_REG8(0x5dd2), 0x75 },
++ { CCI_REG8(0x5dd3), 0x75 }, { CCI_REG8(0x5dd4), 0x75 },
++ { CCI_REG8(0x5dd5), 0x75 }, { CCI_REG8(0x5dd6), 0x75 },
++ { CCI_REG8(0x5dd7), 0x75 }, { CCI_REG8(0x5dd8), 0x75 },
++ { CCI_REG8(0x5dd9), 0x75 }, { CCI_REG8(0x5dda), 0x75 },
++ { CCI_REG8(0x5ddb), 0x75 }, { CCI_REG8(0x5ddc), 0x75 },
++ { CCI_REG8(0x5ddd), 0x75 }, { CCI_REG8(0x5dde), 0x75 },
++ { CCI_REG8(0x5ddf), 0x75 }, { CCI_REG8(0x5de0), 0x75 },
++ { CCI_REG8(0x5de1), 0x75 }, { CCI_REG8(0x5de2), 0x75 },
++ { CCI_REG8(0x5de3), 0x75 }, { CCI_REG8(0x5de4), 0x75 },
++ { CCI_REG8(0x5de5), 0x75 }, { CCI_REG8(0x5de6), 0x75 },
++ { CCI_REG8(0x5de7), 0x75 }, { CCI_REG8(0x5de8), 0x75 },
++ { CCI_REG8(0x5de9), 0x75 }, { CCI_REG8(0x5dea), 0x75 },
++ { CCI_REG8(0x5deb), 0x75 }, { CCI_REG8(0x5dec), 0x75 },
++ { CCI_REG8(0x5ded), 0x75 }, { CCI_REG8(0x5dee), 0x75 },
++ { CCI_REG8(0x5def), 0x75 }, { CCI_REG8(0x5df0), 0x75 },
++ { CCI_REG8(0x5df1), 0x75 }, { CCI_REG8(0x5df2), 0x75 },
++ { CCI_REG8(0x5df3), 0x75 }, { CCI_REG8(0x5df4), 0x75 },
++ { CCI_REG8(0x5df5), 0x75 }, { CCI_REG8(0x5df6), 0x75 },
++ { CCI_REG8(0x5df7), 0x75 }, { CCI_REG8(0x5df8), 0x75 },
++ { CCI_REG8(0x5df9), 0x75 }, { CCI_REG8(0x5dfa), 0x75 },
++ { CCI_REG8(0x5dfb), 0x75 }, { CCI_REG8(0x5dfc), 0x75 },
++ { CCI_REG8(0x5dfd), 0x75 }, { CCI_REG8(0x5dfe), 0x75 },
++ { CCI_REG8(0x5dff), 0x75 }, { CCI_REG8(0x5e00), 0x75 },
++ { CCI_REG8(0x5e01), 0x75 }, { CCI_REG8(0x5e02), 0x75 },
++ { CCI_REG8(0x5e03), 0x75 }, { CCI_REG8(0x5e04), 0x75 },
++ { CCI_REG8(0x5e05), 0x75 }, { CCI_REG8(0x5e06), 0x75 },
++ { CCI_REG8(0x5e07), 0x75 }, { CCI_REG8(0x5e08), 0x75 },
++ { CCI_REG8(0x5e09), 0x75 }, { CCI_REG8(0x5e0a), 0x75 },
++ { CCI_REG8(0x5e0b), 0x75 }, { CCI_REG8(0x5e0c), 0x75 },
++ { CCI_REG8(0x5e0d), 0x75 }, { CCI_REG8(0x5e0e), 0x75 },
++ { CCI_REG8(0x5e0f), 0x75 }, { CCI_REG8(0x5e10), 0x75 },
++ { CCI_REG8(0x5e11), 0x75 }, { CCI_REG8(0x5e12), 0x75 },
++ { CCI_REG8(0x5e13), 0x75 }, { CCI_REG8(0x5e14), 0x75 },
++ { CCI_REG8(0x5e15), 0x75 }, { CCI_REG8(0x5e16), 0x75 },
++ { CCI_REG8(0x5e17), 0x75 }, { CCI_REG8(0x5e18), 0x75 },
++ { CCI_REG8(0x5e19), 0x75 }, { CCI_REG8(0x5e1a), 0x75 },
++ { CCI_REG8(0x5e1b), 0x75 }, { CCI_REG8(0x5e1c), 0x75 },
++ { CCI_REG8(0x5e1d), 0x75 }, { CCI_REG8(0x5e1e), 0x75 },
++ { CCI_REG8(0x5e1f), 0x75 }, { CCI_REG8(0x5e20), 0x75 },
++ { CCI_REG8(0x5e21), 0x75 }, { CCI_REG8(0x5e22), 0x75 },
++ { CCI_REG8(0x5e23), 0x75 }, { CCI_REG8(0x5e24), 0x75 },
++ { CCI_REG8(0x5e25), 0x75 }, { CCI_REG8(0x5e26), 0x75 },
++ { CCI_REG8(0x5e27), 0x75 }, { CCI_REG8(0x5e28), 0x75 },
++ { CCI_REG8(0x5e29), 0x75 }, { CCI_REG8(0x5e2a), 0x75 },
++ { CCI_REG8(0x5e2b), 0x75 }, { CCI_REG8(0x5e2c), 0x75 },
++ { CCI_REG8(0x5e2d), 0x75 }, { CCI_REG8(0x5e2e), 0x75 },
++ { CCI_REG8(0x5e2f), 0x75 }, { CCI_REG8(0x5e30), 0x75 },
++ { CCI_REG8(0x5e31), 0x75 }, { CCI_REG8(0x5e32), 0x75 },
++ { CCI_REG8(0x5e33), 0x75 }, { CCI_REG8(0x5e34), 0x75 },
++ { CCI_REG8(0x5e35), 0x75 }, { CCI_REG8(0x5e36), 0x75 },
++ { CCI_REG8(0x5e37), 0x75 }, { CCI_REG8(0x5e38), 0x75 },
++ { CCI_REG8(0x5e39), 0x75 }, { CCI_REG8(0x5e3a), 0x75 },
++ { CCI_REG8(0x5e3b), 0x75 }, { CCI_REG8(0x5e3c), 0x75 },
++ { CCI_REG8(0x5e3d), 0x75 }, { CCI_REG8(0x5e3e), 0x75 },
++ { CCI_REG8(0x5e3f), 0x75 }, { CCI_REG8(0x5e40), 0x75 },
++ { CCI_REG8(0x5e41), 0x75 }, { CCI_REG8(0x5e42), 0x75 },
++ { CCI_REG8(0x5e43), 0x75 }, { CCI_REG8(0x5e44), 0x75 },
++ { CCI_REG8(0x5e45), 0x75 }, { CCI_REG8(0x5e46), 0x75 },
++ { CCI_REG8(0x5e47), 0x75 }, { CCI_REG8(0x5e48), 0x75 },
++ { CCI_REG8(0x5e49), 0x75 }, { CCI_REG8(0x5e4a), 0x75 },
++ { CCI_REG8(0x5e4b), 0x75 }, { CCI_REG8(0x5e4c), 0x75 },
++ { CCI_REG8(0x5e4d), 0x75 }, { CCI_REG8(0x5e4e), 0x75 },
++ { CCI_REG8(0x5e4f), 0x75 }, { CCI_REG8(0x5e50), 0x75 },
++ { CCI_REG8(0x5e51), 0x75 }, { CCI_REG8(0x5e52), 0x75 },
++ { CCI_REG8(0x5e53), 0x75 }, { CCI_REG8(0x5e54), 0x75 },
++ { CCI_REG8(0x5e55), 0x75 }, { CCI_REG8(0x5e56), 0x75 },
++ { CCI_REG8(0x5e57), 0x75 }, { CCI_REG8(0x5e58), 0x75 },
++ { CCI_REG8(0x5e59), 0x75 }, { CCI_REG8(0x5e5a), 0x75 },
++ { CCI_REG8(0x5e5b), 0x75 }, { CCI_REG8(0x5e5c), 0x75 },
++ { CCI_REG8(0x5e5d), 0x75 }, { CCI_REG8(0x5e5e), 0x75 },
++ { CCI_REG8(0x5e5f), 0x75 }, { CCI_REG8(0x5e60), 0x75 },
++ { CCI_REG8(0x5e61), 0x75 }, { CCI_REG8(0x5e62), 0x75 },
++ { CCI_REG8(0x5e63), 0x75 }, { CCI_REG8(0x5e64), 0x75 },
++ { CCI_REG8(0x5e65), 0x75 }, { CCI_REG8(0x5e66), 0x75 },
++ { CCI_REG8(0x5e67), 0x75 }, { CCI_REG8(0x5e68), 0x75 },
++ { CCI_REG8(0x5e69), 0x75 }, { CCI_REG8(0x5e6a), 0x75 },
++ { CCI_REG8(0x5e6b), 0x75 }, { CCI_REG8(0x5e6c), 0x75 },
++ { CCI_REG8(0x5e6d), 0x75 }, { CCI_REG8(0x5e6e), 0x75 },
++ { CCI_REG8(0x5e6f), 0x75 }, { CCI_REG8(0x5e70), 0x75 },
++ { CCI_REG8(0x5e71), 0x75 }, { CCI_REG8(0x5e72), 0x75 },
++ { CCI_REG8(0x5e73), 0x75 }, { CCI_REG8(0x5e74), 0x75 },
++ { CCI_REG8(0x5e75), 0x75 }, { CCI_REG8(0x5e76), 0x75 },
++ { CCI_REG8(0x5e77), 0x75 }, { CCI_REG8(0x5e78), 0x75 },
++ { CCI_REG8(0x5e79), 0x75 }, { CCI_REG8(0x5e7a), 0x75 },
++ { CCI_REG8(0x5e7b), 0x75 }, { CCI_REG8(0x5e7c), 0x75 },
++ { CCI_REG8(0x5e7d), 0x75 }, { CCI_REG8(0x5e7e), 0x75 },
++ { CCI_REG8(0x5e7f), 0x75 }, { CCI_REG8(0x5e80), 0x75 },
++ { CCI_REG8(0x5e81), 0x75 }, { CCI_REG8(0x5e82), 0x75 },
++ { CCI_REG8(0x5e83), 0x75 }, { CCI_REG8(0x5e84), 0x75 },
++ { CCI_REG8(0x5e85), 0x75 }, { CCI_REG8(0x5e86), 0x75 },
++ { CCI_REG8(0x5e87), 0x75 }, { CCI_REG8(0x5e88), 0x75 },
++ { CCI_REG8(0x5e89), 0x75 }, { CCI_REG8(0x5e8a), 0x75 },
++ { CCI_REG8(0x5e8b), 0x75 }, { CCI_REG8(0x5e8c), 0x75 },
++ { CCI_REG8(0x5e8d), 0x75 }, { CCI_REG8(0x5e8e), 0x75 },
++ { CCI_REG8(0x5e8f), 0x75 }, { CCI_REG8(0x5e90), 0x75 },
++ { CCI_REG8(0x5e91), 0x75 }, { CCI_REG8(0x5e92), 0x75 },
++ { CCI_REG8(0x5e93), 0x75 }, { CCI_REG8(0x5e94), 0x75 },
++ { CCI_REG8(0x5e95), 0x75 }, { CCI_REG8(0x5e96), 0x75 },
++ { CCI_REG8(0x5e97), 0x75 }, { CCI_REG8(0x5e98), 0x75 },
++ { CCI_REG8(0x5e99), 0x75 }, { CCI_REG8(0x5e9a), 0x75 },
++ { CCI_REG8(0x5e9b), 0x75 }, { CCI_REG8(0x5e9c), 0x75 },
++ { CCI_REG8(0x5e9d), 0x75 }, { CCI_REG8(0x5e9e), 0x75 },
++ { CCI_REG8(0x5e9f), 0x75 }, { CCI_REG8(0x5ea0), 0x75 },
++ { CCI_REG8(0x5ea1), 0x75 }, { CCI_REG8(0x5ea2), 0x75 },
++ { CCI_REG8(0x5ea3), 0x75 }, { CCI_REG8(0x5ea4), 0x75 },
++ { CCI_REG8(0x5ea5), 0x75 }, { CCI_REG8(0x5ea6), 0x75 },
++ { CCI_REG8(0x5ea7), 0x75 }, { CCI_REG8(0x5ea8), 0x75 },
++ { CCI_REG8(0x5ea9), 0x75 }, { CCI_REG8(0x5eaa), 0x75 },
++ { CCI_REG8(0x5eab), 0x75 }, { CCI_REG8(0x5eac), 0x75 },
++ { CCI_REG8(0x5ead), 0x75 }, { CCI_REG8(0x5eae), 0x75 },
++ { CCI_REG8(0x5eaf), 0x75 }, { CCI_REG8(0x5eb0), 0x75 },
++ { CCI_REG8(0x5eb1), 0x75 }, { CCI_REG8(0x5eb2), 0x75 },
++ { CCI_REG8(0x5eb3), 0x75 }, { CCI_REG8(0x5eb4), 0x75 },
++ { CCI_REG8(0x5eb5), 0x75 }, { CCI_REG8(0x5eb6), 0x75 },
++ { CCI_REG8(0x5eb7), 0x75 }, { CCI_REG8(0x5eb8), 0x75 },
++ { CCI_REG8(0x5eb9), 0x75 }, { CCI_REG8(0x5eba), 0x75 },
++ { CCI_REG8(0x5ebb), 0x75 }, { CCI_REG8(0x5ebc), 0x75 },
++ { CCI_REG8(0x5ebd), 0x75 }, { CCI_REG8(0x5ebe), 0x75 },
++ { CCI_REG8(0x5ebf), 0x75 }, { CCI_REG8(0x5ec0), 0x75 },
++ { CCI_REG8(0x5ec1), 0x75 }, { CCI_REG8(0x5ec2), 0x75 },
++ { CCI_REG8(0x5ec3), 0x75 }, { CCI_REG8(0x5ec4), 0x75 },
++ { CCI_REG8(0x5ec5), 0x75 }, { CCI_REG8(0x5ec6), 0x75 },
++ { CCI_REG8(0x5ec7), 0x75 }, { CCI_REG8(0x5ec8), 0x75 },
++ { CCI_REG8(0x5ec9), 0x75 }, { CCI_REG8(0x5eca), 0x75 },
++ { CCI_REG8(0x5ecb), 0x75 }, { CCI_REG8(0x5ecc), 0x75 },
++ { CCI_REG8(0x5ecd), 0x75 }, { CCI_REG8(0x5ece), 0x75 },
++ { CCI_REG8(0x5ecf), 0x75 }, { CCI_REG8(0x5ed0), 0x75 },
++ { CCI_REG8(0x5ed1), 0x75 }, { CCI_REG8(0x5ed2), 0x75 },
++ { CCI_REG8(0x5ed3), 0x75 }, { CCI_REG8(0x5ed4), 0x75 },
++ { CCI_REG8(0x5ed5), 0x75 }, { CCI_REG8(0x5ed6), 0x75 },
++ { CCI_REG8(0x5ed7), 0x75 }, { CCI_REG8(0x5ed8), 0x75 },
++ { CCI_REG8(0x5ed9), 0x75 }, { CCI_REG8(0x5eda), 0x75 },
++ { CCI_REG8(0x5edb), 0x75 }, { CCI_REG8(0x5edc), 0x75 },
++ { CCI_REG8(0x5edd), 0x75 }, { CCI_REG8(0x5ede), 0x75 },
++ { CCI_REG8(0x5edf), 0x75 }, { CCI_REG8(0xfff9), 0x08 },
++ { CCI_REG8(0x1570), 0x00 }, { CCI_REG8(0x15d0), 0x00 },
++ { CCI_REG8(0x15a0), 0x02 }, { CCI_REG8(0x15a1), 0x00 },
++ { CCI_REG8(0x15a2), 0x02 }, { CCI_REG8(0x15a3), 0x76 },
++ { CCI_REG8(0x15a4), 0x03 }, { CCI_REG8(0x15a5), 0x08 },
++ { CCI_REG8(0x15a6), 0x00 }, { CCI_REG8(0x15a7), 0x60 },
++ { CCI_REG8(0x15a8), 0x01 }, { CCI_REG8(0x15a9), 0x00 },
++ { CCI_REG8(0x15aa), 0x02 }, { CCI_REG8(0x15ab), 0x00 },
++ { CCI_REG8(0x1600), 0x02 }, { CCI_REG8(0x1601), 0x00 },
++ { CCI_REG8(0x1602), 0x02 }, { CCI_REG8(0x1603), 0x76 },
++ { CCI_REG8(0x1604), 0x03 }, { CCI_REG8(0x1605), 0x08 },
++ { CCI_REG8(0x1606), 0x00 }, { CCI_REG8(0x1607), 0x60 },
++ { CCI_REG8(0x1608), 0x01 }, { CCI_REG8(0x1609), 0x00 },
++ { CCI_REG8(0x160a), 0x02 }, { CCI_REG8(0x160b), 0x00 },
++ { CCI_REG8(0x1633), 0x03 }, { CCI_REG8(0x1634), 0x01 },
++ { CCI_REG8(0x163c), 0x3a }, { CCI_REG8(0x163d), 0x01 },
++ { CCI_REG8(0x1648), 0x32 }, { CCI_REG8(0x1658), 0x01 },
++ { CCI_REG8(0x1659), 0x01 }, { CCI_REG8(0x165f), 0x01 },
++ { CCI_REG8(0x1677), 0x01 }, { CCI_REG8(0x1690), 0x08 },
++ { CCI_REG8(0x1691), 0x00 }, { CCI_REG8(0x1692), 0x20 },
++ { CCI_REG8(0x1693), 0x00 }, { CCI_REG8(0x1694), 0x10 },
++ { CCI_REG8(0x1695), 0x14 }, { CCI_REG8(0x1696), 0x10 },
++ { CCI_REG8(0x1697), 0x0e }, { CCI_REG8(0x1730), 0x01 },
++ { CCI_REG8(0x1732), 0x00 }, { CCI_REG8(0x1733), 0x10 },
++ { CCI_REG8(0x1734), 0x01 }, { CCI_REG8(0x1735), 0x00 },
++ { CCI_REG8(0x1748), 0x01 }, { CCI_REG8(0xfff9), 0x06 },
++ { CCI_REG8(0x5000), 0xff }, { CCI_REG8(0x5001), 0x3d },
++ { CCI_REG8(0x5002), 0xf5 }, { CCI_REG8(0x5004), 0x80 },
++ { CCI_REG8(0x5006), 0x04 }, { CCI_REG8(0x5061), 0x20 },
++ { CCI_REG8(0x5063), 0x20 }, { CCI_REG8(0x5064), 0x24 },
++ { CCI_REG8(0x5065), 0x00 }, { CCI_REG8(0x5066), 0x1b },
++ { CCI_REG8(0x5067), 0x00 }, { CCI_REG8(0x5068), 0x03 },
++ { CCI_REG8(0x5069), 0x10 }, { CCI_REG8(0x506a), 0x20 },
++ { CCI_REG8(0x506b), 0x04 }, { CCI_REG8(0x506c), 0x04 },
++ { CCI_REG8(0x506d), 0x0c }, { CCI_REG8(0x506e), 0x0c },
++ { CCI_REG8(0x506f), 0x04 }, { CCI_REG8(0x5070), 0x0c },
++ { CCI_REG8(0x5071), 0x14 }, { CCI_REG8(0x5072), 0x1c },
++ { CCI_REG8(0x5073), 0x01 }, { CCI_REG8(0x5074), 0x01 },
++ { CCI_REG8(0x5075), 0xbe }, { CCI_REG8(0x5083), 0x00 },
++ { CCI_REG8(0x5114), 0x03 }, { CCI_REG8(0x51b0), 0x00 },
++ { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x02 },
++ { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 },
++ { CCI_REG8(0x51b8), 0x00 }, { CCI_REG8(0x51b9), 0x70 },
++ { CCI_REG8(0x51ba), 0x00 }, { CCI_REG8(0x51bb), 0x10 },
++ { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 },
++ { CCI_REG8(0x51d2), 0xff }, { CCI_REG8(0x51d3), 0x1c },
++ { CCI_REG8(0x5250), 0x34 }, { CCI_REG8(0x5251), 0x00 },
++ { CCI_REG8(0x525b), 0x00 }, { CCI_REG8(0x525d), 0x00 },
++ { CCI_REG8(0x527a), 0x00 }, { CCI_REG8(0x527b), 0x38 },
++ { CCI_REG8(0x527c), 0x00 }, { CCI_REG8(0x527d), 0x4b },
++ { CCI_REG8(0x5286), 0x1b }, { CCI_REG8(0x5287), 0x40 },
++ { CCI_REG8(0x5290), 0x00 }, { CCI_REG8(0x5291), 0x50 },
++ { CCI_REG8(0x5292), 0x00 }, { CCI_REG8(0x5293), 0x50 },
++ { CCI_REG8(0x5294), 0x00 }, { CCI_REG8(0x5295), 0x50 },
++ { CCI_REG8(0x5296), 0x00 }, { CCI_REG8(0x5297), 0x50 },
++ { CCI_REG8(0x5298), 0x00 }, { CCI_REG8(0x5299), 0x50 },
++ { CCI_REG8(0x529a), 0x01 }, { CCI_REG8(0x529b), 0x00 },
++ { CCI_REG8(0x529c), 0x01 }, { CCI_REG8(0x529d), 0x00 },
++ { CCI_REG8(0x529e), 0x00 }, { CCI_REG8(0x529f), 0x50 },
++ { CCI_REG8(0x52a0), 0x00 }, { CCI_REG8(0x52a1), 0x50 },
++ { CCI_REG8(0x52a2), 0x01 }, { CCI_REG8(0x52a3), 0x00 },
++ { CCI_REG8(0x52a4), 0x01 }, { CCI_REG8(0x52a5), 0x00 },
++ { CCI_REG8(0x52a6), 0x00 }, { CCI_REG8(0x52a7), 0x50 },
++ { CCI_REG8(0x52a8), 0x00 }, { CCI_REG8(0x52a9), 0x50 },
++ { CCI_REG8(0x52aa), 0x00 }, { CCI_REG8(0x52ab), 0x50 },
++ { CCI_REG8(0x52ac), 0x00 }, { CCI_REG8(0x52ad), 0x50 },
++ { CCI_REG8(0x52ae), 0x00 }, { CCI_REG8(0x52af), 0x50 },
++ { CCI_REG8(0x52b0), 0x00 }, { CCI_REG8(0x52b1), 0x50 },
++ { CCI_REG8(0x52b2), 0x00 }, { CCI_REG8(0x52b3), 0x50 },
++ { CCI_REG8(0x52b4), 0x00 }, { CCI_REG8(0x52b5), 0x50 },
++ { CCI_REG8(0x52b6), 0x00 }, { CCI_REG8(0x52b7), 0x50 },
++ { CCI_REG8(0x52b8), 0x00 }, { CCI_REG8(0x52b9), 0x50 },
++ { CCI_REG8(0x52ba), 0x01 }, { CCI_REG8(0x52bb), 0x00 },
++ { CCI_REG8(0x52bc), 0x01 }, { CCI_REG8(0x52bd), 0x00 },
++ { CCI_REG8(0x52be), 0x00 }, { CCI_REG8(0x52bf), 0x50 },
++ { CCI_REG8(0x52c0), 0x00 }, { CCI_REG8(0x52c1), 0x50 },
++ { CCI_REG8(0x52c2), 0x01 }, { CCI_REG8(0x52c3), 0x00 },
++ { CCI_REG8(0x52c4), 0x01 }, { CCI_REG8(0x52c5), 0x00 },
++ { CCI_REG8(0x52c6), 0x00 }, { CCI_REG8(0x52c7), 0x50 },
++ { CCI_REG8(0x52c8), 0x00 }, { CCI_REG8(0x52c9), 0x50 },
++ { CCI_REG8(0x52ca), 0x00 }, { CCI_REG8(0x52cb), 0x50 },
++ { CCI_REG8(0x52cc), 0x00 }, { CCI_REG8(0x52cd), 0x50 },
++ { CCI_REG8(0x52ce), 0x00 }, { CCI_REG8(0x52cf), 0x50 },
++ { CCI_REG8(0x52f0), 0x04 }, { CCI_REG8(0x52f1), 0x03 },
++ { CCI_REG8(0x52f2), 0x02 }, { CCI_REG8(0x52f3), 0x01 },
++ { CCI_REG8(0x52f4), 0x08 }, { CCI_REG8(0x52f5), 0x07 },
++ { CCI_REG8(0x52f6), 0x06 }, { CCI_REG8(0x52f7), 0x05 },
++ { CCI_REG8(0x52f8), 0x0c }, { CCI_REG8(0x52f9), 0x0b },
++ { CCI_REG8(0x52fa), 0x0a }, { CCI_REG8(0x52fb), 0x09 },
++ { CCI_REG8(0x52fc), 0x10 }, { CCI_REG8(0x52fd), 0x0f },
++ { CCI_REG8(0x52fe), 0x0e }, { CCI_REG8(0x52ff), 0x0d },
++ { CCI_REG8(0x5300), 0x14 }, { CCI_REG8(0x5301), 0x13 },
++ { CCI_REG8(0x5302), 0x12 }, { CCI_REG8(0x5303), 0x11 },
++ { CCI_REG8(0x5304), 0x18 }, { CCI_REG8(0x5305), 0x17 },
++ { CCI_REG8(0x5306), 0x16 }, { CCI_REG8(0x5307), 0x15 },
++ { CCI_REG8(0x5308), 0x1c }, { CCI_REG8(0x5309), 0x1b },
++ { CCI_REG8(0x530a), 0x1a }, { CCI_REG8(0x530b), 0x19 },
++ { CCI_REG8(0x530c), 0x20 }, { CCI_REG8(0x530d), 0x1f },
++ { CCI_REG8(0x530e), 0x1e }, { CCI_REG8(0x530f), 0x1d },
++ { CCI_REG8(0x5310), 0x03 }, { CCI_REG8(0x5311), 0xe8 },
++ { CCI_REG8(0x5331), 0x0a }, { CCI_REG8(0x5332), 0x43 },
++ { CCI_REG8(0x5333), 0x45 }, { CCI_REG8(0x5353), 0x09 },
++ { CCI_REG8(0x5354), 0x00 }, { CCI_REG8(0x5414), 0x03 },
++ { CCI_REG8(0x54b0), 0x10 }, { CCI_REG8(0x54b3), 0x0e },
++ { CCI_REG8(0x54b5), 0x02 }, { CCI_REG8(0x54b6), 0x00 },
++ { CCI_REG8(0x54b7), 0x00 }, { CCI_REG8(0x54b8), 0x00 },
++ { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54ba), 0x00 },
++ { CCI_REG8(0x54bb), 0x10 }, { CCI_REG8(0x54bc), 0x00 },
++ { CCI_REG8(0x54bd), 0x00 }, { CCI_REG8(0x54d2), 0xff },
++ { CCI_REG8(0x54d3), 0x1c }, { CCI_REG8(0x5510), 0x03 },
++ { CCI_REG8(0x5511), 0xe8 }, { CCI_REG8(0x5550), 0x6c },
++ { CCI_REG8(0x5551), 0x00 }, { CCI_REG8(0x557a), 0x00 },
++ { CCI_REG8(0x557b), 0x38 }, { CCI_REG8(0x557c), 0x00 },
++ { CCI_REG8(0x557d), 0x4b }, { CCI_REG8(0x5590), 0x00 },
++ { CCI_REG8(0x5591), 0x50 }, { CCI_REG8(0x5592), 0x00 },
++ { CCI_REG8(0x5593), 0x50 }, { CCI_REG8(0x5594), 0x00 },
++ { CCI_REG8(0x5595), 0x50 }, { CCI_REG8(0x5596), 0x00 },
++ { CCI_REG8(0x5597), 0x50 }, { CCI_REG8(0x5598), 0x00 },
++ { CCI_REG8(0x5599), 0x50 }, { CCI_REG8(0x559a), 0x01 },
++ { CCI_REG8(0x559b), 0x00 }, { CCI_REG8(0x559c), 0x01 },
++ { CCI_REG8(0x559d), 0x00 }, { CCI_REG8(0x559e), 0x00 },
++ { CCI_REG8(0x559f), 0x50 }, { CCI_REG8(0x55a0), 0x00 },
++ { CCI_REG8(0x55a1), 0x50 }, { CCI_REG8(0x55a2), 0x01 },
++ { CCI_REG8(0x55a3), 0x00 }, { CCI_REG8(0x55a4), 0x01 },
++ { CCI_REG8(0x55a5), 0x00 }, { CCI_REG8(0x55a6), 0x00 },
++ { CCI_REG8(0x55a7), 0x50 }, { CCI_REG8(0x55a8), 0x00 },
++ { CCI_REG8(0x55a9), 0x50 }, { CCI_REG8(0x55aa), 0x00 },
++ { CCI_REG8(0x55ab), 0x50 }, { CCI_REG8(0x55ac), 0x00 },
++ { CCI_REG8(0x55ad), 0x50 }, { CCI_REG8(0x55ae), 0x00 },
++ { CCI_REG8(0x55af), 0x50 }, { CCI_REG8(0x55b0), 0x00 },
++ { CCI_REG8(0x55b1), 0x50 }, { CCI_REG8(0x55b2), 0x00 },
++ { CCI_REG8(0x55b3), 0x50 }, { CCI_REG8(0x55b4), 0x00 },
++ { CCI_REG8(0x55b5), 0x50 }, { CCI_REG8(0x55b6), 0x00 },
++ { CCI_REG8(0x55b7), 0x50 }, { CCI_REG8(0x55b8), 0x00 },
++ { CCI_REG8(0x55b9), 0x50 }, { CCI_REG8(0x55ba), 0x01 },
++ { CCI_REG8(0x55bb), 0x00 }, { CCI_REG8(0x55bc), 0x01 },
++ { CCI_REG8(0x55bd), 0x00 }, { CCI_REG8(0x55be), 0x00 },
++ { CCI_REG8(0x55bf), 0x50 }, { CCI_REG8(0x55c0), 0x00 },
++ { CCI_REG8(0x55c1), 0x50 }, { CCI_REG8(0x55c2), 0x01 },
++ { CCI_REG8(0x55c3), 0x00 }, { CCI_REG8(0x55c4), 0x01 },
++ { CCI_REG8(0x55c5), 0x00 }, { CCI_REG8(0x55c6), 0x00 },
++ { CCI_REG8(0x55c7), 0x50 }, { CCI_REG8(0x55c8), 0x00 },
++ { CCI_REG8(0x55c9), 0x50 }, { CCI_REG8(0x55ca), 0x00 },
++ { CCI_REG8(0x55cb), 0x50 }, { CCI_REG8(0x55cc), 0x00 },
++ { CCI_REG8(0x55cd), 0x50 }, { CCI_REG8(0x55ce), 0x00 },
++ { CCI_REG8(0x55cf), 0x50 }, { CCI_REG8(0x55f0), 0x04 },
++ { CCI_REG8(0x55f1), 0x03 }, { CCI_REG8(0x55f2), 0x02 },
++ { CCI_REG8(0x55f3), 0x01 }, { CCI_REG8(0x55f4), 0x08 },
++ { CCI_REG8(0x55f5), 0x07 }, { CCI_REG8(0x55f6), 0x06 },
++ { CCI_REG8(0x55f7), 0x05 }, { CCI_REG8(0x55f8), 0x0c },
++ { CCI_REG8(0x55f9), 0x0b }, { CCI_REG8(0x55fa), 0x0a },
++ { CCI_REG8(0x55fb), 0x09 }, { CCI_REG8(0x55fc), 0x10 },
++ { CCI_REG8(0x55fd), 0x0f }, { CCI_REG8(0x55fe), 0x0e },
++ { CCI_REG8(0x55ff), 0x0d }, { CCI_REG8(0x5600), 0x14 },
++ { CCI_REG8(0x5601), 0x13 }, { CCI_REG8(0x5602), 0x12 },
++ { CCI_REG8(0x5603), 0x11 }, { CCI_REG8(0x5604), 0x18 },
++ { CCI_REG8(0x5605), 0x17 }, { CCI_REG8(0x5606), 0x16 },
++ { CCI_REG8(0x5607), 0x15 }, { CCI_REG8(0x5608), 0x1c },
++ { CCI_REG8(0x5609), 0x1b }, { CCI_REG8(0x560a), 0x1a },
++ { CCI_REG8(0x560b), 0x19 }, { CCI_REG8(0x560c), 0x20 },
++ { CCI_REG8(0x560d), 0x1f }, { CCI_REG8(0x560e), 0x1e },
++ { CCI_REG8(0x560f), 0x1d }, { CCI_REG8(0x5631), 0x02 },
++ { CCI_REG8(0x5632), 0x42 }, { CCI_REG8(0x5633), 0x24 },
++ { CCI_REG8(0x5653), 0x09 }, { CCI_REG8(0x5654), 0x00 },
++ { CCI_REG8(0x5714), 0x03 }, { CCI_REG8(0x57b0), 0x10 },
++ { CCI_REG8(0x57b3), 0x0e }, { CCI_REG8(0x57b5), 0x02 },
++ { CCI_REG8(0x57b6), 0x00 }, { CCI_REG8(0x57b7), 0x00 },
++ { CCI_REG8(0x57b8), 0x00 }, { CCI_REG8(0x57b9), 0x70 },
++ { CCI_REG8(0x57ba), 0x00 }, { CCI_REG8(0x57bb), 0x10 },
++ { CCI_REG8(0x57bc), 0x00 }, { CCI_REG8(0x57bd), 0x00 },
++ { CCI_REG8(0x57d2), 0xff }, { CCI_REG8(0x57d3), 0x1c },
++ { CCI_REG8(0x5810), 0x03 }, { CCI_REG8(0x5811), 0xe8 },
++ { CCI_REG8(0x5850), 0x6c }, { CCI_REG8(0x5851), 0x00 },
++ { CCI_REG8(0x587a), 0x00 }, { CCI_REG8(0x587b), 0x38 },
++ { CCI_REG8(0x587c), 0x00 }, { CCI_REG8(0x587d), 0x4b },
++ { CCI_REG8(0x5890), 0x00 }, { CCI_REG8(0x5891), 0x50 },
++ { CCI_REG8(0x5892), 0x00 }, { CCI_REG8(0x5893), 0x50 },
++ { CCI_REG8(0x5894), 0x00 }, { CCI_REG8(0x5895), 0x50 },
++ { CCI_REG8(0x5896), 0x00 }, { CCI_REG8(0x5897), 0x50 },
++ { CCI_REG8(0x5898), 0x00 }, { CCI_REG8(0x5899), 0x50 },
++ { CCI_REG8(0x589a), 0x01 }, { CCI_REG8(0x589b), 0x00 },
++ { CCI_REG8(0x589c), 0x01 }, { CCI_REG8(0x589d), 0x00 },
++ { CCI_REG8(0x589e), 0x00 }, { CCI_REG8(0x589f), 0x50 },
++ { CCI_REG8(0x58a0), 0x00 }, { CCI_REG8(0x58a1), 0x50 },
++ { CCI_REG8(0x58a2), 0x01 }, { CCI_REG8(0x58a3), 0x00 },
++ { CCI_REG8(0x58a4), 0x01 }, { CCI_REG8(0x58a5), 0x00 },
++ { CCI_REG8(0x58a6), 0x00 }, { CCI_REG8(0x58a7), 0x50 },
++ { CCI_REG8(0x58a8), 0x00 }, { CCI_REG8(0x58a9), 0x50 },
++ { CCI_REG8(0x58aa), 0x00 }, { CCI_REG8(0x58ab), 0x50 },
++ { CCI_REG8(0x58ac), 0x00 }, { CCI_REG8(0x58ad), 0x50 },
++ { CCI_REG8(0x58ae), 0x00 }, { CCI_REG8(0x58af), 0x50 },
++ { CCI_REG8(0x58b0), 0x00 }, { CCI_REG8(0x58b1), 0x50 },
++ { CCI_REG8(0x58b2), 0x00 }, { CCI_REG8(0x58b3), 0x50 },
++ { CCI_REG8(0x58b4), 0x00 }, { CCI_REG8(0x58b5), 0x50 },
++ { CCI_REG8(0x58b6), 0x00 }, { CCI_REG8(0x58b7), 0x50 },
++ { CCI_REG8(0x58b8), 0x00 }, { CCI_REG8(0x58b9), 0x50 },
++ { CCI_REG8(0x58ba), 0x01 }, { CCI_REG8(0x58bb), 0x00 },
++ { CCI_REG8(0x58bc), 0x01 }, { CCI_REG8(0x58bd), 0x00 },
++ { CCI_REG8(0x58be), 0x00 }, { CCI_REG8(0x58bf), 0x50 },
++ { CCI_REG8(0x58c0), 0x00 }, { CCI_REG8(0x58c1), 0x50 },
++ { CCI_REG8(0x58c2), 0x01 }, { CCI_REG8(0x58c3), 0x00 },
++ { CCI_REG8(0x58c4), 0x01 }, { CCI_REG8(0x58c5), 0x00 },
++ { CCI_REG8(0x58c6), 0x00 }, { CCI_REG8(0x58c7), 0x50 },
++ { CCI_REG8(0x58c8), 0x00 }, { CCI_REG8(0x58c9), 0x50 },
++ { CCI_REG8(0x58ca), 0x00 }, { CCI_REG8(0x58cb), 0x50 },
++ { CCI_REG8(0x58cc), 0x00 }, { CCI_REG8(0x58cd), 0x50 },
++ { CCI_REG8(0x58ce), 0x00 }, { CCI_REG8(0x58cf), 0x50 },
++ { CCI_REG8(0x58f0), 0x04 }, { CCI_REG8(0x58f1), 0x03 },
++ { CCI_REG8(0x58f2), 0x02 }, { CCI_REG8(0x58f3), 0x01 },
++ { CCI_REG8(0x58f4), 0x08 }, { CCI_REG8(0x58f5), 0x07 },
++ { CCI_REG8(0x58f6), 0x06 }, { CCI_REG8(0x58f7), 0x05 },
++ { CCI_REG8(0x58f8), 0x0c }, { CCI_REG8(0x58f9), 0x0b },
++ { CCI_REG8(0x58fa), 0x0a }, { CCI_REG8(0x58fb), 0x09 },
++ { CCI_REG8(0x58fc), 0x10 }, { CCI_REG8(0x58fd), 0x0f },
++ { CCI_REG8(0x58fe), 0x0e }, { CCI_REG8(0x58ff), 0x0d },
++ { CCI_REG8(0x5900), 0x14 }, { CCI_REG8(0x5901), 0x13 },
++ { CCI_REG8(0x5902), 0x12 }, { CCI_REG8(0x5903), 0x11 },
++ { CCI_REG8(0x5904), 0x18 }, { CCI_REG8(0x5905), 0x17 },
++ { CCI_REG8(0x5906), 0x16 }, { CCI_REG8(0x5907), 0x15 },
++ { CCI_REG8(0x5908), 0x1c }, { CCI_REG8(0x5909), 0x1b },
++ { CCI_REG8(0x590a), 0x1a }, { CCI_REG8(0x590b), 0x19 },
++ { CCI_REG8(0x590c), 0x20 }, { CCI_REG8(0x590d), 0x1f },
++ { CCI_REG8(0x590e), 0x1e }, { CCI_REG8(0x590f), 0x1d },
++ { CCI_REG8(0x5931), 0x02 }, { CCI_REG8(0x5932), 0x42 },
++ { CCI_REG8(0x5933), 0x24 }, { CCI_REG8(0x5953), 0x09 },
++ { CCI_REG8(0x5954), 0x00 }, { CCI_REG8(0x5989), 0x84 },
++ { CCI_REG8(0x59c3), 0x04 }, { CCI_REG8(0x59c4), 0x24 },
++ { CCI_REG8(0x59c5), 0x40 }, { CCI_REG8(0x59c6), 0x1b },
++ { CCI_REG8(0x59c7), 0x40 }, { CCI_REG8(0x5a02), 0x0f },
++ { CCI_REG8(0x5f00), 0x29 }, { CCI_REG8(0x5f2d), 0x28 },
++ { CCI_REG8(0x5f2e), 0x28 }, { CCI_REG8(0x6801), 0x11 },
++ { CCI_REG8(0x6802), 0x3f }, { CCI_REG8(0x6803), 0xe7 },
++ { CCI_REG8(0x6825), 0x0f }, { CCI_REG8(0x6826), 0x20 },
++ { CCI_REG8(0x6827), 0x00 }, { CCI_REG8(0x6829), 0x16 },
++ { CCI_REG8(0x682b), 0xb3 }, { CCI_REG8(0x682c), 0x01 },
++ { CCI_REG8(0x6832), 0xff }, { CCI_REG8(0x6833), 0xff },
++ { CCI_REG8(0x6898), 0x80 }, { CCI_REG8(0x6899), 0x80 },
++ { CCI_REG8(0x689b), 0x40 }, { CCI_REG8(0x689c), 0x20 },
++ { CCI_REG8(0x689d), 0x20 }, { CCI_REG8(0x689e), 0x80 },
++ { CCI_REG8(0x689f), 0x60 }, { CCI_REG8(0x68a0), 0x40 },
++ { CCI_REG8(0x68a4), 0x40 }, { CCI_REG8(0x68a5), 0x20 },
++ { CCI_REG8(0x68a6), 0x00 }, { CCI_REG8(0x68b6), 0x80 },
++ { CCI_REG8(0x68b7), 0x80 }, { CCI_REG8(0x68b8), 0x80 },
++ { CCI_REG8(0x68bc), 0x80 }, { CCI_REG8(0x68bd), 0x80 },
++ { CCI_REG8(0x68be), 0x80 }, { CCI_REG8(0x68bf), 0x40 },
++ { CCI_REG8(0x68c2), 0x80 }, { CCI_REG8(0x68c3), 0x80 },
++ { CCI_REG8(0x68c4), 0x60 }, { CCI_REG8(0x68c5), 0x30 },
++ { CCI_REG8(0x6918), 0x80 }, { CCI_REG8(0x6919), 0x80 },
++ { CCI_REG8(0x691b), 0x40 }, { CCI_REG8(0x691c), 0x20 },
++ { CCI_REG8(0x691d), 0x20 }, { CCI_REG8(0x691e), 0x80 },
++ { CCI_REG8(0x691f), 0x60 }, { CCI_REG8(0x6920), 0x40 },
++ { CCI_REG8(0x6924), 0x40 }, { CCI_REG8(0x6925), 0x20 },
++ { CCI_REG8(0x6926), 0x00 }, { CCI_REG8(0x6936), 0x40 },
++ { CCI_REG8(0x6937), 0x40 }, { CCI_REG8(0x6938), 0x20 },
++ { CCI_REG8(0x6939), 0x20 }, { CCI_REG8(0x693a), 0x10 },
++ { CCI_REG8(0x693b), 0x10 }, { CCI_REG8(0x693c), 0x20 },
++ { CCI_REG8(0x693d), 0x20 }, { CCI_REG8(0x693e), 0x10 },
++ { CCI_REG8(0x693f), 0x10 }, { CCI_REG8(0x6940), 0x00 },
++ { CCI_REG8(0x6941), 0x00 }, { CCI_REG8(0x6942), 0x08 },
++ { CCI_REG8(0x6943), 0x08 }, { CCI_REG8(0x6944), 0x00 },
++ { CCI_REG8(0x69c2), 0x07 }, { CCI_REG8(0x6a20), 0x01 },
++ { CCI_REG8(0x6a23), 0x10 }, { CCI_REG8(0x6a26), 0x3d },
++ { CCI_REG8(0x6a27), 0x3e }, { CCI_REG8(0x6a38), 0x02 },
++ { CCI_REG8(0x6a39), 0x20 }, { CCI_REG8(0x6a3a), 0x02 },
++ { CCI_REG8(0x6a3b), 0x84 }, { CCI_REG8(0x6a3e), 0x02 },
++ { CCI_REG8(0x6a3f), 0x20 }, { CCI_REG8(0x6a47), 0x3b },
++ { CCI_REG8(0x6a63), 0x04 }, { CCI_REG8(0x6a65), 0x00 },
++ { CCI_REG8(0x6a67), 0x0f }, { CCI_REG8(0x6b22), 0x07 },
++ { CCI_REG8(0x6b23), 0xc2 }, { CCI_REG8(0x6b2f), 0x00 },
++ { CCI_REG8(0x6b60), 0x1f }, { CCI_REG8(0x6bd2), 0x5a },
++ { CCI_REG8(0x6c20), 0x50 }, { CCI_REG8(0x6c60), 0x50 },
++ { CCI_REG8(0x6c61), 0x06 }, { CCI_REG8(0x7318), 0x04 },
++ { CCI_REG8(0x7319), 0x01 }, { CCI_REG8(0x731a), 0x04 },
++ { CCI_REG8(0x731b), 0x01 }, { CCI_REG8(0x731c), 0x00 },
++ { CCI_REG8(0x731d), 0x00 }, { CCI_REG8(0x731e), 0x04 },
++ { CCI_REG8(0x731f), 0x01 }, { CCI_REG8(0x7320), 0x04 },
++ { CCI_REG8(0x7321), 0x00 }, { CCI_REG8(0x7322), 0x04 },
++ { CCI_REG8(0x7323), 0x00 }, { CCI_REG8(0x7324), 0x04 },
++ { CCI_REG8(0x7325), 0x00 }, { CCI_REG8(0x7326), 0x04 },
++ { CCI_REG8(0x7327), 0x00 }, { CCI_REG8(0x7600), 0x00 },
++ { CCI_REG8(0x7601), 0x00 }, { CCI_REG8(0x7602), 0x10 },
++ { CCI_REG8(0x7603), 0x00 }, { CCI_REG8(0x7604), 0x00 },
++ { CCI_REG8(0x7605), 0x00 }, { CCI_REG8(0x7606), 0x10 },
++ { CCI_REG8(0x7607), 0x00 }, { CCI_REG8(0x7608), 0x00 },
++ { CCI_REG8(0x7609), 0x00 }, { CCI_REG8(0x760a), 0x10 },
++ { CCI_REG8(0x760b), 0x00 }, { CCI_REG8(0x760c), 0x00 },
++ { CCI_REG8(0x760d), 0x00 }, { CCI_REG8(0x760e), 0x10 },
++ { CCI_REG8(0x760f), 0x00 }, { CCI_REG8(0x7610), 0x00 },
++ { CCI_REG8(0x7611), 0x00 }, { CCI_REG8(0x7612), 0x10 },
++ { CCI_REG8(0x7613), 0x00 }, { CCI_REG8(0x7614), 0x00 },
++ { CCI_REG8(0x7615), 0x00 }, { CCI_REG8(0x7616), 0x10 },
++ { CCI_REG8(0x7617), 0x00 }, { CCI_REG8(0x7618), 0x00 },
++ { CCI_REG8(0x7619), 0x00 }, { CCI_REG8(0x761a), 0x10 },
++ { CCI_REG8(0x761b), 0x00 }, { CCI_REG8(0x761c), 0x00 },
++ { CCI_REG8(0x761d), 0x00 }, { CCI_REG8(0x761e), 0x10 },
++ { CCI_REG8(0x761f), 0x00 }, { CCI_REG8(0x7620), 0x00 },
++ { CCI_REG8(0x7621), 0x00 }, { CCI_REG8(0x7622), 0x10 },
++ { CCI_REG8(0x7623), 0x00 }, { CCI_REG8(0x7624), 0x00 },
++ { CCI_REG8(0x7625), 0x00 }, { CCI_REG8(0x7626), 0x10 },
++ { CCI_REG8(0x7627), 0x00 }, { CCI_REG8(0x7628), 0x00 },
++ { CCI_REG8(0x7629), 0x00 }, { CCI_REG8(0x762a), 0x10 },
++ { CCI_REG8(0x762b), 0x00 }, { CCI_REG8(0x762c), 0x00 },
++ { CCI_REG8(0x762d), 0x00 }, { CCI_REG8(0x762e), 0x10 },
++ { CCI_REG8(0x762f), 0x00 }, { CCI_REG8(0x7630), 0x00 },
++ { CCI_REG8(0x7631), 0x00 }, { CCI_REG8(0x7632), 0x10 },
++ { CCI_REG8(0x7633), 0x00 }, { CCI_REG8(0x7634), 0x00 },
++ { CCI_REG8(0x7635), 0x00 }, { CCI_REG8(0x7636), 0x10 },
++ { CCI_REG8(0x7637), 0x00 }, { CCI_REG8(0x7638), 0x00 },
++ { CCI_REG8(0x7639), 0x00 }, { CCI_REG8(0x763a), 0x10 },
++ { CCI_REG8(0x763b), 0x00 }, { CCI_REG8(0x763c), 0x00 },
++ { CCI_REG8(0x763d), 0x00 }, { CCI_REG8(0x763e), 0x10 },
++ { CCI_REG8(0x763f), 0x00 }, { CCI_REG8(0x7640), 0x00 },
++ { CCI_REG8(0x7641), 0x00 }, { CCI_REG8(0x7642), 0x10 },
++ { CCI_REG8(0x7643), 0x00 }, { CCI_REG8(0x7644), 0x00 },
++ { CCI_REG8(0x7645), 0x00 }, { CCI_REG8(0x7646), 0x10 },
++ { CCI_REG8(0x7647), 0x00 }, { CCI_REG8(0x7648), 0x00 },
++ { CCI_REG8(0x7649), 0x00 }, { CCI_REG8(0x764a), 0x10 },
++ { CCI_REG8(0x764b), 0x00 }, { CCI_REG8(0x764c), 0x00 },
++ { CCI_REG8(0x764d), 0x00 }, { CCI_REG8(0x764e), 0x10 },
++ { CCI_REG8(0x764f), 0x00 }, { CCI_REG8(0x7650), 0x00 },
++ { CCI_REG8(0x7651), 0x00 }, { CCI_REG8(0x7652), 0x10 },
++ { CCI_REG8(0x7653), 0x00 }, { CCI_REG8(0x7654), 0x00 },
++ { CCI_REG8(0x7655), 0x00 }, { CCI_REG8(0x7656), 0x10 },
++ { CCI_REG8(0x7657), 0x00 }, { CCI_REG8(0x7658), 0x00 },
++ { CCI_REG8(0x7659), 0x00 }, { CCI_REG8(0x765a), 0x10 },
++ { CCI_REG8(0x765b), 0x00 }, { CCI_REG8(0x765c), 0x00 },
++ { CCI_REG8(0x765d), 0x00 }, { CCI_REG8(0x765e), 0x10 },
++ { CCI_REG8(0x765f), 0x00 }, { CCI_REG8(0x7660), 0x00 },
++ { CCI_REG8(0x7661), 0x00 }, { CCI_REG8(0x7662), 0x10 },
++ { CCI_REG8(0x7663), 0x00 }, { CCI_REG8(0x7664), 0x00 },
++ { CCI_REG8(0x7665), 0x00 }, { CCI_REG8(0x7666), 0x10 },
++ { CCI_REG8(0x7667), 0x00 }, { CCI_REG8(0x7668), 0x00 },
++ { CCI_REG8(0x7669), 0x00 }, { CCI_REG8(0x766a), 0x10 },
++ { CCI_REG8(0x766b), 0x00 }, { CCI_REG8(0x766c), 0x00 },
++ { CCI_REG8(0x766d), 0x00 }, { CCI_REG8(0x766e), 0x10 },
++ { CCI_REG8(0x766f), 0x00 }, { CCI_REG8(0x7670), 0x00 },
++ { CCI_REG8(0x7671), 0x00 }, { CCI_REG8(0x7672), 0x10 },
++ { CCI_REG8(0x7673), 0x00 }, { CCI_REG8(0x7674), 0x00 },
++ { CCI_REG8(0x7675), 0x00 }, { CCI_REG8(0x7676), 0x10 },
++ { CCI_REG8(0x7677), 0x00 }, { CCI_REG8(0x7678), 0x00 },
++ { CCI_REG8(0x7679), 0x00 }, { CCI_REG8(0x767a), 0x10 },
++ { CCI_REG8(0x767b), 0x00 }, { CCI_REG8(0x767c), 0x00 },
++ { CCI_REG8(0x767d), 0x00 }, { CCI_REG8(0x767e), 0x10 },
++ { CCI_REG8(0x767f), 0x00 }, { CCI_REG8(0x7680), 0x00 },
++ { CCI_REG8(0x7681), 0x00 }, { CCI_REG8(0x7682), 0x10 },
++ { CCI_REG8(0x7683), 0x00 }, { CCI_REG8(0x7684), 0x00 },
++ { CCI_REG8(0x7685), 0x00 }, { CCI_REG8(0x7686), 0x10 },
++ { CCI_REG8(0x7687), 0x00 }, { CCI_REG8(0x7688), 0x00 },
++ { CCI_REG8(0x7689), 0x00 }, { CCI_REG8(0x768a), 0x10 },
++ { CCI_REG8(0x768b), 0x00 }, { CCI_REG8(0x768c), 0x00 },
++ { CCI_REG8(0x768d), 0x00 }, { CCI_REG8(0x768e), 0x10 },
++ { CCI_REG8(0x768f), 0x00 }, { CCI_REG8(0x7690), 0x00 },
++ { CCI_REG8(0x7691), 0x00 }, { CCI_REG8(0x7692), 0x10 },
++ { CCI_REG8(0x7693), 0x00 }, { CCI_REG8(0x7694), 0x00 },
++ { CCI_REG8(0x7695), 0x00 }, { CCI_REG8(0x7696), 0x10 },
++ { CCI_REG8(0x7697), 0x00 }, { CCI_REG8(0x7698), 0x00 },
++ { CCI_REG8(0x7699), 0x00 }, { CCI_REG8(0x769a), 0x10 },
++ { CCI_REG8(0x769b), 0x00 }, { CCI_REG8(0x769c), 0x00 },
++ { CCI_REG8(0x769d), 0x00 }, { CCI_REG8(0x769e), 0x10 },
++ { CCI_REG8(0x769f), 0x00 }, { CCI_REG8(0x76a0), 0x00 },
++ { CCI_REG8(0x76a1), 0x00 }, { CCI_REG8(0x76a2), 0x10 },
++ { CCI_REG8(0x76a3), 0x00 }, { CCI_REG8(0x76a4), 0x00 },
++ { CCI_REG8(0x76a5), 0x00 }, { CCI_REG8(0x76a6), 0x10 },
++ { CCI_REG8(0x76a7), 0x00 }, { CCI_REG8(0x76a8), 0x00 },
++ { CCI_REG8(0x76a9), 0x00 }, { CCI_REG8(0x76aa), 0x10 },
++ { CCI_REG8(0x76ab), 0x00 }, { CCI_REG8(0x76ac), 0x00 },
++ { CCI_REG8(0x76ad), 0x00 }, { CCI_REG8(0x76ae), 0x10 },
++ { CCI_REG8(0x76af), 0x00 }, { CCI_REG8(0x76b0), 0x00 },
++ { CCI_REG8(0x76b1), 0x00 }, { CCI_REG8(0x76b2), 0x10 },
++ { CCI_REG8(0x76b3), 0x00 }, { CCI_REG8(0x76b4), 0x00 },
++ { CCI_REG8(0x76b5), 0x00 }, { CCI_REG8(0x76b6), 0x10 },
++ { CCI_REG8(0x76b7), 0x00 }, { CCI_REG8(0x76b8), 0x00 },
++ { CCI_REG8(0x76b9), 0x00 }, { CCI_REG8(0x76ba), 0x10 },
++ { CCI_REG8(0x76bb), 0x00 }, { CCI_REG8(0x76bc), 0x00 },
++ { CCI_REG8(0x76bd), 0x00 }, { CCI_REG8(0x76be), 0x10 },
++ { CCI_REG8(0x76bf), 0x00 }, { CCI_REG8(0x76c0), 0x00 },
++ { CCI_REG8(0x76c1), 0x00 }, { CCI_REG8(0x76c2), 0x10 },
++ { CCI_REG8(0x76c3), 0x00 }, { CCI_REG8(0x76c4), 0x00 },
++ { CCI_REG8(0x76c5), 0x00 }, { CCI_REG8(0x76c6), 0x10 },
++ { CCI_REG8(0x76c7), 0x00 }, { CCI_REG8(0x76c8), 0x00 },
++ { CCI_REG8(0x76c9), 0x00 }, { CCI_REG8(0x76ca), 0x10 },
++ { CCI_REG8(0x76cb), 0x00 }, { CCI_REG8(0x76cc), 0x00 },
++ { CCI_REG8(0x76cd), 0x00 }, { CCI_REG8(0x76ce), 0x10 },
++ { CCI_REG8(0x76cf), 0x00 }, { CCI_REG8(0x76d0), 0x00 },
++ { CCI_REG8(0x76d1), 0x00 }, { CCI_REG8(0x76d2), 0x10 },
++ { CCI_REG8(0x76d3), 0x00 }, { CCI_REG8(0x76d4), 0x00 },
++ { CCI_REG8(0x76d5), 0x00 }, { CCI_REG8(0x76d6), 0x10 },
++ { CCI_REG8(0x76d7), 0x00 }, { CCI_REG8(0x76d8), 0x00 },
++ { CCI_REG8(0x76d9), 0x00 }, { CCI_REG8(0x76da), 0x10 },
++ { CCI_REG8(0x76db), 0x00 }, { CCI_REG8(0x76dc), 0x00 },
++ { CCI_REG8(0x76dd), 0x00 }, { CCI_REG8(0x76de), 0x10 },
++ { CCI_REG8(0x76df), 0x00 }, { CCI_REG8(0x76e0), 0x00 },
++ { CCI_REG8(0x76e1), 0x00 }, { CCI_REG8(0x76e2), 0x10 },
++ { CCI_REG8(0x76e3), 0x00 }, { CCI_REG8(0x76e4), 0x00 },
++ { CCI_REG8(0x76e5), 0x00 }, { CCI_REG8(0x76e6), 0x10 },
++ { CCI_REG8(0x76e7), 0x00 }, { CCI_REG8(0x76e8), 0x00 },
++ { CCI_REG8(0x76e9), 0x00 }, { CCI_REG8(0x76ea), 0x10 },
++ { CCI_REG8(0x76eb), 0x00 }, { CCI_REG8(0x76ec), 0x00 },
++ { CCI_REG8(0x76ed), 0x00 }, { CCI_REG8(0x76ee), 0x10 },
++ { CCI_REG8(0x76ef), 0x00 }, { CCI_REG8(0x76f0), 0x00 },
++ { CCI_REG8(0x76f1), 0x00 }, { CCI_REG8(0x76f2), 0x10 },
++ { CCI_REG8(0x76f3), 0x00 }, { CCI_REG8(0x76f4), 0x00 },
++ { CCI_REG8(0x76f5), 0x00 }, { CCI_REG8(0x76f6), 0x10 },
++ { CCI_REG8(0x76f7), 0x00 }, { CCI_REG8(0x76f8), 0x00 },
++ { CCI_REG8(0x76f9), 0x00 }, { CCI_REG8(0x76fa), 0x10 },
++ { CCI_REG8(0x76fb), 0x00 }, { CCI_REG8(0x76fc), 0x00 },
++ { CCI_REG8(0x76fd), 0x00 }, { CCI_REG8(0x76fe), 0x10 },
++ { CCI_REG8(0x76ff), 0x00 }, { CCI_REG8(0x7700), 0x00 },
++ { CCI_REG8(0x7701), 0x00 }, { CCI_REG8(0x7702), 0x10 },
++ { CCI_REG8(0x7703), 0x00 }, { CCI_REG8(0x7704), 0x00 },
++ { CCI_REG8(0x7705), 0x00 }, { CCI_REG8(0x7706), 0x10 },
++ { CCI_REG8(0x7707), 0x00 }, { CCI_REG8(0x7708), 0x00 },
++ { CCI_REG8(0x7709), 0x00 }, { CCI_REG8(0x770a), 0x10 },
++ { CCI_REG8(0x770b), 0x00 }, { CCI_REG8(0x770c), 0x00 },
++ { CCI_REG8(0x770d), 0x00 }, { CCI_REG8(0x770e), 0x10 },
++ { CCI_REG8(0x770f), 0x00 }, { CCI_REG8(0x7710), 0x00 },
++ { CCI_REG8(0x7711), 0x00 }, { CCI_REG8(0x7712), 0x10 },
++ { CCI_REG8(0x7713), 0x00 }, { CCI_REG8(0x7714), 0x00 },
++ { CCI_REG8(0x7715), 0x00 }, { CCI_REG8(0x7716), 0x10 },
++ { CCI_REG8(0x7717), 0x00 }, { CCI_REG8(0x7718), 0x00 },
++ { CCI_REG8(0x7719), 0x00 }, { CCI_REG8(0x771a), 0x10 },
++ { CCI_REG8(0x771b), 0x00 }, { CCI_REG8(0x771c), 0x00 },
++ { CCI_REG8(0x771d), 0x00 }, { CCI_REG8(0x771e), 0x10 },
++ { CCI_REG8(0x771f), 0x00 }, { CCI_REG8(0x7720), 0x00 },
++ { CCI_REG8(0x7721), 0x00 }, { CCI_REG8(0x7722), 0x10 },
++ { CCI_REG8(0x7723), 0x00 }, { CCI_REG8(0x7724), 0x00 },
++ { CCI_REG8(0x7725), 0x00 }, { CCI_REG8(0x7726), 0x10 },
++ { CCI_REG8(0x7727), 0x00 }, { CCI_REG8(0x7728), 0x00 },
++ { CCI_REG8(0x7729), 0x00 }, { CCI_REG8(0x772a), 0x10 },
++ { CCI_REG8(0x772b), 0x00 }, { CCI_REG8(0x772c), 0x00 },
++ { CCI_REG8(0x772d), 0x00 }, { CCI_REG8(0x772e), 0x10 },
++ { CCI_REG8(0x772f), 0x00 }, { CCI_REG8(0x7730), 0x00 },
++ { CCI_REG8(0x7731), 0x00 }, { CCI_REG8(0x7732), 0x10 },
++ { CCI_REG8(0x7733), 0x00 }, { CCI_REG8(0x7734), 0x00 },
++ { CCI_REG8(0x7735), 0x00 }, { CCI_REG8(0x7736), 0x10 },
++ { CCI_REG8(0x7737), 0x00 }, { CCI_REG8(0x7738), 0x00 },
++ { CCI_REG8(0x7739), 0x00 }, { CCI_REG8(0x773a), 0x10 },
++ { CCI_REG8(0x773b), 0x00 }, { CCI_REG8(0x773c), 0x00 },
++ { CCI_REG8(0x773d), 0x00 }, { CCI_REG8(0x773e), 0x10 },
++ { CCI_REG8(0x773f), 0x00 }, { CCI_REG8(0x7740), 0x00 },
++ { CCI_REG8(0x7741), 0x00 }, { CCI_REG8(0x7742), 0x10 },
++ { CCI_REG8(0x7743), 0x00 }, { CCI_REG8(0x3421), 0x02 },
++ { CCI_REG8(0x37d0), 0x00 }, { CCI_REG8(0x3632), 0x99 },
++ { CCI_REG8(0xc518), 0x1f }, { CCI_REG8(0xc519), 0x1f },
++ { CCI_REG8(0xc51a), 0x1f }, { CCI_REG8(0xc51b), 0x1f },
++ { CCI_REG8(0xc51c), 0x1f }, { CCI_REG8(0xc51d), 0x1f },
++ { CCI_REG8(0xc51e), 0x1f }, { CCI_REG8(0xc51f), 0x1f },
++ { CCI_REG8(0xc520), 0x1f }, { CCI_REG8(0xc521), 0x1f },
++ { CCI_REG8(0x3616), 0xa0 }, { CCI_REG8(0x3615), 0xc5 },
++ { CCI_REG8(0xc4c1), 0x02 }, { CCI_REG8(0xc4c2), 0x02 },
++ { CCI_REG8(0xc4c3), 0x03 }, { CCI_REG8(0xc4c4), 0x03 },
++ { CCI_REG8(0xc4f6), 0x0a }, { CCI_REG8(0xc4f7), 0x0a },
++ { CCI_REG8(0xc4f8), 0x0a }, { CCI_REG8(0xc4f9), 0x0a },
++ { CCI_REG8(0xc4fa), 0x0a }, { CCI_REG8(0xc4c6), 0x0a },
++ { CCI_REG8(0xc4c7), 0x0a }, { CCI_REG8(0xc4c8), 0x0a },
++ { CCI_REG8(0xc4c9), 0x0a }, { CCI_REG8(0xc4ca), 0x14 },
++ { CCI_REG8(0xc4cb), 0x14 }, { CCI_REG8(0xc4cc), 0x14 },
++ { CCI_REG8(0xc4cd), 0x14 }, { CCI_REG8(0x3b92), 0x05 },
++ { CCI_REG8(0x3b93), 0x05 }, { CCI_REG8(0x3b94), 0x05 },
++ { CCI_REG8(0x3b95), 0x05 }, { CCI_REG8(0x3623), 0x10 },
++ { CCI_REG8(0xc522), 0x18 }, { CCI_REG8(0xc523), 0x12 },
++ { CCI_REG8(0xc524), 0x0e }, { CCI_REG8(0xc525), 0x0b },
++ { CCI_REG8(0xc526), 0x18 }, { CCI_REG8(0xc527), 0x12 },
++ { CCI_REG8(0xc528), 0x0c }, { CCI_REG8(0xc529), 0x08 },
++ { CCI_REG8(0xc52a), 0x18 }, { CCI_REG8(0xc52b), 0x12 },
++ { CCI_REG8(0xc52c), 0x0e }, { CCI_REG8(0xc52d), 0x0b },
++ { CCI_REG8(0xc52e), 0x18 }, { CCI_REG8(0xc52f), 0x12 },
++ { CCI_REG8(0xc530), 0x0e }, { CCI_REG8(0xc531), 0x0b },
++ { CCI_REG8(0xc532), 0x18 }, { CCI_REG8(0xc533), 0x12 },
++ { CCI_REG8(0xc534), 0x0e }, { CCI_REG8(0xc535), 0x0b },
++ { CCI_REG8(0xc536), 0x18 }, { CCI_REG8(0xc537), 0x12 },
++ { CCI_REG8(0xc538), 0x0e }, { CCI_REG8(0xc539), 0x0b },
++ { CCI_REG8(0xc53a), 0x18 }, { CCI_REG8(0xc53b), 0x12 },
++ { CCI_REG8(0xc53c), 0x0c }, { CCI_REG8(0xc53d), 0x08 },
++ { CCI_REG8(0xc53e), 0x18 }, { CCI_REG8(0xc53f), 0x12 },
++ { CCI_REG8(0xc540), 0x0e }, { CCI_REG8(0xc541), 0x0b },
++ { CCI_REG8(0xc542), 0x18 }, { CCI_REG8(0xc543), 0x12 },
++ { CCI_REG8(0xc544), 0x0e }, { CCI_REG8(0xc545), 0x0b },
++ { CCI_REG8(0xc546), 0x18 }, { CCI_REG8(0xc547), 0x12 },
++ { CCI_REG8(0xc548), 0x0e }, { CCI_REG8(0xc549), 0x0b },
++ { CCI_REG8(0x3701), 0x18 }, { CCI_REG8(0x3702), 0x38 },
++ { CCI_REG8(0x3703), 0x72 }, { CCI_REG8(0x3708), 0x26 },
++ { CCI_REG8(0x3709), 0xe6 }, { CCI_REG8(0x3a1d), 0x18 },
++ { CCI_REG8(0x3a1e), 0x18 }, { CCI_REG8(0x3a21), 0x18 },
++ { CCI_REG8(0x3a22), 0x18 }, { CCI_REG8(0x39fb), 0x18 },
++ { CCI_REG8(0x39fc), 0x18 }, { CCI_REG8(0x39fd), 0x18 },
++ { CCI_REG8(0x39fe), 0x18 }, { CCI_REG8(0xc44a), 0x08 },
++ { CCI_REG8(0xc44c), 0x08 }, { CCI_REG8(0xc5e8), 0x0a },
++ { CCI_REG8(0xc5ea), 0x0a }, { CCI_REG8(0x391d), 0x54 },
++ { CCI_REG8(0x391e), 0xca }, { CCI_REG8(0x3991), 0x0c },
++ { CCI_REG8(0x399d), 0x0c }, { CCI_REG8(0x3744), 0x24 },
++ { CCI_REG8(0x374b), 0x0c }, { CCI_REG8(0x3be7), 0x1e },
++ { CCI_REG8(0x3be8), 0x26 }, { CCI_REG8(0x3a50), 0x14 },
++ { CCI_REG8(0x3a54), 0x14 }, { CCI_REG8(0x3add), 0x1f },
++ { CCI_REG8(0x3adf), 0x24 }, { CCI_REG8(0x3aef), 0x1f },
++ { CCI_REG8(0x3af0), 0x24 }, { CCI_REG8(0xc57f), 0x30 },
++ { CCI_REG8(0xc580), 0x30 }, { CCI_REG8(0xc581), 0x30 },
++ { CCI_REG8(0xc582), 0x30 }, { CCI_REG8(0xc583), 0x30 },
++ { CCI_REG8(0xc584), 0x30 }, { CCI_REG8(0xc585), 0x30 },
++ { CCI_REG8(0xc586), 0x30 }, { CCI_REG8(0xc587), 0x30 },
++ { CCI_REG8(0xc588), 0x30 }, { CCI_REG8(0xc589), 0x30 },
++ { CCI_REG8(0xc58a), 0x30 }, { CCI_REG8(0xc58b), 0x30 },
++ { CCI_REG8(0xc58c), 0x30 }, { CCI_REG8(0xc58d), 0x30 },
++ { CCI_REG8(0xc58e), 0x30 }, { CCI_REG8(0xc58f), 0x30 },
++ { CCI_REG8(0xc590), 0x30 }, { CCI_REG8(0xc591), 0x30 },
++ { CCI_REG8(0xc592), 0x30 }, { CCI_REG8(0xc598), 0x30 },
++ { CCI_REG8(0xc599), 0x30 }, { CCI_REG8(0xc59a), 0x30 },
++ { CCI_REG8(0xc59b), 0x30 }, { CCI_REG8(0xc59c), 0x30 },
++ { CCI_REG8(0xc59d), 0x30 }, { CCI_REG8(0xc59e), 0x30 },
++ { CCI_REG8(0xc59f), 0x30 }, { CCI_REG8(0xc5a0), 0x30 },
++ { CCI_REG8(0xc5a1), 0x30 }, { CCI_REG8(0xc5a2), 0x30 },
++ { CCI_REG8(0xc5a3), 0x30 }, { CCI_REG8(0xc5a4), 0x30 },
++ { CCI_REG8(0xc5a5), 0x30 }, { CCI_REG8(0xc5a6), 0x30 },
++ { CCI_REG8(0xc5a7), 0x30 }, { CCI_REG8(0xc5a8), 0x30 },
++ { CCI_REG8(0xc5a9), 0x30 }, { CCI_REG8(0xc5aa), 0x30 },
++ { CCI_REG8(0xc5ab), 0x30 }, { CCI_REG8(0xc5b1), 0x38 },
++ { CCI_REG8(0xc5b2), 0x38 }, { CCI_REG8(0xc5b3), 0x38 },
++ { CCI_REG8(0xc5b4), 0x38 }, { CCI_REG8(0xc5b5), 0x38 },
++ { CCI_REG8(0xc5b6), 0x38 }, { CCI_REG8(0xc5b7), 0x38 },
++ { CCI_REG8(0xc5b8), 0x38 }, { CCI_REG8(0xc5b9), 0x38 },
++ { CCI_REG8(0xc5ba), 0x38 }, { CCI_REG8(0xc5bb), 0x38 },
++ { CCI_REG8(0xc5bc), 0x38 }, { CCI_REG8(0xc5bd), 0x38 },
++ { CCI_REG8(0xc5be), 0x38 }, { CCI_REG8(0xc5bf), 0x38 },
++ { CCI_REG8(0xc5c0), 0x38 }, { CCI_REG8(0xc5c1), 0x38 },
++ { CCI_REG8(0xc5c2), 0x38 }, { CCI_REG8(0xc5c3), 0x38 },
++ { CCI_REG8(0xc5c4), 0x38 }, { CCI_REG8(0xc5ca), 0x38 },
++ { CCI_REG8(0xc5cb), 0x38 }, { CCI_REG8(0xc5cc), 0x38 },
++ { CCI_REG8(0xc5cd), 0x38 }, { CCI_REG8(0xc5ce), 0x38 },
++ { CCI_REG8(0xc5cf), 0x38 }, { CCI_REG8(0xc5d0), 0x38 },
++ { CCI_REG8(0xc5d1), 0x38 }, { CCI_REG8(0xc5d2), 0x38 },
++ { CCI_REG8(0xc5d3), 0x38 }, { CCI_REG8(0xc5d4), 0x38 },
++ { CCI_REG8(0xc5d5), 0x38 }, { CCI_REG8(0xc5d6), 0x38 },
++ { CCI_REG8(0xc5d7), 0x38 }, { CCI_REG8(0xc5d8), 0x38 },
++ { CCI_REG8(0xc5d9), 0x38 }, { CCI_REG8(0xc5da), 0x38 },
++ { CCI_REG8(0xc5db), 0x38 }, { CCI_REG8(0xc5dc), 0x38 },
++ { CCI_REG8(0xc5dd), 0x38 }, { CCI_REG8(0x3a60), 0x68 },
++ { CCI_REG8(0x3a6f), 0x68 }, { CCI_REG8(0x3a5e), 0xdc },
++ { CCI_REG8(0x3a6d), 0xdc }, { CCI_REG8(0x3aed), 0x6e },
++ { CCI_REG8(0x3af1), 0x73 }, { CCI_REG8(0x3992), 0x02 },
++ { CCI_REG8(0x399e), 0x02 }, { CCI_REG8(0x371d), 0x17 },
++ { CCI_REG8(0x371f), 0x08 }, { CCI_REG8(0x3721), 0xc9 },
++ { CCI_REG8(0x401e), 0x00 }, { CCI_REG8(0x401f), 0xf8 },
++ { CCI_REG8(0x3642), 0x00 }, { CCI_REG8(0x3641), 0x7f },
++ { CCI_REG8(0x3ac5), 0x0c }, { CCI_REG8(0x3ac6), 0x09 },
++ { CCI_REG8(0x3ac7), 0x06 }, { CCI_REG8(0x3ac8), 0x02 },
++ { CCI_REG8(0x3ac9), 0x0c }, { CCI_REG8(0x3aca), 0x09 },
++ { CCI_REG8(0x3acb), 0x06 }, { CCI_REG8(0x3acc), 0x02 },
++ { CCI_REG8(0x3acd), 0x0c }, { CCI_REG8(0x3ace), 0x09 },
++ { CCI_REG8(0x3acf), 0x07 }, { CCI_REG8(0x3ad0), 0x04 },
++ { CCI_REG8(0x3ad1), 0x0c }, { CCI_REG8(0x3ad2), 0x09 },
++ { CCI_REG8(0x3ad3), 0x07 }, { CCI_REG8(0x3ad4), 0x04 },
++ { CCI_REG8(0xc483), 0x0c }, { CCI_REG8(0xc484), 0x0c },
++ { CCI_REG8(0xc485), 0x0c }, { CCI_REG8(0xc486), 0x0c },
++ { CCI_REG8(0x3a2f), 0x0c }, { CCI_REG8(0x3a30), 0x09 },
++ { CCI_REG8(0x3a31), 0x06 }, { CCI_REG8(0x3a32), 0x02 },
++ { CCI_REG8(0x3a34), 0x0c }, { CCI_REG8(0x3a35), 0x09 },
++ { CCI_REG8(0x3a36), 0x07 }, { CCI_REG8(0x3a37), 0x04 },
++ { CCI_REG8(0x3a43), 0x0c }, { CCI_REG8(0x3a44), 0x09 },
++ { CCI_REG8(0x3a45), 0x06 }, { CCI_REG8(0x3a46), 0x02 },
++ { CCI_REG8(0x3a48), 0x0c }, { CCI_REG8(0x3a49), 0x09 },
++ { CCI_REG8(0x3a4a), 0x07 }, { CCI_REG8(0x3a4b), 0x04 },
++ { CCI_REG8(0xc487), 0x0c }, { CCI_REG8(0xc488), 0x0c },
++ { CCI_REG8(0xc489), 0x0c }, { CCI_REG8(0xc48a), 0x0c },
++ { CCI_REG8(0x3645), 0xbd }, { CCI_REG8(0x373f), 0x00 },
++ { CCI_REG8(0x374f), 0x10 }, { CCI_REG8(0x3743), 0xc6 },
++ { CCI_REG8(0x3717), 0x82 }, { CCI_REG8(0x3732), 0x07 },
++ { CCI_REG8(0x3731), 0x16 }, { CCI_REG8(0x3730), 0x16 },
++ { CCI_REG8(0x3828), 0x07 }, { CCI_REG8(0x3714), 0x68 },
++ { CCI_REG8(0x371d), 0x02 }, { CCI_REG8(0x371f), 0x02 },
++ { CCI_REG8(0x37e0), 0x00 }, { CCI_REG8(0x37e1), 0x03 },
++ { CCI_REG8(0x37e2), 0x07 }, { CCI_REG8(0x3734), 0x3e },
++ { CCI_REG8(0x3736), 0x02 }, { CCI_REG8(0x37e4), 0x36 },
++ { CCI_REG8(0x37e9), 0x1c }, { CCI_REG8(0x37ea), 0x01 },
++ { CCI_REG8(0x37eb), 0x0a }, { CCI_REG8(0x37ec), 0x1c },
++ { CCI_REG8(0x37ed), 0x01 }, { CCI_REG8(0x37ee), 0x36 },
++ { CCI_REG8(0x373b), 0x1c }, { CCI_REG8(0x373c), 0x02 },
++ { CCI_REG8(0x37bb), 0x1c }, { CCI_REG8(0x37bc), 0x02 },
++ { CCI_REG8(0x37b8), 0x0c }, { CCI_REG8(0x371c), 0x01 },
++ { CCI_REG8(0x371e), 0x11 }, { CCI_REG8(0x371d), 0x01 },
++ { CCI_REG8(0x371f), 0x01 }, { CCI_REG8(0x3721), 0x01 },
++ { CCI_REG8(0x3725), 0x12 }, { CCI_REG8(0x37e3), 0x06 },
++ { CCI_REG8(0x37dd), 0x86 }, { CCI_REG8(0x37db), 0x0a },
++ { CCI_REG8(0x37dc), 0x14 }, { CCI_REG8(0x3727), 0x20 },
++ { CCI_REG8(0x37b2), 0x80 }, { CCI_REG8(0x37da), 0x04 },
++ { CCI_REG8(0x37df), 0x01 }, { CCI_REG8(0x3731), 0x11 },
++ { CCI_REG8(0x37dd), 0x86 }, { CCI_REG8(0x37df), 0x01 },
++ { CCI_REG8(0x37da), 0x03 }, { CCI_REG8(0x37b2), 0x80 },
++ { CCI_REG8(0x3727), 0x20 }, { CCI_REG8(0x4883), 0x26 },
++ { CCI_REG8(0x488b), 0x88 }, { CCI_REG8(0x3d85), 0x1f },
++ { CCI_REG8(0x3d81), 0x01 }, { CCI_REG8(0x3d84), 0x40 },
++ { CCI_REG8(0x3d88), 0x00 }, { CCI_REG8(0x3d89), 0x00 },
++ { CCI_REG8(0x3d8a), 0x0b }, { CCI_REG8(0x3d8b), 0xff },
++ { CCI_REG8(0x4d00), 0x05 }, { CCI_REG8(0x4d01), 0xc4 },
++ { CCI_REG8(0x4d02), 0xa3 }, { CCI_REG8(0x4d03), 0x8c },
++ { CCI_REG8(0x4d04), 0xfb }, { CCI_REG8(0x4d05), 0xed },
++ { CCI_REG8(0x4010), 0x28 }, { CCI_REG8(0x4030), 0x00 },
++ { CCI_REG8(0x4031), 0x00 }, { CCI_REG8(0x4032), 0x00 },
++ { CCI_REG8(0x4033), 0x00 }, { CCI_REG8(0x4034), 0x00 },
++ { CCI_REG8(0x4035), 0x00 }, { CCI_REG8(0x4036), 0x00 },
++ { CCI_REG8(0x4037), 0x00 }, { CCI_REG8(0x4040), 0x00 },
++ { CCI_REG8(0x4041), 0x00 }, { CCI_REG8(0x4042), 0x00 },
++ { CCI_REG8(0x4043), 0x00 }, { CCI_REG8(0x4044), 0x00 },
++ { CCI_REG8(0x4045), 0x00 }, { CCI_REG8(0x4046), 0x00 },
++ { CCI_REG8(0x4047), 0x00 }, { CCI_REG8(0x3400), 0x00 },
++ { CCI_REG8(0x3421), 0x23 }, { CCI_REG8(0x3422), 0xfc },
++ { CCI_REG8(0x3423), 0x07 }, { CCI_REG8(0x3424), 0x01 },
++ { CCI_REG8(0x3425), 0x04 }, { CCI_REG8(0x3426), 0x50 },
++ { CCI_REG8(0x3427), 0x55 }, { CCI_REG8(0x3428), 0x15 },
++ { CCI_REG8(0x3429), 0x00 }, { CCI_REG8(0x3025), 0x03 },
++ { CCI_REG8(0x3053), 0x00 }, { CCI_REG8(0x3054), 0x00 },
++ { CCI_REG8(0x3055), 0x00 }, { CCI_REG8(0x3056), 0x00 },
++ { CCI_REG8(0x3057), 0x00 }, { CCI_REG8(0x3058), 0x00 },
++ { CCI_REG8(0x305c), 0x00 }, { CCI_REG8(0x340c), 0x1f },
++ { CCI_REG8(0x340d), 0x00 }, { CCI_REG8(0x3501), 0x01 },
++ { CCI_REG8(0x3542), 0x48 }, { CCI_REG8(0x3582), 0x24 },
++ { CCI_REG8(0x3015), 0xf1 }, { CCI_REG8(0x3018), 0xf2 },
++ { CCI_REG8(0x301c), 0xf2 }, { CCI_REG8(0x301d), 0xf6 },
++ { CCI_REG8(0x301e), 0xf1 }, { CCI_REG8(0x0100), 0x01 },
++ { CCI_REG8(0xfff9), 0x08 }, { CCI_REG8(0x3900), 0xcd },
++ { CCI_REG8(0x3901), 0xcd }, { CCI_REG8(0x3902), 0xcd },
++ { CCI_REG8(0x3903), 0xcd }, { CCI_REG8(0x3904), 0xcd },
++ { CCI_REG8(0x3905), 0xcd }, { CCI_REG8(0x3906), 0xcd },
++ { CCI_REG8(0x3907), 0xcd }, { CCI_REG8(0x3908), 0xcd },
++ { CCI_REG8(0x3909), 0xcd }, { CCI_REG8(0x390a), 0xcd },
++ { CCI_REG8(0x390b), 0xcd }, { CCI_REG8(0x390c), 0xcd },
++ { CCI_REG8(0x390d), 0xcd }, { CCI_REG8(0x390e), 0xcd },
++ { CCI_REG8(0x390f), 0xcd }, { CCI_REG8(0x3910), 0xcd },
++ { CCI_REG8(0x3911), 0xcd }, { CCI_REG8(0x3912), 0xcd },
++ { CCI_REG8(0x3913), 0xcd }, { CCI_REG8(0x3914), 0xcd },
++ { CCI_REG8(0x3915), 0xcd }, { CCI_REG8(0x3916), 0xcd },
++ { CCI_REG8(0x3917), 0xcd }, { CCI_REG8(0x3918), 0xcd },
++ { CCI_REG8(0x3919), 0xcd }, { CCI_REG8(0x391a), 0xcd },
++ { CCI_REG8(0x391b), 0xcd }, { CCI_REG8(0x391c), 0xcd },
++ { CCI_REG8(0x391d), 0xcd }, { CCI_REG8(0x391e), 0xcd },
++ { CCI_REG8(0x391f), 0xcd }, { CCI_REG8(0x3920), 0xcd },
++ { CCI_REG8(0x3921), 0xcd }, { CCI_REG8(0x3922), 0xcd },
++ { CCI_REG8(0x3923), 0xcd }, { CCI_REG8(0x3924), 0xcd },
++ { CCI_REG8(0x3925), 0xcd }, { CCI_REG8(0x3926), 0xcd },
++ { CCI_REG8(0x3927), 0xcd }, { CCI_REG8(0x3928), 0xcd },
++ { CCI_REG8(0x3929), 0xcd }, { CCI_REG8(0x392a), 0xcd },
++ { CCI_REG8(0x392b), 0xcd }, { CCI_REG8(0x392c), 0xcd },
++ { CCI_REG8(0x392d), 0xcd }, { CCI_REG8(0x392e), 0xcd },
++ { CCI_REG8(0x392f), 0xcd }, { CCI_REG8(0x3930), 0xcd },
++ { CCI_REG8(0x3931), 0xcd }, { CCI_REG8(0x3932), 0xcd },
++ { CCI_REG8(0x3933), 0xcd }, { CCI_REG8(0x3934), 0xcd },
++ { CCI_REG8(0x3935), 0xcd }, { CCI_REG8(0x3936), 0xcd },
++ { CCI_REG8(0x3937), 0xcd }, { CCI_REG8(0x3938), 0xcd },
++ { CCI_REG8(0x3939), 0xcd }, { CCI_REG8(0x393a), 0xcd },
++ { CCI_REG8(0x393b), 0xcd }, { CCI_REG8(0x393c), 0xcd },
++ { CCI_REG8(0x393d), 0xcd }, { CCI_REG8(0x393e), 0xcd },
++ { CCI_REG8(0x393f), 0xcd }, { CCI_REG8(0x3940), 0xcd },
++ { CCI_REG8(0x3941), 0xcd }, { CCI_REG8(0x3942), 0xcd },
++ { CCI_REG8(0x3943), 0xcd }, { CCI_REG8(0x3944), 0xcd },
++ { CCI_REG8(0x3945), 0xcd }, { CCI_REG8(0x3946), 0xcd },
++ { CCI_REG8(0x3947), 0xcd }, { CCI_REG8(0x3948), 0xcd },
++ { CCI_REG8(0x3949), 0xcd }, { CCI_REG8(0x394a), 0xcd },
++ { CCI_REG8(0x394b), 0xcd }, { CCI_REG8(0x394c), 0xcd },
++ { CCI_REG8(0x394d), 0xcd }, { CCI_REG8(0x394e), 0xcd },
++ { CCI_REG8(0x394f), 0xcd }, { CCI_REG8(0x3950), 0xcd },
++ { CCI_REG8(0x3951), 0xcd }, { CCI_REG8(0x3952), 0xcd },
++ { CCI_REG8(0x3953), 0xcd }, { CCI_REG8(0x3954), 0xcd },
++ { CCI_REG8(0x3955), 0xcd }, { CCI_REG8(0x3956), 0xcd },
++ { CCI_REG8(0x3957), 0xcd }, { CCI_REG8(0x3958), 0xcd },
++ { CCI_REG8(0x3959), 0xcd }, { CCI_REG8(0x395a), 0xcd },
++ { CCI_REG8(0x395b), 0xcd }, { CCI_REG8(0x395c), 0xcd },
++ { CCI_REG8(0x395d), 0xcd }, { CCI_REG8(0x395e), 0xcd },
++ { CCI_REG8(0x395f), 0xcd }, { CCI_REG8(0x3960), 0xcd },
++ { CCI_REG8(0x3961), 0xcd }, { CCI_REG8(0x3962), 0xcd },
++ { CCI_REG8(0x3963), 0xcd }, { CCI_REG8(0x3964), 0xcd },
++ { CCI_REG8(0x3965), 0xcd }, { CCI_REG8(0x3966), 0xcd },
++ { CCI_REG8(0x3967), 0xcd }, { CCI_REG8(0x3968), 0xcd },
++ { CCI_REG8(0x3969), 0xcd }, { CCI_REG8(0x396a), 0xcd },
++ { CCI_REG8(0x396b), 0xcd }, { CCI_REG8(0x396c), 0xcd },
++ { CCI_REG8(0x396d), 0xcd }, { CCI_REG8(0x396e), 0xcd },
++ { CCI_REG8(0x396f), 0xcd }, { CCI_REG8(0x3970), 0xcd },
++ { CCI_REG8(0x3971), 0xcd }, { CCI_REG8(0x3972), 0xcd },
++ { CCI_REG8(0x3973), 0xcd }, { CCI_REG8(0x3974), 0xcd },
++ { CCI_REG8(0x3975), 0xcd }, { CCI_REG8(0x3976), 0xcd },
++ { CCI_REG8(0x3977), 0xcd }, { CCI_REG8(0x3978), 0xcd },
++ { CCI_REG8(0x3979), 0xcd }, { CCI_REG8(0x397a), 0xcd },
++ { CCI_REG8(0x397b), 0xcd }, { CCI_REG8(0x397c), 0xcd },
++ { CCI_REG8(0x397d), 0xcd }, { CCI_REG8(0x397e), 0xcd },
++ { CCI_REG8(0x397f), 0xcd }, { CCI_REG8(0x3980), 0xcd },
++ { CCI_REG8(0x3981), 0xcd }, { CCI_REG8(0x3982), 0xcd },
++ { CCI_REG8(0x3983), 0xcd }, { CCI_REG8(0x3984), 0xcd },
++ { CCI_REG8(0x3985), 0xcd }, { CCI_REG8(0x3986), 0xcd },
++ { CCI_REG8(0x3987), 0xcd }, { CCI_REG8(0x3988), 0xcd },
++ { CCI_REG8(0x3989), 0xcd }, { CCI_REG8(0x398a), 0xcd },
++ { CCI_REG8(0x398b), 0xcd }, { CCI_REG8(0x398c), 0xcd },
++ { CCI_REG8(0x398d), 0xcd }, { CCI_REG8(0x398e), 0xcd },
++ { CCI_REG8(0x398f), 0xcd }, { CCI_REG8(0x3990), 0xcd },
++ { CCI_REG8(0x3991), 0xcd }, { CCI_REG8(0x3992), 0xcd },
++ { CCI_REG8(0x3993), 0xcd }, { CCI_REG8(0x3994), 0xcd },
++ { CCI_REG8(0x3995), 0xcd }, { CCI_REG8(0x3996), 0xcd },
++ { CCI_REG8(0x3997), 0xcd }, { CCI_REG8(0x3998), 0xcd },
++ { CCI_REG8(0x3999), 0xcd }, { CCI_REG8(0x399a), 0xcd },
++ { CCI_REG8(0x399b), 0xcd }, { CCI_REG8(0x399c), 0xcd },
++ { CCI_REG8(0x399d), 0xcd }, { CCI_REG8(0x399e), 0xcd },
++ { CCI_REG8(0x399f), 0xcd }, { CCI_REG8(0x39a0), 0xcd },
++ { CCI_REG8(0x39a1), 0xcd }, { CCI_REG8(0x39a2), 0xcd },
++ { CCI_REG8(0x39a3), 0xcd }, { CCI_REG8(0x39a4), 0xcd },
++ { CCI_REG8(0x39a5), 0xcd }, { CCI_REG8(0x39a6), 0xcd },
++ { CCI_REG8(0x39a7), 0xcd }, { CCI_REG8(0x39a8), 0xcd },
++ { CCI_REG8(0x39a9), 0xcd }, { CCI_REG8(0x39aa), 0xcd },
++ { CCI_REG8(0x39ab), 0xcd }, { CCI_REG8(0x39ac), 0xcd },
++ { CCI_REG8(0x39ad), 0xcd }, { CCI_REG8(0x39ae), 0xcd },
++ { CCI_REG8(0x39af), 0xcd }, { CCI_REG8(0x39b0), 0xcd },
++ { CCI_REG8(0x39b1), 0xcd }, { CCI_REG8(0x39b2), 0xcd },
++ { CCI_REG8(0x39b3), 0xcd }, { CCI_REG8(0x39b4), 0xcd },
++ { CCI_REG8(0x39b5), 0xcd }, { CCI_REG8(0x39b6), 0xcd },
++ { CCI_REG8(0x39b7), 0xcd }, { CCI_REG8(0x39b8), 0xcd },
++ { CCI_REG8(0x39b9), 0xcd }, { CCI_REG8(0x39ba), 0xcd },
++ { CCI_REG8(0x39bb), 0xcd }, { CCI_REG8(0x39bc), 0xcd },
++ { CCI_REG8(0x39bd), 0xcd }, { CCI_REG8(0x39be), 0xcd },
++ { CCI_REG8(0x39bf), 0xcd }, { CCI_REG8(0x39c0), 0xcd },
++ { CCI_REG8(0x39c1), 0xcd }, { CCI_REG8(0x39c2), 0xcd },
++ { CCI_REG8(0x39c3), 0xcd }, { CCI_REG8(0x39c4), 0xcd },
++ { CCI_REG8(0x39c5), 0xcd }, { CCI_REG8(0x39c6), 0xcd },
++ { CCI_REG8(0x39c7), 0xcd }, { CCI_REG8(0x39c8), 0xcd },
++ { CCI_REG8(0x39c9), 0xcd }, { CCI_REG8(0x39ca), 0xcd },
++ { CCI_REG8(0x39cb), 0xcd }, { CCI_REG8(0x39cc), 0xcd },
++ { CCI_REG8(0x39cd), 0xcd }, { CCI_REG8(0x39ce), 0xcd },
++ { CCI_REG8(0x39cf), 0xcd }, { CCI_REG8(0x39d0), 0xcd },
++ { CCI_REG8(0x39d1), 0xcd }, { CCI_REG8(0x39d2), 0xcd },
++ { CCI_REG8(0x39d3), 0xcd }, { CCI_REG8(0x39d4), 0xcd },
++ { CCI_REG8(0x39d5), 0xcd }, { CCI_REG8(0x39d6), 0xcd },
++ { CCI_REG8(0x39d7), 0xcd }, { CCI_REG8(0x39d8), 0xcd },
++ { CCI_REG8(0x39d9), 0xcd }, { CCI_REG8(0x39da), 0xcd },
++ { CCI_REG8(0x39db), 0xcd }, { CCI_REG8(0x39dc), 0xcd },
++ { CCI_REG8(0x39dd), 0xcd }, { CCI_REG8(0x39de), 0xcd },
++ { CCI_REG8(0x39df), 0xcd }, { CCI_REG8(0x39e0), 0xcd },
++ { CCI_REG8(0x39e1), 0x40 }, { CCI_REG8(0x39e2), 0x40 },
++ { CCI_REG8(0x39e3), 0x40 }, { CCI_REG8(0x39e4), 0x40 },
++ { CCI_REG8(0x39e5), 0x40 }, { CCI_REG8(0x39e6), 0x40 },
++ { CCI_REG8(0x39e7), 0x40 }, { CCI_REG8(0x39e8), 0x40 },
++ { CCI_REG8(0x39e9), 0x40 }, { CCI_REG8(0x39ea), 0x40 },
++ { CCI_REG8(0x39eb), 0x40 }, { CCI_REG8(0x39ec), 0x40 },
++ { CCI_REG8(0x39ed), 0x40 }, { CCI_REG8(0x39ee), 0x40 },
++ { CCI_REG8(0x39ef), 0x40 }, { CCI_REG8(0x39f0), 0x40 },
++ { CCI_REG8(0x39f1), 0x40 }, { CCI_REG8(0x39f2), 0x40 },
++ { CCI_REG8(0x39f3), 0x40 }, { CCI_REG8(0x39f4), 0x40 },
++ { CCI_REG8(0x39f5), 0x40 }, { CCI_REG8(0x39f6), 0x40 },
++ { CCI_REG8(0x39f7), 0x40 }, { CCI_REG8(0x39f8), 0x40 },
++ { CCI_REG8(0x39f9), 0x40 }, { CCI_REG8(0x39fa), 0x40 },
++ { CCI_REG8(0x39fb), 0x40 }, { CCI_REG8(0x39fc), 0x40 },
++ { CCI_REG8(0x39fd), 0x40 }, { CCI_REG8(0x39fe), 0x40 },
++ { CCI_REG8(0x39ff), 0x40 }, { CCI_REG8(0x3a00), 0x40 },
++ { CCI_REG8(0x3a01), 0x40 }, { CCI_REG8(0x3a02), 0x40 },
++ { CCI_REG8(0x3a03), 0x40 }, { CCI_REG8(0x3a04), 0x40 },
++ { CCI_REG8(0x3a05), 0x40 }, { CCI_REG8(0x3a06), 0x40 },
++ { CCI_REG8(0x3a07), 0x40 }, { CCI_REG8(0x3a08), 0x40 },
++ { CCI_REG8(0x3a09), 0x40 }, { CCI_REG8(0x3a0a), 0x40 },
++ { CCI_REG8(0x3a0b), 0x40 }, { CCI_REG8(0x3a0c), 0x40 },
++ { CCI_REG8(0x3a0d), 0x40 }, { CCI_REG8(0x3a0e), 0x40 },
++ { CCI_REG8(0x3a0f), 0x40 }, { CCI_REG8(0x3a10), 0x40 },
++ { CCI_REG8(0x3a11), 0x40 }, { CCI_REG8(0x3a12), 0x40 },
++ { CCI_REG8(0x3a13), 0x40 }, { CCI_REG8(0x3a14), 0x40 },
++ { CCI_REG8(0x3a15), 0x40 }, { CCI_REG8(0x3a16), 0x40 },
++ { CCI_REG8(0x3a17), 0x40 }, { CCI_REG8(0x3a18), 0x40 },
++ { CCI_REG8(0x3a19), 0x40 }, { CCI_REG8(0x3a1a), 0x40 },
++ { CCI_REG8(0x3a1b), 0x40 }, { CCI_REG8(0x3a1c), 0x40 },
++ { CCI_REG8(0x3a1d), 0x40 }, { CCI_REG8(0x3a1e), 0x40 },
++ { CCI_REG8(0x3a1f), 0x40 }, { CCI_REG8(0x3a20), 0x40 },
++ { CCI_REG8(0x3a21), 0x40 }, { CCI_REG8(0x3a22), 0x40 },
++ { CCI_REG8(0x3a23), 0x40 }, { CCI_REG8(0x3a24), 0x40 },
++ { CCI_REG8(0x3a25), 0x40 }, { CCI_REG8(0x3a26), 0x40 },
++ { CCI_REG8(0x3a27), 0x40 }, { CCI_REG8(0x3a28), 0x40 },
++ { CCI_REG8(0x3a29), 0x40 }, { CCI_REG8(0x3a2a), 0x40 },
++ { CCI_REG8(0x3a2b), 0x40 }, { CCI_REG8(0x3a2c), 0x40 },
++ { CCI_REG8(0x3a2d), 0x40 }, { CCI_REG8(0x3a2e), 0x40 },
++ { CCI_REG8(0x3a2f), 0x40 }, { CCI_REG8(0x3a30), 0x40 },
++ { CCI_REG8(0x3a31), 0x40 }, { CCI_REG8(0x3a32), 0x40 },
++ { CCI_REG8(0x3a33), 0x40 }, { CCI_REG8(0x3a34), 0x40 },
++ { CCI_REG8(0x3a35), 0x40 }, { CCI_REG8(0x3a36), 0x40 },
++ { CCI_REG8(0x3a37), 0x40 }, { CCI_REG8(0x3a38), 0x40 },
++ { CCI_REG8(0x3a39), 0x40 }, { CCI_REG8(0x3a3a), 0x40 },
++ { CCI_REG8(0x3a3b), 0xcd }, { CCI_REG8(0x3a3c), 0xcd },
++ { CCI_REG8(0x3a3d), 0xcd }, { CCI_REG8(0x3a3e), 0xcd },
++ { CCI_REG8(0x3a3f), 0xcd }, { CCI_REG8(0x3a40), 0xcd },
++ { CCI_REG8(0x3a41), 0xcd }, { CCI_REG8(0x3a42), 0xcd },
++ { CCI_REG8(0x3a43), 0xcd }, { CCI_REG8(0x3a44), 0xcd },
++ { CCI_REG8(0x3a45), 0xcd }, { CCI_REG8(0x3a46), 0xcd },
++ { CCI_REG8(0x3a47), 0xcd }, { CCI_REG8(0x3a48), 0xcd },
++ { CCI_REG8(0x3a49), 0xcd }, { CCI_REG8(0x3a4a), 0xcd },
++ { CCI_REG8(0x3a4b), 0xcd }, { CCI_REG8(0x3a4c), 0xcd },
++ { CCI_REG8(0x3a4d), 0xcd }, { CCI_REG8(0x3a4e), 0xcd },
++ { CCI_REG8(0x3a4f), 0xcd }, { CCI_REG8(0x3a50), 0xcd },
++ { CCI_REG8(0x3a51), 0xcd }, { CCI_REG8(0x3a52), 0xcd },
++ { CCI_REG8(0x3a53), 0xcd }, { CCI_REG8(0x3a54), 0xcd },
++ { CCI_REG8(0x3a55), 0xcd }, { CCI_REG8(0x3a56), 0xcd },
++ { CCI_REG8(0x3a57), 0xcd }, { CCI_REG8(0x3a58), 0xcd },
++ { CCI_REG8(0x3a59), 0xcd }, { CCI_REG8(0x3a5a), 0xcd },
++ { CCI_REG8(0x3a5b), 0xcd }, { CCI_REG8(0x3a5c), 0xcd },
++ { CCI_REG8(0x3a5d), 0xcd }, { CCI_REG8(0x3a5e), 0xcd },
++ { CCI_REG8(0x3a5f), 0xcd }, { CCI_REG8(0x3a60), 0xcd },
++ { CCI_REG8(0x3a61), 0xcd }, { CCI_REG8(0x3a62), 0xcd },
++ { CCI_REG8(0x3a63), 0xcd }, { CCI_REG8(0x3a64), 0xcd },
++ { CCI_REG8(0x3a65), 0xcd }, { CCI_REG8(0x3a66), 0xcd },
++ { CCI_REG8(0x3a67), 0xcd }, { CCI_REG8(0x3a68), 0xcd },
++ { CCI_REG8(0x3a69), 0xcd }, { CCI_REG8(0x3a6a), 0xcd },
++ { CCI_REG8(0x3a6b), 0xcd }, { CCI_REG8(0x3a6c), 0xcd },
++ { CCI_REG8(0x3a6d), 0xcd }, { CCI_REG8(0x3a6e), 0xcd },
++ { CCI_REG8(0x3a6f), 0xcd }, { CCI_REG8(0x3a70), 0xcd },
++ { CCI_REG8(0x3a71), 0xcd }, { CCI_REG8(0x3a72), 0xcd },
++ { CCI_REG8(0x3a73), 0xcd }, { CCI_REG8(0x3a74), 0xcd },
++ { CCI_REG8(0x3a75), 0xcd }, { CCI_REG8(0x3a76), 0xcd },
++ { CCI_REG8(0x3a77), 0xcd }, { CCI_REG8(0x3a78), 0xcd },
++ { CCI_REG8(0x3a79), 0xcd }, { CCI_REG8(0x3a7a), 0xcd },
++ { CCI_REG8(0x3a7b), 0xcd }, { CCI_REG8(0x3a7c), 0xcd },
++ { CCI_REG8(0x3a7d), 0xcd }, { CCI_REG8(0x3a7e), 0xcd },
++ { CCI_REG8(0x3a7f), 0xcd }, { CCI_REG8(0x3a80), 0xcd },
++ { CCI_REG8(0x3a81), 0xcd }, { CCI_REG8(0x3a82), 0xcd },
++ { CCI_REG8(0x3a83), 0xcd }, { CCI_REG8(0x3a84), 0xcd },
++ { CCI_REG8(0x3a85), 0xcd }, { CCI_REG8(0x3a86), 0xcd },
++ { CCI_REG8(0x3a87), 0xcd }, { CCI_REG8(0x3a88), 0xcd },
++ { CCI_REG8(0x3a89), 0xcd }, { CCI_REG8(0x3a8a), 0xcd },
++ { CCI_REG8(0x3a8b), 0xcd }, { CCI_REG8(0x3a8c), 0xcd },
++ { CCI_REG8(0x3a8d), 0xcd }, { CCI_REG8(0x3a8e), 0xcd },
++ { CCI_REG8(0x3a8f), 0xcd }, { CCI_REG8(0x3a90), 0xcd },
++ { CCI_REG8(0x3a91), 0xcd }, { CCI_REG8(0x3a92), 0xcd },
++ { CCI_REG8(0x3a93), 0xcd }, { CCI_REG8(0x3a94), 0xcd },
++ { CCI_REG8(0x3a95), 0x40 }, { CCI_REG8(0x3a96), 0x40 },
++ { CCI_REG8(0x3a97), 0x40 }, { CCI_REG8(0x3a98), 0x40 },
++ { CCI_REG8(0x3a99), 0x40 }, { CCI_REG8(0x3a9a), 0x40 },
++ { CCI_REG8(0x3a9b), 0x40 }, { CCI_REG8(0x3a9c), 0x40 },
++ { CCI_REG8(0x3a9d), 0x40 }, { CCI_REG8(0x3a9e), 0x40 },
++ { CCI_REG8(0x3a9f), 0x40 }, { CCI_REG8(0x3aa0), 0x40 },
++ { CCI_REG8(0x3aa1), 0x40 }, { CCI_REG8(0x3aa2), 0x40 },
++ { CCI_REG8(0x3aa3), 0x40 }, { CCI_REG8(0x3aa4), 0x40 },
++ { CCI_REG8(0x3aa5), 0x40 }, { CCI_REG8(0x3aa6), 0x40 },
++ { CCI_REG8(0x3aa7), 0x40 }, { CCI_REG8(0x3aa8), 0x40 },
++ { CCI_REG8(0x3aa9), 0x40 }, { CCI_REG8(0x3aaa), 0x40 },
++ { CCI_REG8(0x3aab), 0x40 }, { CCI_REG8(0x3aac), 0x40 },
++ { CCI_REG8(0x3aad), 0x40 }, { CCI_REG8(0x3aae), 0x40 },
++ { CCI_REG8(0x3aaf), 0x40 }, { CCI_REG8(0x3ab0), 0x40 },
++ { CCI_REG8(0x3ab1), 0x40 }, { CCI_REG8(0x3ab2), 0x40 },
++ { CCI_REG8(0x3ab3), 0x40 }, { CCI_REG8(0x3ab4), 0x40 },
++ { CCI_REG8(0x3ab5), 0x40 }, { CCI_REG8(0x3ab6), 0x40 },
++ { CCI_REG8(0x3ab7), 0x40 }, { CCI_REG8(0x3ab8), 0x40 },
++ { CCI_REG8(0x3ab9), 0x40 }, { CCI_REG8(0x3aba), 0x40 },
++ { CCI_REG8(0x3abb), 0x40 }, { CCI_REG8(0x3abc), 0x40 },
++ { CCI_REG8(0x3abd), 0x40 }, { CCI_REG8(0x3abe), 0x40 },
++ { CCI_REG8(0x3abf), 0x40 }, { CCI_REG8(0x3ac0), 0x40 },
++ { CCI_REG8(0x3ac1), 0x40 }, { CCI_REG8(0x3ac2), 0x40 },
++ { CCI_REG8(0x3ac3), 0x40 }, { CCI_REG8(0x3ac4), 0x40 },
++ { CCI_REG8(0x3ac5), 0x40 }, { CCI_REG8(0x3ac6), 0x40 },
++ { CCI_REG8(0x3ac7), 0x40 }, { CCI_REG8(0x3ac8), 0x40 },
++ { CCI_REG8(0x3ac9), 0x40 }, { CCI_REG8(0x3aca), 0x40 },
++ { CCI_REG8(0x3acb), 0x40 }, { CCI_REG8(0x3acc), 0x40 },
++ { CCI_REG8(0x3acd), 0x40 }, { CCI_REG8(0x3ace), 0x40 },
++ { CCI_REG8(0x3acf), 0x40 }, { CCI_REG8(0x3ad0), 0x40 },
++ { CCI_REG8(0x3ad1), 0x40 }, { CCI_REG8(0x3ad2), 0x40 },
++ { CCI_REG8(0x3ad3), 0x40 }, { CCI_REG8(0x3ad4), 0x40 },
++ { CCI_REG8(0x3ad5), 0x40 }, { CCI_REG8(0x3ad6), 0x40 },
++ { CCI_REG8(0x3ad7), 0x40 }, { CCI_REG8(0x3ad8), 0x40 },
++ { CCI_REG8(0x3ad9), 0x40 }, { CCI_REG8(0x3ada), 0x40 },
++ { CCI_REG8(0x3adb), 0x40 }, { CCI_REG8(0x3adc), 0x40 },
++ { CCI_REG8(0x3add), 0x40 }, { CCI_REG8(0x3ade), 0x40 },
++ { CCI_REG8(0x3adf), 0x40 }, { CCI_REG8(0x3ae0), 0x40 },
++ { CCI_REG8(0x3ae1), 0x40 }, { CCI_REG8(0x3ae2), 0x40 },
++ { CCI_REG8(0x3ae3), 0x40 }, { CCI_REG8(0x3ae4), 0x40 },
++ { CCI_REG8(0x3ae5), 0x40 }, { CCI_REG8(0x3ae6), 0x40 },
++ { CCI_REG8(0x3ae7), 0x40 }, { CCI_REG8(0x3ae8), 0x40 },
++ { CCI_REG8(0x3ae9), 0x40 }, { CCI_REG8(0x3aea), 0x40 },
++ { CCI_REG8(0x3aeb), 0x40 }, { CCI_REG8(0x3aec), 0x40 },
++ { CCI_REG8(0x3aed), 0x40 }, { CCI_REG8(0x3aee), 0x40 },
++ { CCI_REG8(0x3aef), 0xcd }, { CCI_REG8(0x3af0), 0xcd },
++ { CCI_REG8(0x3af1), 0xcd }, { CCI_REG8(0x3af2), 0xcd },
++ { CCI_REG8(0x3af3), 0xcd }, { CCI_REG8(0x3af4), 0xcd },
++ { CCI_REG8(0x3af5), 0xcd }, { CCI_REG8(0x3af6), 0xcd },
++ { CCI_REG8(0x3af7), 0xcd }, { CCI_REG8(0x3af8), 0xcd },
++ { CCI_REG8(0x3af9), 0xcd }, { CCI_REG8(0x3afa), 0xcd },
++ { CCI_REG8(0x3afb), 0xcd }, { CCI_REG8(0x3afc), 0xcd },
++ { CCI_REG8(0x3afd), 0xcd }, { CCI_REG8(0x3afe), 0xcd },
++ { CCI_REG8(0x3aff), 0xcd }, { CCI_REG8(0x3b00), 0xcd },
++ { CCI_REG8(0x3b01), 0xcd }, { CCI_REG8(0x3b02), 0xcd },
++ { CCI_REG8(0x3b03), 0xcd }, { CCI_REG8(0x3b04), 0xcd },
++ { CCI_REG8(0x3b05), 0xcd }, { CCI_REG8(0x3b06), 0xcd },
++ { CCI_REG8(0x3b07), 0xcd }, { CCI_REG8(0x3b08), 0xcd },
++ { CCI_REG8(0x3b09), 0xcd }, { CCI_REG8(0x3b0a), 0xcd },
++ { CCI_REG8(0x3b0b), 0xcd }, { CCI_REG8(0x3b0c), 0xcd },
++ { CCI_REG8(0x3b0d), 0xcd }, { CCI_REG8(0x3b0e), 0xcd },
++ { CCI_REG8(0x3b0f), 0xcd }, { CCI_REG8(0x3b10), 0xcd },
++ { CCI_REG8(0x3b11), 0xcd }, { CCI_REG8(0x3b12), 0xcd },
++ { CCI_REG8(0x3b13), 0xcd }, { CCI_REG8(0x3b14), 0xcd },
++ { CCI_REG8(0x3b15), 0xcd }, { CCI_REG8(0x3b16), 0xcd },
++ { CCI_REG8(0x3b17), 0xcd }, { CCI_REG8(0x3b18), 0xcd },
++ { CCI_REG8(0x3b19), 0xcd }, { CCI_REG8(0x3b1a), 0xcd },
++ { CCI_REG8(0x3b1b), 0xcd }, { CCI_REG8(0x3b1c), 0xcd },
++ { CCI_REG8(0x3b1d), 0xcd }, { CCI_REG8(0x3b1e), 0xcd },
++ { CCI_REG8(0x3b1f), 0xcd }, { CCI_REG8(0x3b20), 0xcd },
++ { CCI_REG8(0x3b21), 0xcd }, { CCI_REG8(0x3b22), 0xcd },
++ { CCI_REG8(0x3b23), 0xcd }, { CCI_REG8(0x3b24), 0xcd },
++ { CCI_REG8(0x3b25), 0xcd }, { CCI_REG8(0x3b26), 0xcd },
++ { CCI_REG8(0x3b27), 0xcd }, { CCI_REG8(0x3b28), 0xcd },
++ { CCI_REG8(0x3b29), 0xcd }, { CCI_REG8(0x3b2a), 0xcd },
++ { CCI_REG8(0x3b2b), 0xcd }, { CCI_REG8(0x3b2c), 0xcd },
++ { CCI_REG8(0x3b2d), 0xcd }, { CCI_REG8(0x3b2e), 0xcd },
++ { CCI_REG8(0x3b2f), 0xcd }, { CCI_REG8(0x3b30), 0xcd },
++ { CCI_REG8(0x3b31), 0xcd }, { CCI_REG8(0x3b32), 0xcd },
++ { CCI_REG8(0x3b33), 0xcd }, { CCI_REG8(0x3b34), 0xcd },
++ { CCI_REG8(0x3b35), 0xcd }, { CCI_REG8(0x3b36), 0xcd },
++ { CCI_REG8(0x3b37), 0xcd }, { CCI_REG8(0x3b38), 0xcd },
++ { CCI_REG8(0x3b39), 0xcd }, { CCI_REG8(0x3b3a), 0xcd },
++ { CCI_REG8(0x3b3b), 0xcd }, { CCI_REG8(0x3b3c), 0xcd },
++ { CCI_REG8(0x3b3d), 0xcd }, { CCI_REG8(0x3b3e), 0xcd },
++ { CCI_REG8(0x3b3f), 0xcd }, { CCI_REG8(0x3b40), 0xcd },
++ { CCI_REG8(0x3b41), 0xcd }, { CCI_REG8(0x3b42), 0xcd },
++ { CCI_REG8(0x3b43), 0xcd }, { CCI_REG8(0x3b44), 0xcd },
++ { CCI_REG8(0x3b45), 0xcd }, { CCI_REG8(0x3b46), 0xcd },
++ { CCI_REG8(0x3b47), 0xcd }, { CCI_REG8(0x3b48), 0xcd },
++ { CCI_REG8(0x3b49), 0xcd }, { CCI_REG8(0x3b4a), 0xcd },
++ { CCI_REG8(0x3b4b), 0xcd }, { CCI_REG8(0x3b4c), 0xcd },
++ { CCI_REG8(0x3b4d), 0xcd }, { CCI_REG8(0x3b4e), 0xcd },
++ { CCI_REG8(0x3b4f), 0xcd }, { CCI_REG8(0x3b50), 0xcd },
++ { CCI_REG8(0x3b51), 0xcd }, { CCI_REG8(0x3b52), 0xcd },
++ { CCI_REG8(0x3b53), 0xcd }, { CCI_REG8(0x3b54), 0xcd },
++ { CCI_REG8(0x3b55), 0xcd }, { CCI_REG8(0x3b56), 0xcd },
++ { CCI_REG8(0x3b57), 0xcd }, { CCI_REG8(0x3b58), 0xcd },
++ { CCI_REG8(0x3b59), 0xcd }, { CCI_REG8(0x3b5a), 0xcd },
++ { CCI_REG8(0x3b5b), 0xcd }, { CCI_REG8(0x3b5c), 0xcd },
++ { CCI_REG8(0x3b5d), 0xcd }, { CCI_REG8(0x3b5e), 0xcd },
++ { CCI_REG8(0x3b5f), 0xcd }, { CCI_REG8(0x3b60), 0xcd },
++ { CCI_REG8(0x3b61), 0xcd }, { CCI_REG8(0x3b62), 0xcd },
++ { CCI_REG8(0x3b63), 0xcd }, { CCI_REG8(0x3b64), 0xcd },
++ { CCI_REG8(0x3b65), 0xcd }, { CCI_REG8(0x3b66), 0xcd },
++ { CCI_REG8(0x3b67), 0xcd }, { CCI_REG8(0x3b68), 0xcd },
++ { CCI_REG8(0x3b69), 0xcd }, { CCI_REG8(0x3b6a), 0xcd },
++ { CCI_REG8(0x3b6b), 0xcd }, { CCI_REG8(0x3b6c), 0xcd },
++ { CCI_REG8(0x3b6d), 0xcd }, { CCI_REG8(0x3b6e), 0xcd },
++ { CCI_REG8(0x3b6f), 0xcd }, { CCI_REG8(0x3b70), 0xcd },
++ { CCI_REG8(0x3b71), 0xcd }, { CCI_REG8(0x3b72), 0xcd },
++ { CCI_REG8(0x3b73), 0xcd }, { CCI_REG8(0x3b74), 0xcd },
++ { CCI_REG8(0x3b75), 0xcd }, { CCI_REG8(0x3b76), 0xcd },
++ { CCI_REG8(0x3b77), 0xcd }, { CCI_REG8(0x3b78), 0xcd },
++ { CCI_REG8(0x3b79), 0xcd }, { CCI_REG8(0x3b7a), 0xcd },
++ { CCI_REG8(0x3b7b), 0xcd }, { CCI_REG8(0x3b7c), 0xcd },
++ { CCI_REG8(0x3b7d), 0xcd }, { CCI_REG8(0x3b7e), 0xcd },
++ { CCI_REG8(0x3b7f), 0xcd }, { CCI_REG8(0x3b80), 0xcd },
++ { CCI_REG8(0x3b81), 0xcd }, { CCI_REG8(0x3b82), 0xcd },
++ { CCI_REG8(0x3b83), 0xcd }, { CCI_REG8(0x3b84), 0xcd },
++ { CCI_REG8(0x3b85), 0xcd }, { CCI_REG8(0x3b86), 0xcd },
++ { CCI_REG8(0x3b87), 0xcd }, { CCI_REG8(0x3b88), 0xcd },
++ { CCI_REG8(0x3b89), 0xcd }, { CCI_REG8(0x3b8a), 0xcd },
++ { CCI_REG8(0x3b8b), 0xcd }, { CCI_REG8(0x3b8c), 0xcd },
++ { CCI_REG8(0x3b8d), 0xcd }, { CCI_REG8(0x3b8e), 0xcd },
++ { CCI_REG8(0x3b8f), 0xcd }, { CCI_REG8(0x3b90), 0xcd },
++ { CCI_REG8(0x3b91), 0xcd }, { CCI_REG8(0x3b92), 0xcd },
++ { CCI_REG8(0x3b93), 0xcd }, { CCI_REG8(0x3b94), 0xcd },
++ { CCI_REG8(0x3b95), 0xcd }, { CCI_REG8(0x3b96), 0xcd },
++ { CCI_REG8(0x3b97), 0xcd }, { CCI_REG8(0x3b98), 0xcd },
++ { CCI_REG8(0x3b99), 0xcd }, { CCI_REG8(0x3b9a), 0xcd },
++ { CCI_REG8(0x3b9b), 0xcd }, { CCI_REG8(0x3b9c), 0xcd },
++ { CCI_REG8(0x3b9d), 0xcd }, { CCI_REG8(0x3b9e), 0xcd },
++ { CCI_REG8(0x3b9f), 0xcd }, { CCI_REG8(0x3ba0), 0xcd },
++ { CCI_REG8(0x3ba1), 0xcd }, { CCI_REG8(0x3ba2), 0xcd },
++ { CCI_REG8(0x3ba3), 0xcd }, { CCI_REG8(0x3ba4), 0xcd },
++ { CCI_REG8(0x3ba5), 0xcd }, { CCI_REG8(0x3ba6), 0xcd },
++ { CCI_REG8(0x3ba7), 0xcd }, { CCI_REG8(0x3ba8), 0xcd },
++ { CCI_REG8(0x3ba9), 0xcd }, { CCI_REG8(0x3baa), 0xcd },
++ { CCI_REG8(0x3bab), 0xcd }, { CCI_REG8(0x3bac), 0xcd },
++ { CCI_REG8(0x3bad), 0xcd }, { CCI_REG8(0x3bae), 0xcd },
++ { CCI_REG8(0x3baf), 0xcd }, { CCI_REG8(0x3bb0), 0xcd },
++ { CCI_REG8(0x3bb1), 0xcd }, { CCI_REG8(0x3bb2), 0xcd },
++ { CCI_REG8(0x3bb3), 0xcd }, { CCI_REG8(0x3bb4), 0xcd },
++ { CCI_REG8(0x3bb5), 0xcd }, { CCI_REG8(0x3bb6), 0xcd },
++ { CCI_REG8(0x3bb7), 0xcd }, { CCI_REG8(0x3bb8), 0xcd },
++ { CCI_REG8(0x3bb9), 0xcd }, { CCI_REG8(0x3bba), 0xcd },
++ { CCI_REG8(0x3bbb), 0xcd }, { CCI_REG8(0x3bbc), 0xcd },
++ { CCI_REG8(0x3bbd), 0xcd }, { CCI_REG8(0x3bbe), 0xcd },
++ { CCI_REG8(0x3bbf), 0xcd }, { CCI_REG8(0x3bc0), 0xcd },
++ { CCI_REG8(0x3bc1), 0xcd }, { CCI_REG8(0x3bc2), 0xcd },
++ { CCI_REG8(0x3bc3), 0xcd }, { CCI_REG8(0x3bc4), 0xcd },
++ { CCI_REG8(0x3bc5), 0xcd }, { CCI_REG8(0x3bc6), 0xcd },
++ { CCI_REG8(0x3bc7), 0xcd }, { CCI_REG8(0x3bc8), 0xcd },
++ { CCI_REG8(0x3bc9), 0xcd }, { CCI_REG8(0x3bca), 0xcd },
++ { CCI_REG8(0x3bcb), 0xcd }, { CCI_REG8(0x3bcc), 0xcd },
++ { CCI_REG8(0x3bcd), 0xcd }, { CCI_REG8(0x3bce), 0xcd },
++ { CCI_REG8(0x3bcf), 0xcd }, { CCI_REG8(0x3bd0), 0xcd },
++ { CCI_REG8(0x3bd1), 0xcd }, { CCI_REG8(0x3bd2), 0xcd },
++ { CCI_REG8(0x3bd3), 0xcd }, { CCI_REG8(0x3bd4), 0xcd },
++ { CCI_REG8(0x3bd5), 0xcd }, { CCI_REG8(0x3bd6), 0xcd },
++ { CCI_REG8(0x3bd7), 0xcd }, { CCI_REG8(0x3bd8), 0xcd },
++ { CCI_REG8(0x3bd9), 0xcd }, { CCI_REG8(0x3bda), 0xcd },
++ { CCI_REG8(0x3bdb), 0xcd }, { CCI_REG8(0x3bdc), 0xcd },
++ { CCI_REG8(0x3bdd), 0xcd }, { CCI_REG8(0x3bde), 0xcd },
++ { CCI_REG8(0x3bdf), 0xcd }, { CCI_REG8(0x3be0), 0xcd },
++ { CCI_REG8(0x3be1), 0xcd }, { CCI_REG8(0x3be2), 0xcd },
++ { CCI_REG8(0x3be3), 0xcd }, { CCI_REG8(0x3be4), 0xcd },
++ { CCI_REG8(0x3be5), 0xcd }, { CCI_REG8(0x3be6), 0xcd },
++ { CCI_REG8(0x3be7), 0xcd }, { CCI_REG8(0x3be8), 0xcd },
++ { CCI_REG8(0x3be9), 0xcd }, { CCI_REG8(0x3bea), 0xcd },
++ { CCI_REG8(0x3beb), 0xcd }, { CCI_REG8(0x3bec), 0xcd },
++ { CCI_REG8(0x3bed), 0xcd }, { CCI_REG8(0x3bee), 0xcd },
++ { CCI_REG8(0x3bef), 0xcd }, { CCI_REG8(0x3bf0), 0xcd },
++ { CCI_REG8(0x3bf1), 0xcd }, { CCI_REG8(0x3bf2), 0xcd },
++ { CCI_REG8(0x3bf3), 0xcd }, { CCI_REG8(0x3bf4), 0xcd },
++ { CCI_REG8(0x3bf5), 0xcd }, { CCI_REG8(0x3bf6), 0xcd },
++ { CCI_REG8(0x3bf7), 0xcd }, { CCI_REG8(0x3bf8), 0xcd },
++ { CCI_REG8(0x3bf9), 0xcd }, { CCI_REG8(0x3bfa), 0xcd },
++ { CCI_REG8(0x3bfb), 0xcd }, { CCI_REG8(0x3bfc), 0xcd },
++ { CCI_REG8(0x3bfd), 0xcd }, { CCI_REG8(0x3bfe), 0xcd },
++ { CCI_REG8(0x3bff), 0xcd }, { CCI_REG8(0x3c00), 0xcd },
++ { CCI_REG8(0x3c01), 0xcd }, { CCI_REG8(0x3c02), 0xcd },
++ { CCI_REG8(0x3c03), 0xcd }, { CCI_REG8(0x3c04), 0xcd },
++ { CCI_REG8(0x3c05), 0xcd }, { CCI_REG8(0x3c06), 0xcd },
++ { CCI_REG8(0x3c07), 0xcd }, { CCI_REG8(0x3c08), 0xcd },
++ { CCI_REG8(0x3c09), 0xcd }, { CCI_REG8(0x3c0a), 0xcd },
++ { CCI_REG8(0x3c0b), 0xcd }, { CCI_REG8(0x3c0c), 0xcd },
++ { CCI_REG8(0x3c0d), 0xcd }, { CCI_REG8(0x3c0e), 0xcd },
++ { CCI_REG8(0x3c0f), 0xcd }, { CCI_REG8(0x3c10), 0xcd },
++ { CCI_REG8(0x3c11), 0xcd }, { CCI_REG8(0x3c12), 0xcd },
++ { CCI_REG8(0x3c13), 0xcd }, { CCI_REG8(0x3c14), 0xcd },
++ { CCI_REG8(0x3c15), 0xcd }, { CCI_REG8(0x3c16), 0xcd },
++ { CCI_REG8(0x3c17), 0xcd }, { CCI_REG8(0x3c18), 0xcd },
++ { CCI_REG8(0x3c19), 0xcd }, { CCI_REG8(0x3c1a), 0xcd },
++ { CCI_REG8(0x3c1b), 0xcd }, { CCI_REG8(0x3c1c), 0xcd },
++ { CCI_REG8(0x3c1d), 0xcd }, { CCI_REG8(0x3c1e), 0xcd },
++ { CCI_REG8(0x3c1f), 0xcd }, { CCI_REG8(0x3c20), 0xcd },
++ { CCI_REG8(0x3c21), 0xcd }, { CCI_REG8(0x3c22), 0xcd },
++ { CCI_REG8(0x3c23), 0xcd }, { CCI_REG8(0x3c24), 0xcd },
++ { CCI_REG8(0x3c25), 0xcd }, { CCI_REG8(0x3c26), 0xcd },
++ { CCI_REG8(0x3c27), 0xcd }, { CCI_REG8(0x3c28), 0xcd },
++ { CCI_REG8(0x3c29), 0xcd }, { CCI_REG8(0x3c2a), 0xcd },
++ { CCI_REG8(0x3c2b), 0xcd }, { CCI_REG8(0x3c2c), 0xcd },
++ { CCI_REG8(0x3c2d), 0xcd }, { CCI_REG8(0x3c2e), 0xcd },
++ { CCI_REG8(0x3c2f), 0xcd }, { CCI_REG8(0x3c30), 0xcd },
++ { CCI_REG8(0x3c31), 0xcd }, { CCI_REG8(0x3c32), 0xcd },
++ { CCI_REG8(0x3c33), 0xcd }, { CCI_REG8(0x3c34), 0xcd },
++ { CCI_REG8(0x3c35), 0xcd }, { CCI_REG8(0x3c36), 0xcd },
++ { CCI_REG8(0x3c37), 0xcd }, { CCI_REG8(0x3c38), 0xcd },
++ { CCI_REG8(0x3c39), 0xcd }, { CCI_REG8(0x3c3a), 0xcd },
++ { CCI_REG8(0x3c3b), 0xcd }, { CCI_REG8(0x3c3c), 0xcd },
++ { CCI_REG8(0x3c3d), 0xcd }, { CCI_REG8(0x3c3e), 0xcd },
++ { CCI_REG8(0x3c3f), 0xcd }, { CCI_REG8(0x3c40), 0xcd },
++ { CCI_REG8(0x3c41), 0xcd }, { CCI_REG8(0x3c42), 0xcd },
++ { CCI_REG8(0x3c43), 0xcd }, { CCI_REG8(0x3c44), 0xcd },
++ { CCI_REG8(0x3c45), 0xcd }, { CCI_REG8(0x3c46), 0xcd },
++ { CCI_REG8(0x3c47), 0xcd }, { CCI_REG8(0x3c48), 0xcd },
++ { CCI_REG8(0x3c49), 0xcd }, { CCI_REG8(0x3c4a), 0xcd },
++ { CCI_REG8(0x3c4b), 0xcd }, { CCI_REG8(0x3c4c), 0xcd },
++ { CCI_REG8(0x3c4d), 0xcd }, { CCI_REG8(0x3c4e), 0xcd },
++ { CCI_REG8(0x3c4f), 0xcd }, { CCI_REG8(0x3c50), 0xcd },
++ { CCI_REG8(0x3c51), 0xcd }, { CCI_REG8(0x3c52), 0xcd },
++ { CCI_REG8(0x3c53), 0xcd }, { CCI_REG8(0x3c54), 0xcd },
++ { CCI_REG8(0x3c55), 0xcd }, { CCI_REG8(0x3c56), 0xcd },
++ { CCI_REG8(0x3c57), 0xcd }, { CCI_REG8(0x3c58), 0xcd },
++ { CCI_REG8(0x3c59), 0xcd }, { CCI_REG8(0x3c5a), 0xcd },
++ { CCI_REG8(0x3c5b), 0xcd }, { CCI_REG8(0x3c5c), 0xcd },
++ { CCI_REG8(0x3c5d), 0xcd }, { CCI_REG8(0x3c5e), 0xcd },
++ { CCI_REG8(0x3c5f), 0xcd }, { CCI_REG8(0x3c60), 0xcd },
++ { CCI_REG8(0x3c61), 0xcd }, { CCI_REG8(0x3c62), 0xcd },
++ { CCI_REG8(0x3c63), 0xcd }, { CCI_REG8(0x3c64), 0xcd },
++ { CCI_REG8(0x3c65), 0xcd }, { CCI_REG8(0x3c66), 0xcd },
++ { CCI_REG8(0x3c67), 0xcd }, { CCI_REG8(0x3c68), 0xcd },
++ { CCI_REG8(0x3c69), 0xcd }, { CCI_REG8(0x3c6a), 0xcd },
++ { CCI_REG8(0x3c6b), 0xcd }, { CCI_REG8(0x3c6c), 0xcd },
++ { CCI_REG8(0x3c6d), 0xcd }, { CCI_REG8(0x3c6e), 0xcd },
++ { CCI_REG8(0x3c6f), 0xcd }, { CCI_REG8(0x3c70), 0xcd },
++ { CCI_REG8(0x3c71), 0xcd }, { CCI_REG8(0x3c72), 0xcd },
++ { CCI_REG8(0x3c73), 0xcd }, { CCI_REG8(0x3c74), 0xcd },
++ { CCI_REG8(0x3c75), 0xcd }, { CCI_REG8(0x3c76), 0xcd },
++ { CCI_REG8(0x3c77), 0xcd }, { CCI_REG8(0x3c78), 0xcd },
++ { CCI_REG8(0x3c79), 0xcd }, { CCI_REG8(0x3c7a), 0xcd },
++ { CCI_REG8(0x3c7b), 0xcd }, { CCI_REG8(0x3c7c), 0xcd },
++ { CCI_REG8(0x3c7d), 0xcd }, { CCI_REG8(0x3c7e), 0xcd },
++ { CCI_REG8(0x3c7f), 0xcd }, { CCI_REG8(0x3c80), 0xcd },
++ { CCI_REG8(0x3c81), 0xcd }, { CCI_REG8(0x3c82), 0xcd },
++ { CCI_REG8(0x3c83), 0xcd }, { CCI_REG8(0x3c84), 0xcd },
++ { CCI_REG8(0x3c85), 0xcd }, { CCI_REG8(0x3c86), 0xcd },
++ { CCI_REG8(0x3c87), 0xcd }, { CCI_REG8(0x3c88), 0xcd },
++ { CCI_REG8(0x3c89), 0xcd }, { CCI_REG8(0x3c8a), 0xcd },
++ { CCI_REG8(0x3c8b), 0xcd }, { CCI_REG8(0x3c8c), 0xcd },
++ { CCI_REG8(0x3c8d), 0xcd }, { CCI_REG8(0x3c8e), 0xcd },
++ { CCI_REG8(0x3c8f), 0xcd }, { CCI_REG8(0x3c90), 0xcd },
++ { CCI_REG8(0x3c91), 0xcd }, { CCI_REG8(0x3c92), 0xcd },
++ { CCI_REG8(0x3c93), 0xcd }, { CCI_REG8(0x3c94), 0xcd },
++ { CCI_REG8(0x3c95), 0xcd }, { CCI_REG8(0x3c96), 0xcd },
++ { CCI_REG8(0x3c97), 0xcd }, { CCI_REG8(0x3c98), 0xcd },
++ { CCI_REG8(0x3c99), 0xcd }, { CCI_REG8(0x3c9a), 0xcd },
++ { CCI_REG8(0x3c9b), 0xcd }, { CCI_REG8(0x3c9c), 0xcd },
++ { CCI_REG8(0x3c9d), 0xcd }, { CCI_REG8(0x3c9e), 0xcd },
++ { CCI_REG8(0x3c9f), 0xcd }, { CCI_REG8(0x3ca0), 0xcd },
++ { CCI_REG8(0x3ca1), 0xcd }, { CCI_REG8(0x3ca2), 0xcd },
++ { CCI_REG8(0x3ca3), 0xcd }, { CCI_REG8(0x3ca4), 0xcd },
++ { CCI_REG8(0x3ca5), 0xcd }, { CCI_REG8(0x3ca6), 0xcd },
++ { CCI_REG8(0x3ca7), 0xcd }, { CCI_REG8(0x3ca8), 0xcd },
++ { CCI_REG8(0x3ca9), 0xcd }, { CCI_REG8(0x3caa), 0xcd },
++ { CCI_REG8(0x3cab), 0xcd }, { CCI_REG8(0x3cac), 0xcd },
++ { CCI_REG8(0x3cad), 0xcd }, { CCI_REG8(0x3cae), 0xcd },
++ { CCI_REG8(0x3caf), 0xcd }, { CCI_REG8(0x3cb0), 0xcd },
++ { CCI_REG8(0x3cb1), 0x40 }, { CCI_REG8(0x3cb2), 0x40 },
++ { CCI_REG8(0x3cb3), 0x40 }, { CCI_REG8(0x3cb4), 0x40 },
++ { CCI_REG8(0x3cb5), 0x40 }, { CCI_REG8(0x3cb6), 0x40 },
++ { CCI_REG8(0x3cb7), 0x40 }, { CCI_REG8(0x3cb8), 0x40 },
++ { CCI_REG8(0x3cb9), 0x40 }, { CCI_REG8(0x3cba), 0x40 },
++ { CCI_REG8(0x3cbb), 0x40 }, { CCI_REG8(0x3cbc), 0x40 },
++ { CCI_REG8(0x3cbd), 0x40 }, { CCI_REG8(0x3cbe), 0x40 },
++ { CCI_REG8(0x3cbf), 0x40 }, { CCI_REG8(0x3cc0), 0x40 },
++ { CCI_REG8(0x3cc1), 0x40 }, { CCI_REG8(0x3cc2), 0x40 },
++ { CCI_REG8(0x3cc3), 0x40 }, { CCI_REG8(0x3cc4), 0x40 },
++ { CCI_REG8(0x3cc5), 0x40 }, { CCI_REG8(0x3cc6), 0x40 },
++ { CCI_REG8(0x3cc7), 0x40 }, { CCI_REG8(0x3cc8), 0x40 },
++ { CCI_REG8(0x3cc9), 0x40 }, { CCI_REG8(0x3cca), 0x40 },
++ { CCI_REG8(0x3ccb), 0x40 }, { CCI_REG8(0x3ccc), 0x40 },
++ { CCI_REG8(0x3ccd), 0x40 }, { CCI_REG8(0x3cce), 0x40 },
++ { CCI_REG8(0x3ccf), 0x40 }, { CCI_REG8(0x3cd0), 0x40 },
++ { CCI_REG8(0x3cd1), 0x40 }, { CCI_REG8(0x3cd2), 0x40 },
++ { CCI_REG8(0x3cd3), 0x40 }, { CCI_REG8(0x3cd4), 0x40 },
++ { CCI_REG8(0x3cd5), 0x40 }, { CCI_REG8(0x3cd6), 0x40 },
++ { CCI_REG8(0x3cd7), 0x40 }, { CCI_REG8(0x3cd8), 0x40 },
++ { CCI_REG8(0x3cd9), 0x40 }, { CCI_REG8(0x3cda), 0x40 },
++ { CCI_REG8(0x3cdb), 0x40 }, { CCI_REG8(0x3cdc), 0x40 },
++ { CCI_REG8(0x3cdd), 0x40 }, { CCI_REG8(0x3cde), 0x40 },
++ { CCI_REG8(0x3cdf), 0x40 }, { CCI_REG8(0x3ce0), 0x40 },
++ { CCI_REG8(0x3ce1), 0x40 }, { CCI_REG8(0x3ce2), 0x40 },
++ { CCI_REG8(0x3ce3), 0x40 }, { CCI_REG8(0x3ce4), 0x40 },
++ { CCI_REG8(0x3ce5), 0x40 }, { CCI_REG8(0x3ce6), 0x40 },
++ { CCI_REG8(0x3ce7), 0x40 }, { CCI_REG8(0x3ce8), 0x40 },
++ { CCI_REG8(0x3ce9), 0x40 }, { CCI_REG8(0x3cea), 0x40 },
++ { CCI_REG8(0x3ceb), 0x40 }, { CCI_REG8(0x3cec), 0x40 },
++ { CCI_REG8(0x3ced), 0x40 }, { CCI_REG8(0x3cee), 0x40 },
++ { CCI_REG8(0x3cef), 0x40 }, { CCI_REG8(0x3cf0), 0x40 },
++ { CCI_REG8(0x3cf1), 0x40 }, { CCI_REG8(0x3cf2), 0x40 },
++ { CCI_REG8(0x3cf3), 0x40 }, { CCI_REG8(0x3cf4), 0x40 },
++ { CCI_REG8(0x3cf5), 0x40 }, { CCI_REG8(0x3cf6), 0x40 },
++ { CCI_REG8(0x3cf7), 0x40 }, { CCI_REG8(0x3cf8), 0x40 },
++ { CCI_REG8(0x3cf9), 0x40 }, { CCI_REG8(0x3cfa), 0x40 },
++ { CCI_REG8(0x3cfb), 0x40 }, { CCI_REG8(0x3cfc), 0x40 },
++ { CCI_REG8(0x3cfd), 0x40 }, { CCI_REG8(0x3cfe), 0x40 },
++ { CCI_REG8(0x3cff), 0x40 }, { CCI_REG8(0x3d00), 0x40 },
++ { CCI_REG8(0x3d01), 0x40 }, { CCI_REG8(0x3d02), 0x40 },
++ { CCI_REG8(0x3d03), 0x40 }, { CCI_REG8(0x3d04), 0x40 },
++ { CCI_REG8(0x3d05), 0x40 }, { CCI_REG8(0x3d06), 0x40 },
++ { CCI_REG8(0x3d07), 0x40 }, { CCI_REG8(0x3d08), 0x40 },
++ { CCI_REG8(0x3d09), 0x40 }, { CCI_REG8(0x3d0a), 0x40 },
++ { CCI_REG8(0x3d0b), 0xcd }, { CCI_REG8(0x3d0c), 0xcd },
++ { CCI_REG8(0x3d0d), 0xcd }, { CCI_REG8(0x3d0e), 0xcd },
++ { CCI_REG8(0x3d0f), 0xcd }, { CCI_REG8(0x3d10), 0xcd },
++ { CCI_REG8(0x3d11), 0xcd }, { CCI_REG8(0x3d12), 0xcd },
++ { CCI_REG8(0x3d13), 0xcd }, { CCI_REG8(0x3d14), 0xcd },
++ { CCI_REG8(0x3d15), 0xcd }, { CCI_REG8(0x3d16), 0xcd },
++ { CCI_REG8(0x3d17), 0xcd }, { CCI_REG8(0x3d18), 0xcd },
++ { CCI_REG8(0x3d19), 0xcd }, { CCI_REG8(0x3d1a), 0xcd },
++ { CCI_REG8(0x3d1b), 0xcd }, { CCI_REG8(0x3d1c), 0xcd },
++ { CCI_REG8(0x3d1d), 0xcd }, { CCI_REG8(0x3d1e), 0xcd },
++ { CCI_REG8(0x3d1f), 0xcd }, { CCI_REG8(0x3d20), 0xcd },
++ { CCI_REG8(0x3d21), 0xcd }, { CCI_REG8(0x3d22), 0xcd },
++ { CCI_REG8(0x3d23), 0xcd }, { CCI_REG8(0x3d24), 0xcd },
++ { CCI_REG8(0x3d25), 0xcd }, { CCI_REG8(0x3d26), 0xcd },
++ { CCI_REG8(0x3d27), 0xcd }, { CCI_REG8(0x3d28), 0xcd },
++ { CCI_REG8(0x3d29), 0xcd }, { CCI_REG8(0x3d2a), 0xcd },
++ { CCI_REG8(0x3d2b), 0xcd }, { CCI_REG8(0x3d2c), 0xcd },
++ { CCI_REG8(0x3d2d), 0xcd }, { CCI_REG8(0x3d2e), 0xcd },
++ { CCI_REG8(0x3d2f), 0xcd }, { CCI_REG8(0x3d30), 0xcd },
++ { CCI_REG8(0x3d31), 0xcd }, { CCI_REG8(0x3d32), 0xcd },
++ { CCI_REG8(0x3d33), 0xcd }, { CCI_REG8(0x3d34), 0xcd },
++ { CCI_REG8(0x3d35), 0xcd }, { CCI_REG8(0x3d36), 0xcd },
++ { CCI_REG8(0x3d37), 0xcd }, { CCI_REG8(0x3d38), 0xcd },
++ { CCI_REG8(0x3d39), 0xcd }, { CCI_REG8(0x3d3a), 0xcd },
++ { CCI_REG8(0x3d3b), 0xcd }, { CCI_REG8(0x3d3c), 0xcd },
++ { CCI_REG8(0x3d3d), 0xcd }, { CCI_REG8(0x3d3e), 0xcd },
++ { CCI_REG8(0x3d3f), 0xcd }, { CCI_REG8(0x3d40), 0xcd },
++ { CCI_REG8(0x3d41), 0xcd }, { CCI_REG8(0x3d42), 0xcd },
++ { CCI_REG8(0x3d43), 0xcd }, { CCI_REG8(0x3d44), 0xcd },
++ { CCI_REG8(0x3d45), 0xcd }, { CCI_REG8(0x3d46), 0xcd },
++ { CCI_REG8(0x3d47), 0xcd }, { CCI_REG8(0x3d48), 0xcd },
++ { CCI_REG8(0x3d49), 0xcd }, { CCI_REG8(0x3d4a), 0xcd },
++ { CCI_REG8(0x3d4b), 0xcd }, { CCI_REG8(0x3d4c), 0xcd },
++ { CCI_REG8(0x3d4d), 0xcd }, { CCI_REG8(0x3d4e), 0xcd },
++ { CCI_REG8(0x3d4f), 0xcd }, { CCI_REG8(0x3d50), 0xcd },
++ { CCI_REG8(0x3d51), 0xcd }, { CCI_REG8(0x3d52), 0xcd },
++ { CCI_REG8(0x3d53), 0xcd }, { CCI_REG8(0x3d54), 0xcd },
++ { CCI_REG8(0x3d55), 0xcd }, { CCI_REG8(0x3d56), 0xcd },
++ { CCI_REG8(0x3d57), 0xcd }, { CCI_REG8(0x3d58), 0xcd },
++ { CCI_REG8(0x3d59), 0xcd }, { CCI_REG8(0x3d5a), 0xcd },
++ { CCI_REG8(0x3d5b), 0xcd }, { CCI_REG8(0x3d5c), 0xcd },
++ { CCI_REG8(0x3d5d), 0xcd }, { CCI_REG8(0x3d5e), 0xcd },
++ { CCI_REG8(0x3d5f), 0xcd }, { CCI_REG8(0x3d60), 0xcd },
++ { CCI_REG8(0x3d61), 0xcd }, { CCI_REG8(0x3d62), 0xcd },
++ { CCI_REG8(0x3d63), 0xcd }, { CCI_REG8(0x3d64), 0xcd },
++ { CCI_REG8(0x3d65), 0x40 }, { CCI_REG8(0x3d66), 0x40 },
++ { CCI_REG8(0x3d67), 0x40 }, { CCI_REG8(0x3d68), 0x40 },
++ { CCI_REG8(0x3d69), 0x40 }, { CCI_REG8(0x3d6a), 0x40 },
++ { CCI_REG8(0x3d6b), 0x40 }, { CCI_REG8(0x3d6c), 0x40 },
++ { CCI_REG8(0x3d6d), 0x40 }, { CCI_REG8(0x3d6e), 0x40 },
++ { CCI_REG8(0x3d6f), 0x40 }, { CCI_REG8(0x3d70), 0x40 },
++ { CCI_REG8(0x3d71), 0x40 }, { CCI_REG8(0x3d72), 0x40 },
++ { CCI_REG8(0x3d73), 0x40 }, { CCI_REG8(0x3d74), 0x40 },
++ { CCI_REG8(0x3d75), 0x40 }, { CCI_REG8(0x3d76), 0x40 },
++ { CCI_REG8(0x3d77), 0x40 }, { CCI_REG8(0x3d78), 0x40 },
++ { CCI_REG8(0x3d79), 0x40 }, { CCI_REG8(0x3d7a), 0x40 },
++ { CCI_REG8(0x3d7b), 0x40 }, { CCI_REG8(0x3d7c), 0x40 },
++ { CCI_REG8(0x3d7d), 0x40 }, { CCI_REG8(0x3d7e), 0x40 },
++ { CCI_REG8(0x3d7f), 0x40 }, { CCI_REG8(0x3d80), 0x40 },
++ { CCI_REG8(0x3d81), 0x40 }, { CCI_REG8(0x3d82), 0x40 },
++ { CCI_REG8(0x3d83), 0x40 }, { CCI_REG8(0x3d84), 0x40 },
++ { CCI_REG8(0x3d85), 0x40 }, { CCI_REG8(0x3d86), 0x40 },
++ { CCI_REG8(0x3d87), 0x40 }, { CCI_REG8(0x3d88), 0x40 },
++ { CCI_REG8(0x3d89), 0x40 }, { CCI_REG8(0x3d8a), 0x40 },
++ { CCI_REG8(0x3d8b), 0x40 }, { CCI_REG8(0x3d8c), 0x40 },
++ { CCI_REG8(0x3d8d), 0x40 }, { CCI_REG8(0x3d8e), 0x40 },
++ { CCI_REG8(0x3d8f), 0x40 }, { CCI_REG8(0x3d90), 0x40 },
++ { CCI_REG8(0x3d91), 0x40 }, { CCI_REG8(0x3d92), 0x40 },
++ { CCI_REG8(0x3d93), 0x40 }, { CCI_REG8(0x3d94), 0x40 },
++ { CCI_REG8(0x3d95), 0x40 }, { CCI_REG8(0x3d96), 0x40 },
++ { CCI_REG8(0x3d97), 0x40 }, { CCI_REG8(0x3d98), 0x40 },
++ { CCI_REG8(0x3d99), 0x40 }, { CCI_REG8(0x3d9a), 0x40 },
++ { CCI_REG8(0x3d9b), 0x40 }, { CCI_REG8(0x3d9c), 0x40 },
++ { CCI_REG8(0x3d9d), 0x40 }, { CCI_REG8(0x3d9e), 0x40 },
++ { CCI_REG8(0x3d9f), 0x40 }, { CCI_REG8(0x3da0), 0x40 },
++ { CCI_REG8(0x3da1), 0x40 }, { CCI_REG8(0x3da2), 0x40 },
++ { CCI_REG8(0x3da3), 0x40 }, { CCI_REG8(0x3da4), 0x40 },
++ { CCI_REG8(0x3da5), 0x40 }, { CCI_REG8(0x3da6), 0x40 },
++ { CCI_REG8(0x3da7), 0x40 }, { CCI_REG8(0x3da8), 0x40 },
++ { CCI_REG8(0x3da9), 0x40 }, { CCI_REG8(0x3daa), 0x40 },
++ { CCI_REG8(0x3dab), 0x40 }, { CCI_REG8(0x3dac), 0x40 },
++ { CCI_REG8(0x3dad), 0x40 }, { CCI_REG8(0x3dae), 0x40 },
++ { CCI_REG8(0x3daf), 0x40 }, { CCI_REG8(0x3db0), 0x40 },
++ { CCI_REG8(0x3db1), 0x40 }, { CCI_REG8(0x3db2), 0x40 },
++ { CCI_REG8(0x3db3), 0x40 }, { CCI_REG8(0x3db4), 0x40 },
++ { CCI_REG8(0x3db5), 0x40 }, { CCI_REG8(0x3db6), 0x40 },
++ { CCI_REG8(0x3db7), 0x40 }, { CCI_REG8(0x3db8), 0x40 },
++ { CCI_REG8(0x3db9), 0x40 }, { CCI_REG8(0x3dba), 0x40 },
++ { CCI_REG8(0x3dbb), 0x40 }, { CCI_REG8(0x3dbc), 0x40 },
++ { CCI_REG8(0x3dbd), 0x40 }, { CCI_REG8(0x3dbe), 0x40 },
++ { CCI_REG8(0x3dbf), 0xcd }, { CCI_REG8(0x3dc0), 0xcd },
++ { CCI_REG8(0x3dc1), 0xcd }, { CCI_REG8(0x3dc2), 0xcd },
++ { CCI_REG8(0x3dc3), 0xcd }, { CCI_REG8(0x3dc4), 0xcd },
++ { CCI_REG8(0x3dc5), 0xcd }, { CCI_REG8(0x3dc6), 0xcd },
++ { CCI_REG8(0x3dc7), 0xcd }, { CCI_REG8(0x3dc8), 0xcd },
++ { CCI_REG8(0x3dc9), 0xcd }, { CCI_REG8(0x3dca), 0xcd },
++ { CCI_REG8(0x3dcb), 0xcd }, { CCI_REG8(0x3dcc), 0xcd },
++ { CCI_REG8(0x3dcd), 0xcd }, { CCI_REG8(0x3dce), 0xcd },
++ { CCI_REG8(0x3dcf), 0xcd }, { CCI_REG8(0x3dd0), 0xcd },
++ { CCI_REG8(0x3dd1), 0xcd }, { CCI_REG8(0x3dd2), 0xcd },
++ { CCI_REG8(0x3dd3), 0xcd }, { CCI_REG8(0x3dd4), 0xcd },
++ { CCI_REG8(0x3dd5), 0xcd }, { CCI_REG8(0x3dd6), 0xcd },
++ { CCI_REG8(0x3dd7), 0xcd }, { CCI_REG8(0x3dd8), 0xcd },
++ { CCI_REG8(0x3dd9), 0xcd }, { CCI_REG8(0x3dda), 0xcd },
++ { CCI_REG8(0x3ddb), 0xcd }, { CCI_REG8(0x3ddc), 0xcd },
++ { CCI_REG8(0x3ddd), 0xcd }, { CCI_REG8(0x3dde), 0xcd },
++ { CCI_REG8(0x3ddf), 0xcd }, { CCI_REG8(0x3de0), 0xcd },
++ { CCI_REG8(0x3de1), 0xcd }, { CCI_REG8(0x3de2), 0xcd },
++ { CCI_REG8(0x3de3), 0xcd }, { CCI_REG8(0x3de4), 0xcd },
++ { CCI_REG8(0x3de5), 0xcd }, { CCI_REG8(0x3de6), 0xcd },
++ { CCI_REG8(0x3de7), 0xcd }, { CCI_REG8(0x3de8), 0xcd },
++ { CCI_REG8(0x3de9), 0xcd }, { CCI_REG8(0x3dea), 0xcd },
++ { CCI_REG8(0x3deb), 0xcd }, { CCI_REG8(0x3dec), 0xcd },
++ { CCI_REG8(0x3ded), 0xcd }, { CCI_REG8(0x3dee), 0xcd },
++ { CCI_REG8(0x3def), 0xcd }, { CCI_REG8(0x3df0), 0xcd },
++ { CCI_REG8(0x3df1), 0xcd }, { CCI_REG8(0x3df2), 0xcd },
++ { CCI_REG8(0x3df3), 0xcd }, { CCI_REG8(0x3df4), 0xcd },
++ { CCI_REG8(0x3df5), 0xcd }, { CCI_REG8(0x3df6), 0xcd },
++ { CCI_REG8(0x3df7), 0xcd }, { CCI_REG8(0x3df8), 0xcd },
++ { CCI_REG8(0x3df9), 0xcd }, { CCI_REG8(0x3dfa), 0xcd },
++ { CCI_REG8(0x3dfb), 0xcd }, { CCI_REG8(0x3dfc), 0xcd },
++ { CCI_REG8(0x3dfd), 0xcd }, { CCI_REG8(0x3dfe), 0xcd },
++ { CCI_REG8(0x3dff), 0xcd }, { CCI_REG8(0x3e00), 0xcd },
++ { CCI_REG8(0x3e01), 0xcd }, { CCI_REG8(0x3e02), 0xcd },
++ { CCI_REG8(0x3e03), 0xcd }, { CCI_REG8(0x3e04), 0xcd },
++ { CCI_REG8(0x3e05), 0xcd }, { CCI_REG8(0x3e06), 0xcd },
++ { CCI_REG8(0x3e07), 0xcd }, { CCI_REG8(0x3e08), 0xcd },
++ { CCI_REG8(0x3e09), 0xcd }, { CCI_REG8(0x3e0a), 0xcd },
++ { CCI_REG8(0x3e0b), 0xcd }, { CCI_REG8(0x3e0c), 0xcd },
++ { CCI_REG8(0x3e0d), 0xcd }, { CCI_REG8(0x3e0e), 0xcd },
++ { CCI_REG8(0x3e0f), 0xcd }, { CCI_REG8(0x3e10), 0xcd },
++ { CCI_REG8(0x3e11), 0xcd }, { CCI_REG8(0x3e12), 0xcd },
++ { CCI_REG8(0x3e13), 0xcd }, { CCI_REG8(0x3e14), 0xcd },
++ { CCI_REG8(0x3e15), 0xcd }, { CCI_REG8(0x3e16), 0xcd },
++ { CCI_REG8(0x3e17), 0xcd }, { CCI_REG8(0x3e18), 0xcd },
++ { CCI_REG8(0x3e19), 0xcd }, { CCI_REG8(0x3e1a), 0xcd },
++ { CCI_REG8(0x3e1b), 0xcd }, { CCI_REG8(0x3e1c), 0xcd },
++ { CCI_REG8(0x3e1d), 0xcd }, { CCI_REG8(0x3e1e), 0xcd },
++ { CCI_REG8(0x3e1f), 0xcd }, { CCI_REG8(0x3e20), 0xcd },
++ { CCI_REG8(0x3e21), 0xcd }, { CCI_REG8(0x3e22), 0xcd },
++ { CCI_REG8(0x3e23), 0xcd }, { CCI_REG8(0x3e24), 0xcd },
++ { CCI_REG8(0x3e25), 0xcd }, { CCI_REG8(0x3e26), 0xcd },
++ { CCI_REG8(0x3e27), 0xcd }, { CCI_REG8(0x3e28), 0xcd },
++ { CCI_REG8(0x3e29), 0xcd }, { CCI_REG8(0x3e2a), 0xcd },
++ { CCI_REG8(0x3e2b), 0xcd }, { CCI_REG8(0x3e2c), 0xcd },
++ { CCI_REG8(0x3e2d), 0xcd }, { CCI_REG8(0x3e2e), 0xcd },
++ { CCI_REG8(0x3e2f), 0xcd }, { CCI_REG8(0x3e30), 0xcd },
++ { CCI_REG8(0x3e31), 0xcd }, { CCI_REG8(0x3e32), 0xcd },
++ { CCI_REG8(0x3e33), 0xcd }, { CCI_REG8(0x3e34), 0xcd },
++ { CCI_REG8(0x3e35), 0xcd }, { CCI_REG8(0x3e36), 0xcd },
++ { CCI_REG8(0x3e37), 0xcd }, { CCI_REG8(0x3e38), 0xcd },
++ { CCI_REG8(0x3e39), 0xcd }, { CCI_REG8(0x3e3a), 0xcd },
++ { CCI_REG8(0x3e3b), 0xcd }, { CCI_REG8(0x3e3c), 0xcd },
++ { CCI_REG8(0x3e3d), 0xcd }, { CCI_REG8(0x3e3e), 0xcd },
++ { CCI_REG8(0x3e3f), 0xcd }, { CCI_REG8(0x3e40), 0xcd },
++ { CCI_REG8(0x3e41), 0xcd }, { CCI_REG8(0x3e42), 0xcd },
++ { CCI_REG8(0x3e43), 0xcd }, { CCI_REG8(0x3e44), 0xcd },
++ { CCI_REG8(0x3e45), 0xcd }, { CCI_REG8(0x3e46), 0xcd },
++ { CCI_REG8(0x3e47), 0xcd }, { CCI_REG8(0x3e48), 0xcd },
++ { CCI_REG8(0x3e49), 0xcd }, { CCI_REG8(0x3e4a), 0xcd },
++ { CCI_REG8(0x3e4b), 0xcd }, { CCI_REG8(0x3e4c), 0xcd },
++ { CCI_REG8(0x3e4d), 0xcd }, { CCI_REG8(0x3e4e), 0xcd },
++ { CCI_REG8(0x3e4f), 0xcd }, { CCI_REG8(0x3e50), 0xcd },
++ { CCI_REG8(0x3e51), 0xcd }, { CCI_REG8(0x3e52), 0xcd },
++ { CCI_REG8(0x3e53), 0xcd }, { CCI_REG8(0x3e54), 0xcd },
++ { CCI_REG8(0x3e55), 0xcd }, { CCI_REG8(0x3e56), 0xcd },
++ { CCI_REG8(0x3e57), 0xcd }, { CCI_REG8(0x3e58), 0xcd },
++ { CCI_REG8(0x3e59), 0xcd }, { CCI_REG8(0x3e5a), 0xcd },
++ { CCI_REG8(0x3e5b), 0xcd }, { CCI_REG8(0x3e5c), 0xcd },
++ { CCI_REG8(0x3e5d), 0xcd }, { CCI_REG8(0x3e5e), 0xcd },
++ { CCI_REG8(0x3e5f), 0xcd }, { CCI_REG8(0x3e60), 0xcd },
++ { CCI_REG8(0x3e61), 0xcd }, { CCI_REG8(0x3e62), 0xcd },
++ { CCI_REG8(0x3e63), 0xcd }, { CCI_REG8(0x3e64), 0xcd },
++ { CCI_REG8(0x3e65), 0xcd }, { CCI_REG8(0x3e66), 0xcd },
++ { CCI_REG8(0x3e67), 0xcd }, { CCI_REG8(0x3e68), 0xcd },
++ { CCI_REG8(0x3e69), 0xcd }, { CCI_REG8(0x3e6a), 0xcd },
++ { CCI_REG8(0x3e6b), 0xcd }, { CCI_REG8(0x3e6c), 0xcd },
++ { CCI_REG8(0x3e6d), 0xcd }, { CCI_REG8(0x3e6e), 0xcd },
++ { CCI_REG8(0x3e6f), 0xcd }, { CCI_REG8(0x3e70), 0xcd },
++ { CCI_REG8(0x3e71), 0xcd }, { CCI_REG8(0x3e72), 0xcd },
++ { CCI_REG8(0x3e73), 0xcd }, { CCI_REG8(0x3e74), 0xcd },
++ { CCI_REG8(0x3e75), 0xcd }, { CCI_REG8(0x3e76), 0xcd },
++ { CCI_REG8(0x3e77), 0xcd }, { CCI_REG8(0x3e78), 0xcd },
++ { CCI_REG8(0x3e79), 0xcd }, { CCI_REG8(0x3e7a), 0xcd },
++ { CCI_REG8(0x3e7b), 0xcd }, { CCI_REG8(0x3e7c), 0xcd },
++ { CCI_REG8(0x3e7d), 0xcd }, { CCI_REG8(0x3e7e), 0xcd },
++ { CCI_REG8(0x3e7f), 0xcd }, { CCI_REG8(0x3e80), 0xcd },
++ { CCI_REG8(0x3e81), 0xcd }, { CCI_REG8(0x3e82), 0xcd },
++ { CCI_REG8(0x3e83), 0xcd }, { CCI_REG8(0x3e84), 0xcd },
++ { CCI_REG8(0x3e85), 0xcd }, { CCI_REG8(0x3e86), 0xcd },
++ { CCI_REG8(0x3e87), 0xcd }, { CCI_REG8(0x3e88), 0xcd },
++ { CCI_REG8(0x3e89), 0xcd }, { CCI_REG8(0x3e8a), 0xcd },
++ { CCI_REG8(0x3e8b), 0xcd }, { CCI_REG8(0x3e8c), 0xcd },
++ { CCI_REG8(0x3e8d), 0xcd }, { CCI_REG8(0x3e8e), 0xcd },
++ { CCI_REG8(0x3e8f), 0xcd }, { CCI_REG8(0x3e90), 0xcd },
++ { CCI_REG8(0x3e91), 0xcd }, { CCI_REG8(0x3e92), 0xcd },
++ { CCI_REG8(0x3e93), 0xcd }, { CCI_REG8(0x3e94), 0xcd },
++ { CCI_REG8(0x3e95), 0xcd }, { CCI_REG8(0x3e96), 0xcd },
++ { CCI_REG8(0x3e97), 0xcd }, { CCI_REG8(0x3e98), 0xcd },
++ { CCI_REG8(0x3e99), 0xcd }, { CCI_REG8(0x3e9a), 0xcd },
++ { CCI_REG8(0x3e9b), 0xcd }, { CCI_REG8(0x3e9c), 0xcd },
++ { CCI_REG8(0x3e9d), 0xcd }, { CCI_REG8(0x3e9e), 0xcd },
++ { CCI_REG8(0x3e9f), 0xcd }, { CCI_REG8(0xfff9), 0x06 },
++ { CCI_REG8(0xc03f), 0x01 }, { CCI_REG8(0xc03e), 0x08 },
++ { CCI_REG8(0xc02c), 0xff }, { CCI_REG8(0xc005), 0x06 },
++ { CCI_REG8(0xc006), 0x30 }, { CCI_REG8(0xc007), 0xc0 },
++ { CCI_REG8(0xc027), 0x01 }, { CCI_REG8(0x30c0), 0x05 },
++ { CCI_REG8(0x30c1), 0x9f }, { CCI_REG8(0x30c2), 0x06 },
++ { CCI_REG8(0x30c3), 0x5f }, { CCI_REG8(0x30c4), 0x80 },
++ { CCI_REG8(0x30c5), 0x08 }, { CCI_REG8(0x30c6), 0x39 },
++ { CCI_REG8(0x30c7), 0x00 }, { CCI_REG8(0xc046), 0x20 },
++ { CCI_REG8(0xc043), 0x01 }, { CCI_REG8(0xc04b), 0x01 },
++ { CCI_REG8(0x0102), 0x01 }, { CCI_REG8(0x0100), 0x00 },
++ { CCI_REG8(0x0102), 0x00 }, { CCI_REG8(0x3015), 0xf0 },
++ { CCI_REG8(0x3018), 0xf0 }, { CCI_REG8(0x301c), 0xf0 },
++ { CCI_REG8(0x301d), 0xf6 }, { CCI_REG8(0x301e), 0xf1 }
++};
++
++static const struct cci_reg_sequence ov64a40_9248x6944[] = {
++ { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 },
++ { CCI_REG8(0x0307), 0x01 }, { CCI_REG8(0x4837), 0x1a },
++ { CCI_REG8(0x4888), 0x10 }, { CCI_REG8(0x4860), 0x00 },
++ { CCI_REG8(0x4850), 0x43 }, { CCI_REG8(0x480C), 0x92 },
++ { CCI_REG8(0x5001), 0x21 }
++};
++
++static const struct cci_reg_sequence ov64a40_8000x6000[] = {
++ { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 },
++ { CCI_REG8(0x0307), 0x01 }, { CCI_REG8(0x4837), 0x1a },
++ { CCI_REG8(0x4888), 0x10 }, { CCI_REG8(0x4860), 0x00 },
++ { CCI_REG8(0x4850), 0x43 }, { CCI_REG8(0x480C), 0x92 },
++ { CCI_REG8(0x5001), 0x21 }
++};
++
++static const struct cci_reg_sequence ov64a40_4624_3472[] = {
++ { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 },
++ { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e },
++ { CCI_REG8(0x3712), 0x50 }, { CCI_REG8(0x3822), 0x00 },
++ { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x08 },
++ { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x02 },
++ { CCI_REG8(0x384d), 0xba }, { CCI_REG8(0x3852), 0x00 },
++ { CCI_REG8(0x3856), 0x08 }, { CCI_REG8(0x3857), 0x08 },
++ { CCI_REG8(0x3858), 0x10 }, { CCI_REG8(0x3859), 0x10 },
++ { CCI_REG8(0x4016), 0x0f }, { CCI_REG8(0x4018), 0x03 },
++ { CCI_REG8(0x4504), 0x1e }, { CCI_REG8(0x4523), 0x41 },
++ { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x12 },
++ { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4915), 0x02 },
++ { CCI_REG8(0x4916), 0x1d }, { CCI_REG8(0x4a15), 0x02 },
++ { CCI_REG8(0x4a16), 0x1d }, { CCI_REG8(0x3703), 0x72 },
++ { CCI_REG8(0x3709), 0xe6 }, { CCI_REG8(0x3a60), 0x68 },
++ { CCI_REG8(0x3a6f), 0x68 }, { CCI_REG8(0x3a5e), 0xdc },
++ { CCI_REG8(0x3a6d), 0xdc }, { CCI_REG8(0x3721), 0xc9 },
++ { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 },
++ { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 },
++ { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 },
++ { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b },
++ { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 },
++ { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b },
++ { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 },
++ { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b },
++ { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x481b), 0x35 },
++ { CCI_REG8(0x4862), 0x25 }, { CCI_REG8(0x3400), 0x00 },
++ { CCI_REG8(0x3421), 0x23 }, { CCI_REG8(0x3422), 0xfc },
++ { CCI_REG8(0x3423), 0x07 }, { CCI_REG8(0x3424), 0x01 },
++ { CCI_REG8(0x3425), 0x04 }, { CCI_REG8(0x3426), 0x50 },
++ { CCI_REG8(0x3427), 0x55 }, { CCI_REG8(0x3428), 0x15 },
++ { CCI_REG8(0x3429), 0x00 }, { CCI_REG8(0x3025), 0x03 },
++ { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x0305), 0x98 },
++ { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 },
++ { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++ { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++ { CCI_REG8(0x480C), 0x92 }, { CCI_REG8(0x5001), 0x21 }
++};
++
++static const struct cci_reg_sequence ov64a40_3840x2160[] = {
++ { CCI_REG8(0x034a), 0x05 }, { CCI_REG8(0x034b), 0x05 },
++ { CCI_REG8(0x3504), 0x08 }, { CCI_REG8(0x360d), 0x82 },
++ { CCI_REG8(0x368a), 0x2e }, { CCI_REG8(0x3712), 0x50 },
++ { CCI_REG8(0x3822), 0x00 }, { CCI_REG8(0x3827), 0x40 },
++ { CCI_REG8(0x383d), 0x08 }, { CCI_REG8(0x383f), 0x00 },
++ { CCI_REG8(0x384c), 0x02 }, { CCI_REG8(0x384d), 0xba },
++ { CCI_REG8(0x3852), 0x00 }, { CCI_REG8(0x3856), 0x08 },
++ { CCI_REG8(0x3857), 0x08 }, { CCI_REG8(0x3858), 0x10 },
++ { CCI_REG8(0x3859), 0x10 }, { CCI_REG8(0x4016), 0x0f },
++ { CCI_REG8(0x4018), 0x03 }, { CCI_REG8(0x4504), 0x1e },
++ { CCI_REG8(0x4523), 0x41 }, { CCI_REG8(0x45c0), 0x01 },
++ { CCI_REG8(0x4641), 0x12 }, { CCI_REG8(0x4643), 0x0c },
++ { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d },
++ { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d },
++ { CCI_REG8(0x3703), 0x72 }, { CCI_REG8(0x3709), 0xe6 },
++ { CCI_REG8(0x3a60), 0x68 }, { CCI_REG8(0x3a6f), 0x68 },
++ { CCI_REG8(0x3a5e), 0xdc }, { CCI_REG8(0x3a6d), 0xdc },
++ { CCI_REG8(0x3721), 0xc9 }, { CCI_REG8(0x5250), 0x06 },
++ { CCI_REG8(0x527a), 0x00 }, { CCI_REG8(0x527b), 0x65 },
++ { CCI_REG8(0x527c), 0x00 }, { CCI_REG8(0x527d), 0x82 },
++ { CCI_REG8(0x5280), 0x24 }, { CCI_REG8(0x5281), 0x40 },
++ { CCI_REG8(0x5282), 0x1b }, { CCI_REG8(0x5283), 0x40 },
++ { CCI_REG8(0x5284), 0x24 }, { CCI_REG8(0x5285), 0x40 },
++ { CCI_REG8(0x5286), 0x1b }, { CCI_REG8(0x5287), 0x40 },
++ { CCI_REG8(0x5200), 0x24 }, { CCI_REG8(0x5201), 0x40 },
++ { CCI_REG8(0x5202), 0x1b }, { CCI_REG8(0x5203), 0x40 },
++ { CCI_REG8(0x481b), 0x35 }, { CCI_REG8(0x4862), 0x25 },
++ { CCI_REG8(0x3400), 0x00 }, { CCI_REG8(0x3421), 0x23 },
++ { CCI_REG8(0x3422), 0xfc }, { CCI_REG8(0x3423), 0x07 },
++ { CCI_REG8(0x3424), 0x01 }, { CCI_REG8(0x3425), 0x04 },
++ { CCI_REG8(0x3426), 0x50 }, { CCI_REG8(0x3427), 0x55 },
++ { CCI_REG8(0x3428), 0x15 }, { CCI_REG8(0x3429), 0x00 },
++ { CCI_REG8(0x3025), 0x03 }, { CCI_REG8(0x5250), 0x06 },
++ { CCI_REG8(0x0305), 0x98 }, { CCI_REG8(0x0306), 0x04 },
++ { CCI_REG8(0x0345), 0x90 }, { CCI_REG8(0x0307), 0x01 },
++ { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++ { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++ { CCI_REG8(0x480C), 0x92 }, { CCI_REG8(0x5001), 0x21 },
++ { CCI_REG8(0x5000), 0x01 }
++};
++
++static const struct cci_reg_sequence ov64a40_2312_1736[] = {
++ { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 },
++ { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e },
++ { CCI_REG8(0x3712), 0x00 }, { CCI_REG8(0x3822), 0x08 },
++ { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x04 },
++ { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x01 },
++ { CCI_REG8(0x384d), 0x12 }, { CCI_REG8(0x3852), 0x00 },
++ { CCI_REG8(0x3856), 0x04 }, { CCI_REG8(0x3857), 0x04 },
++ { CCI_REG8(0x3858), 0x08 }, { CCI_REG8(0x3859), 0x08 },
++ { CCI_REG8(0x4016), 0x07 }, { CCI_REG8(0x4018), 0x01 },
++ { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4523), 0x00 },
++ { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x24 },
++ { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4837), 0x0b },
++ { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d },
++ { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d },
++ { CCI_REG8(0x5000), 0x55 }, { CCI_REG8(0x5001), 0x00 },
++ { CCI_REG8(0x5002), 0x35 }, { CCI_REG8(0x5004), 0xc0 },
++ { CCI_REG8(0x5068), 0x02 }, { CCI_REG8(0x3703), 0x6a },
++ { CCI_REG8(0x3709), 0xa3 }, { CCI_REG8(0x3a60), 0x60 },
++ { CCI_REG8(0x3a6f), 0x60 }, { CCI_REG8(0x3a5e), 0x99 },
++ { CCI_REG8(0x3a6d), 0x99 }, { CCI_REG8(0x3721), 0xc1 },
++ { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 },
++ { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 },
++ { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 },
++ { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b },
++ { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 },
++ { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b },
++ { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 },
++ { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b },
++ { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x3684), 0x05 },
++ { CCI_REG8(0x481b), 0x20 }, { CCI_REG8(0x51b0), 0x38 },
++ { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x04 },
++ { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 },
++ { CCI_REG8(0x51b9), 0x70 }, { CCI_REG8(0x51bb), 0x10 },
++ { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 },
++ { CCI_REG8(0x51b0), 0x38 }, { CCI_REG8(0x54b0), 0x38 },
++ { CCI_REG8(0x54b3), 0x0e }, { CCI_REG8(0x54b5), 0x04 },
++ { CCI_REG8(0x54b6), 0x00 }, { CCI_REG8(0x54b7), 0x00 },
++ { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54bb), 0x10 },
++ { CCI_REG8(0x54bc), 0x00 }, { CCI_REG8(0x54bd), 0x00 },
++ { CCI_REG8(0x57b0), 0x38 }, { CCI_REG8(0x57b3), 0x0e },
++ { CCI_REG8(0x57b5), 0x04 }, { CCI_REG8(0x57b6), 0x00 },
++ { CCI_REG8(0x57b7), 0x00 }, { CCI_REG8(0x57b9), 0x70 },
++ { CCI_REG8(0x57bb), 0x10 }, { CCI_REG8(0x57bc), 0x00 },
++ { CCI_REG8(0x57bd), 0x00 }, { CCI_REG8(0x0305), 0x98 },
++ { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 },
++ { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++ { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++ { CCI_REG8(0x480C), 0x92 }
++};
++
++static const struct cci_reg_sequence ov64a40_1920x1080[] = {
++ { CCI_REG8(0x034b), 0x02 }, { CCI_REG8(0x3504), 0x08 },
++ { CCI_REG8(0x360d), 0x82 }, { CCI_REG8(0x368a), 0x2e },
++ { CCI_REG8(0x3712), 0x00 }, { CCI_REG8(0x3822), 0x08 },
++ { CCI_REG8(0x3827), 0x40 }, { CCI_REG8(0x383d), 0x04 },
++ { CCI_REG8(0x383f), 0x00 }, { CCI_REG8(0x384c), 0x01 },
++ { CCI_REG8(0x384d), 0x12 }, { CCI_REG8(0x3852), 0x00 },
++ { CCI_REG8(0x3856), 0x04 }, { CCI_REG8(0x3857), 0x04 },
++ { CCI_REG8(0x3858), 0x08 }, { CCI_REG8(0x3859), 0x08 },
++ { CCI_REG8(0x4016), 0x07 }, { CCI_REG8(0x4018), 0x01 },
++ { CCI_REG8(0x4504), 0x00 }, { CCI_REG8(0x4523), 0x00 },
++ { CCI_REG8(0x45c0), 0x01 }, { CCI_REG8(0x4641), 0x24 },
++ { CCI_REG8(0x4643), 0x0c }, { CCI_REG8(0x4837), 0x0b },
++ { CCI_REG8(0x4915), 0x02 }, { CCI_REG8(0x4916), 0x1d },
++ { CCI_REG8(0x4a15), 0x02 }, { CCI_REG8(0x4a16), 0x1d },
++ { CCI_REG8(0x5000), 0x55 }, { CCI_REG8(0x5001), 0x00 },
++ { CCI_REG8(0x5002), 0x35 }, { CCI_REG8(0x5004), 0xc0 },
++ { CCI_REG8(0x5068), 0x02 }, { CCI_REG8(0x3703), 0x6a },
++ { CCI_REG8(0x3709), 0xa3 }, { CCI_REG8(0x3a60), 0x60 },
++ { CCI_REG8(0x3a6f), 0x60 }, { CCI_REG8(0x3a5e), 0x99 },
++ { CCI_REG8(0x3a6d), 0x99 }, { CCI_REG8(0x3721), 0xc1 },
++ { CCI_REG8(0x5250), 0x06 }, { CCI_REG8(0x527a), 0x00 },
++ { CCI_REG8(0x527b), 0x65 }, { CCI_REG8(0x527c), 0x00 },
++ { CCI_REG8(0x527d), 0x82 }, { CCI_REG8(0x5280), 0x24 },
++ { CCI_REG8(0x5281), 0x40 }, { CCI_REG8(0x5282), 0x1b },
++ { CCI_REG8(0x5283), 0x40 }, { CCI_REG8(0x5284), 0x24 },
++ { CCI_REG8(0x5285), 0x40 }, { CCI_REG8(0x5286), 0x1b },
++ { CCI_REG8(0x5287), 0x40 }, { CCI_REG8(0x5200), 0x24 },
++ { CCI_REG8(0x5201), 0x40 }, { CCI_REG8(0x5202), 0x1b },
++ { CCI_REG8(0x5203), 0x40 }, { CCI_REG8(0x3684), 0x05 },
++ { CCI_REG8(0x481b), 0x20 }, { CCI_REG8(0x51b0), 0x38 },
++ { CCI_REG8(0x51b3), 0x0e }, { CCI_REG8(0x51b5), 0x04 },
++ { CCI_REG8(0x51b6), 0x00 }, { CCI_REG8(0x51b7), 0x00 },
++ { CCI_REG8(0x51b9), 0x70 }, { CCI_REG8(0x51bb), 0x10 },
++ { CCI_REG8(0x51bc), 0x00 }, { CCI_REG8(0x51bd), 0x00 },
++ { CCI_REG8(0x51b0), 0x38 }, { CCI_REG8(0x54b0), 0x38 },
++ { CCI_REG8(0x54b3), 0x0e }, { CCI_REG8(0x54b5), 0x04 },
++ { CCI_REG8(0x54b6), 0x00 }, { CCI_REG8(0x54b7), 0x00 },
++ { CCI_REG8(0x54b9), 0x70 }, { CCI_REG8(0x54bb), 0x10 },
++ { CCI_REG8(0x54bc), 0x00 }, { CCI_REG8(0x54bd), 0x00 },
++ { CCI_REG8(0x57b0), 0x38 }, { CCI_REG8(0x57b3), 0x0e },
++ { CCI_REG8(0x57b5), 0x04 }, { CCI_REG8(0x57b6), 0x00 },
++ { CCI_REG8(0x57b7), 0x00 }, { CCI_REG8(0x57b9), 0x70 },
++ { CCI_REG8(0x57bb), 0x10 }, { CCI_REG8(0x57bc), 0x00 },
++ { CCI_REG8(0x57bd), 0x00 }, { CCI_REG8(0x0305), 0x98 },
++ { CCI_REG8(0x0306), 0x04 }, { CCI_REG8(0x0307), 0x01 },
++ { CCI_REG8(0x4837), 0x1a }, { CCI_REG8(0x4888), 0x10 },
++ { CCI_REG8(0x4860), 0x00 }, { CCI_REG8(0x4850), 0x43 },
++ { CCI_REG8(0x480C), 0x92 }
++};
++
++/* 456MHz MIPI link frequency with 24MHz input clock. */
++static const struct cci_reg_sequence ov64a40_pll_config[] = {
++ { OV64A40_PLL1_PRE_DIV0, 0x88 },
++ { OV64A40_PLL1_PRE_DIV, 0x02 },
++ { OV64A40_PLL1_MULTIPLIER, 0x0098 },
++ { OV64A40_PLL1_M_DIV, 0x01 },
++ { OV64A40_PLL2_SEL_BAK_SA1, 0x00 },
++ { OV64A40_PLL2_PRE_DIV, 0x12 },
++ { OV64A40_PLL2_MULTIPLIER, 0x0190 },
++ { OV64A40_PLL2_PRE_DIV0, 0xd7 },
++ { OV64A40_PLL2_DIVSP, 0x00 },
++ { OV64A40_PLL2_DIVDAC, 0x00 },
++ { OV64A40_PLL2_DACPREDIV, 0x00 }
++};
++
++struct ov64a40_reglist {
++ unsigned int num_regs;
++ const struct cci_reg_sequence *regvals;
++};
++
++struct ov64a40_subsampling {
++ unsigned int x_odd_inc;
++ unsigned int x_even_inc;
++ unsigned int y_odd_inc;
++ unsigned int y_even_inc;
++ bool vbin;
++ bool hbin;
++};
++
++static struct ov64a40_mode {
++ unsigned int width;
++ unsigned int height;
++ struct ov64a40_timings {
++ unsigned int vts;
++ unsigned int ppl;
++ } timings_default[OV64A40_NUM_LINK_FREQ];
++ const struct ov64a40_reglist reglist;
++ struct v4l2_rect analogue_crop;
++ struct v4l2_rect digital_crop;
++ struct ov64a40_subsampling subsampling;
++} ov64a40_modes[] = {
++ /* Full resolution */
++ {
++ .width = 9248,
++ .height = 6944,
++ .timings_default = {
++ /* 2.6 FPS */
++ [OV64A40_LINK_FREQ_456M_ID] = {
++ .vts = 7072,
++ .ppl = 4072,
++ },
++ /* 2 FPS */
++ [OV64A40_LINK_FREQ_360M_ID] = {
++ .vts = 7072,
++ .ppl = 5248,
++ },
++ },
++ .reglist = {
++ .num_regs = ARRAY_SIZE(ov64a40_9248x6944),
++ .regvals = ov64a40_9248x6944,
++ },
++ .analogue_crop = {
++ .left = 0,
++ .top = 0,
++ .width = 9279,
++ .height = 6975,
++ },
++ .digital_crop = {
++ .left = 17,
++ .top = 16,
++ .width = 9248,
++ .height = 6944,
++ },
++ .subsampling = {
++ .x_odd_inc = 1,
++ .x_even_inc = 1,
++ .y_odd_inc = 1,
++ .y_even_inc = 1,
++ .vbin = false,
++ .hbin = false,
++ },
++ },
++ /* Analogue crop + digital crop */
++ {
++ .width = 8000,
++ .height = 6000,
++ .timings_default = {
++ /* 3.0 FPS */
++ [OV64A40_LINK_FREQ_456M_ID] = {
++ .vts = 6400,
++ .ppl = 3848,
++ },
++ /* 2.5 FPS */
++ [OV64A40_LINK_FREQ_360M_ID] = {
++ .vts = 6304,
++ .ppl = 4736,
++ },
++ },
++ .reglist = {
++ .num_regs = ARRAY_SIZE(ov64a40_8000x6000),
++ .regvals = ov64a40_8000x6000,
++ },
++ .analogue_crop = {
++ .left = 624,
++ .top = 472,
++ .width = 8047,
++ .height = 6031,
++ },
++ .digital_crop = {
++ .left = 17,
++ .top = 16,
++ .width = 8000,
++ .height = 6000,
++ },
++ .subsampling = {
++ .x_odd_inc = 1,
++ .x_even_inc = 1,
++ .y_odd_inc = 1,
++ .y_even_inc = 1,
++ .vbin = false,
++ .hbin = false,
++ },
++ },
++ /* 2x2 downscaled */
++ {
++ .width = 4624,
++ .height = 3472,
++ .timings_default = {
++ /* 10 FPS */
++ [OV64A40_LINK_FREQ_456M_ID] = {
++ .vts = 3533,
++ .ppl = 2112,
++ },
++ /* 7 FPS */
++ [OV64A40_LINK_FREQ_360M_ID] = {
++ .vts = 3939,
++ .ppl = 2720,
++ },
++ },
++ .reglist = {
++ .num_regs = ARRAY_SIZE(ov64a40_4624_3472),
++ .regvals = ov64a40_4624_3472,
++ },
++ .analogue_crop = {
++ .left = 0,
++ .top = 0,
++ .width = 9279,
++ .height = 6975,
++ },
++ .digital_crop = {
++ .left = 9,
++ .top = 8,
++ .width = 4624,
++ .height = 3472,
++ },
++ .subsampling = {
++ .x_odd_inc = 3,
++ .x_even_inc = 1,
++ .y_odd_inc = 1,
++ .y_even_inc = 1,
++ .vbin = true,
++ .hbin = false,
++ },
++ },
++ /* Analogue crop + 2x2 downscale + digital crop */
++ {
++ .width = 3840,
++ .height = 2160,
++ .timings_default = {
++ /* 20 FPS */
++ [OV64A40_LINK_FREQ_456M_ID] = {
++ .vts = 2218,
++ .ppl = 1690,
++ },
++ /* 15 FPS */
++ [OV64A40_LINK_FREQ_360M_ID] = {
++ .vts = 2270,
++ .ppl = 2202,
++ },
++ },
++ .reglist = {
++ .num_regs = ARRAY_SIZE(ov64a40_3840x2160),
++ .regvals = ov64a40_3840x2160,
++ },
++ .analogue_crop = {
++ .left = 784,
++ .top = 1312,
++ .width = 7711,
++ .height = 4351,
++ },
++ .digital_crop = {
++ .left = 9,
++ .top = 8,
++ .width = 3840,
++ .height = 2160,
++ },
++ .subsampling = {
++ .x_odd_inc = 3,
++ .x_even_inc = 1,
++ .y_odd_inc = 1,
++ .y_even_inc = 1,
++ .vbin = true,
++ .hbin = false,
++ },
++ },
++ /* 4x4 downscaled */
++ {
++ .width = 2312,
++ .height = 1736,
++ .timings_default = {
++ /* 30 FPS */
++ [OV64A40_LINK_FREQ_456M_ID] = {
++ .vts = 1998,
++ .ppl = 1248,
++ },
++ /* 25 FPS */
++ [OV64A40_LINK_FREQ_360M_ID] = {
++ .vts = 1994,
++ .ppl = 1504,
++ },
++ },
++ .reglist = {
++ .num_regs = ARRAY_SIZE(ov64a40_2312_1736),
++ .regvals = ov64a40_2312_1736,
++ },
++ .analogue_crop = {
++ .left = 0,
++ .top = 0,
++ .width = 9279,
++ .height = 6975,
++ },
++ .digital_crop = {
++ .left = 5,
++ .top = 4,
++ .width = 2312,
++ .height = 1736,
++ },
++ .subsampling = {
++ .x_odd_inc = 3,
++ .x_even_inc = 1,
++ .y_odd_inc = 3,
++ .y_even_inc = 1,
++ .vbin = true,
++ .hbin = true,
++ },
++ },
++ /* Analogue crop + 4x4 downscale + digital crop */
++ {
++ .width = 1920,
++ .height = 1080,
++ .timings_default = {
++ /* 60 FPS */
++ [OV64A40_LINK_FREQ_456M_ID] = {
++ .vts = 1397,
++ .ppl = 880,
++ },
++ /* 45 FPS */
++ [OV64A40_LINK_FREQ_360M_ID] = {
++ .vts = 1216,
++ .ppl = 1360,
++ },
++ },
++ .reglist = {
++ .num_regs = ARRAY_SIZE(ov64a40_1920x1080),
++ .regvals = ov64a40_1920x1080,
++ },
++ .analogue_crop = {
++ .left = 784,
++ .top = 1312,
++ .width = 7711,
++ .height = 4351,
++ },
++ .digital_crop = {
++ .left = 7,
++ .top = 6,
++ .width = 1920,
++ .height = 1080,
++ },
++ .subsampling = {
++ .x_odd_inc = 3,
++ .x_even_inc = 1,
++ .y_odd_inc = 3,
++ .y_even_inc = 1,
++ .vbin = true,
++ .hbin = true,
++ },
++ },
++};
++
++struct ov64a40 {
++ struct device *dev;
++
++ struct v4l2_subdev sd;
++ struct media_pad pad;
++
++ struct regmap *cci;
++
++ struct ov64a40_mode *mode;
++
++ struct clk *xclk;
++
++ struct gpio_desc *reset_gpio;
++ struct regulator_bulk_data supplies[ARRAY_SIZE(ov64a40_supply_names)];
++
++ s64 *link_frequencies;
++ unsigned int num_link_frequencies;
++
++ struct v4l2_ctrl_handler ctrl_handler;
++ struct v4l2_ctrl *exposure;
++ struct v4l2_ctrl *link_freq;
++ struct v4l2_ctrl *vblank;
++ struct v4l2_ctrl *hblank;
++ struct v4l2_ctrl *vflip;
++ struct v4l2_ctrl *hflip;
++};
++
++static inline struct ov64a40 *sd_to_ov64a40(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ov64a40, sd);
++}
++
++static const struct ov64a40_timings *
++ov64a40_get_timings(struct ov64a40 *ov64a40, unsigned int link_freq_index)
++{
++ s64 link_freq = ov64a40->link_frequencies[link_freq_index];
++ unsigned int timings_index = link_freq == OV64A40_LINK_FREQ_360M
++ ? OV64A40_LINK_FREQ_360M_ID
++ : OV64A40_LINK_FREQ_456M_ID;
++
++ return &ov64a40->mode->timings_default[timings_index];
++}
++
++static int ov64a40_program_geometry(struct ov64a40 *ov64a40)
++{
++ struct ov64a40_mode *mode = ov64a40->mode;
++ struct v4l2_rect *anacrop = &mode->analogue_crop;
++ struct v4l2_rect *digicrop = &mode->digital_crop;
++ const struct ov64a40_timings *timings;
++ int ret = 0;
++
++ /* Analogue crop. */
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL0,
++ anacrop->left, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL2,
++ anacrop->top, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL4,
++ anacrop->width + anacrop->left, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL6,
++ anacrop->height + anacrop->top, &ret);
++
++ /* ISP windowing. */
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL10,
++ digicrop->left, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL12,
++ digicrop->top, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL8,
++ digicrop->width, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLA,
++ digicrop->height, &ret);
++
++ /* Total timings. */
++ timings = ov64a40_get_timings(ov64a40, ov64a40->link_freq->cur.val);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLC, timings->ppl, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRLE, timings->vts, &ret);
++
++ return ret;
++}
++
++static int ov64a40_program_subsampling(struct ov64a40 *ov64a40)
++{
++ struct ov64a40_subsampling *subsampling = &ov64a40->mode->subsampling;
++ int ret = 0;
++
++ /* Skipping configuration */
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL14,
++ OV64A40_SKIPPING_CONFIG(subsampling->x_odd_inc,
++ subsampling->x_even_inc), &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMING_CTRL15,
++ OV64A40_SKIPPING_CONFIG(subsampling->y_odd_inc,
++ subsampling->y_even_inc), &ret);
++
++ /* Binning configuration */
++ cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_20,
++ OV64A40_TIMING_CTRL_20_VBIN,
++ subsampling->vbin ? OV64A40_TIMING_CTRL_20_VBIN : 0,
++ &ret);
++ cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_21,
++ OV64A40_TIMING_CTRL_21_HBIN_CONF,
++ subsampling->hbin ?
++ OV64A40_TIMING_CTRL_21_HBIN_CONF : 0, &ret);
++
++ return ret;
++}
++
++static int ov64a40_start_streaming(struct ov64a40 *ov64a40,
++ struct v4l2_subdev_state *state)
++{
++ const struct ov64a40_reglist *reglist = &ov64a40->mode->reglist;
++ const struct ov64a40_timings *timings;
++ unsigned long delay;
++ int ret;
++
++ ret = pm_runtime_resume_and_get(ov64a40->dev);
++ if (ret < 0)
++ return ret;
++
++ ret = cci_multi_reg_write(ov64a40->cci, ov64a40_init,
++ ARRAY_SIZE(ov64a40_init), NULL);
++ if (ret)
++ goto error_power_off;
++
++ ret = cci_multi_reg_write(ov64a40->cci, reglist->regvals,
++ reglist->num_regs, NULL);
++ if (ret)
++ goto error_power_off;
++
++ ret = ov64a40_program_geometry(ov64a40);
++ if (ret)
++ goto error_power_off;
++
++ ret = ov64a40_program_subsampling(ov64a40);
++ if (ret)
++ goto error_power_off;
++
++ ret = __v4l2_ctrl_handler_setup(&ov64a40->ctrl_handler);
++ if (ret)
++ goto error_power_off;
++
++ ret = cci_write(ov64a40->cci, OV64A40_REG_SMIA,
++ OV64A40_REG_SMIA_STREAMING, NULL);
++ if (ret)
++ goto error_power_off;
++
++ /* Link frequency and flips cannot change while streaming. */
++ __v4l2_ctrl_grab(ov64a40->link_freq, true);
++ __v4l2_ctrl_grab(ov64a40->vflip, true);
++ __v4l2_ctrl_grab(ov64a40->hflip, true);
++
++ /* delay: max(4096 xclk pulses, 150usec) + exposure time */
++ timings = ov64a40_get_timings(ov64a40, ov64a40->link_freq->cur.val);
++ delay = DIV_ROUND_UP(4096, OV64A40_XCLK_FREQ / 1000 / 1000);
++ delay = max(delay, 150ul);
++
++ /* The sensor has an internal x4 multiplier on the line length. */
++ delay += DIV_ROUND_UP(timings->ppl * 4 * ov64a40->exposure->cur.val,
++ OV64A40_PIXEL_RATE / 1000 / 1000);
++ fsleep(delay);
++
++ return 0;
++
++error_power_off:
++ pm_runtime_mark_last_busy(ov64a40->dev);
++ pm_runtime_put_autosuspend(ov64a40->dev);
++
++ return ret;
++}
++
++static int ov64a40_stop_streaming(struct ov64a40 *ov64a40,
++ struct v4l2_subdev_state *state)
++{
++ cci_update_bits(ov64a40->cci, OV64A40_REG_SMIA, BIT(0), 0, NULL);
++ pm_runtime_mark_last_busy(ov64a40->dev);
++ pm_runtime_put_autosuspend(ov64a40->dev);
++
++ __v4l2_ctrl_grab(ov64a40->link_freq, false);
++ __v4l2_ctrl_grab(ov64a40->vflip, false);
++ __v4l2_ctrl_grab(ov64a40->hflip, false);
++
++ return 0;
++}
++
++static int ov64a40_set_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++ struct v4l2_subdev_state *state;
++ int ret;
++
++ state = v4l2_subdev_lock_and_get_active_state(sd);
++ if (enable)
++ ret = ov64a40_start_streaming(ov64a40, state);
++ else
++ ret = ov64a40_stop_streaming(ov64a40, state);
++ v4l2_subdev_unlock_state(state);
++
++ return ret;
++}
++
++static const struct v4l2_subdev_video_ops ov64a40_video_ops = {
++ .s_stream = ov64a40_set_stream,
++};
++
++static u32 ov64a40_mbus_code(struct ov64a40 *ov64a40)
++{
++ unsigned int index = ov64a40->hflip->val << 1 | ov64a40->vflip->val;
++
++ return ov64a40_mbus_codes[index];
++}
++
++static void ov64a40_update_pad_fmt(struct ov64a40 *ov64a40,
++ struct ov64a40_mode *mode,
++ struct v4l2_mbus_framefmt *fmt)
++{
++ fmt->code = ov64a40_mbus_code(ov64a40);
++ fmt->width = mode->width;
++ fmt->height = mode->height;
++ fmt->field = V4L2_FIELD_NONE;
++ fmt->colorspace = V4L2_COLORSPACE_RAW;
++ fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
++ fmt->xfer_func = V4L2_XFER_FUNC_NONE;
++ fmt->ycbcr_enc = V4L2_YCBCR_ENC_601;
++}
++
++static int ov64a40_init_cfg(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *state)
++{
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++ struct v4l2_mbus_framefmt *format;
++ struct v4l2_rect *crop;
++
++ format = v4l2_subdev_get_pad_format(sd, state, 0);
++ ov64a40_update_pad_fmt(ov64a40, &ov64a40_modes[0], format);
++
++ crop = v4l2_subdev_get_pad_crop(sd, state, 0);
++ crop->top = OV64A40_PIXEL_ARRAY_TOP;
++ crop->left = OV64A40_PIXEL_ARRAY_LEFT;
++ crop->width = OV64A40_PIXEL_ARRAY_WIDTH;
++ crop->height = OV64A40_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++}
++
++static int ov64a40_enum_mbus_code(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_mbus_code_enum *code)
++{
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++
++ if (code->index)
++ return -EINVAL;
++
++ code->code = ov64a40_mbus_code(ov64a40);
++
++ return 0;
++}
++
++static int ov64a40_enum_frame_size(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_frame_size_enum *fse)
++{
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++ struct ov64a40_mode *mode;
++ u32 code;
++
++ if (fse->index >= ARRAY_SIZE(ov64a40_modes))
++ return -EINVAL;
++
++ code = ov64a40_mbus_code(ov64a40);
++ if (fse->code != code)
++ return -EINVAL;
++
++ mode = &ov64a40_modes[fse->index];
++ fse->min_width = mode->width;
++ fse->max_width = mode->width;
++ fse->min_height = mode->height;
++ fse->max_height = mode->height;
++
++ return 0;
++}
++
++static int ov64a40_get_selection(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_selection *sel)
++{
++ switch (sel->target) {
++ case V4L2_SEL_TGT_CROP:
++ sel->r = *v4l2_subdev_get_pad_crop(sd, sd_state, 0);
++
++ return 0;
++
++ case V4L2_SEL_TGT_NATIVE_SIZE:
++ sel->r.top = 0;
++ sel->r.left = 0;
++ sel->r.width = OV64A40_NATIVE_WIDTH;
++ sel->r.height = OV64A40_NATIVE_HEIGHT;
++
++ return 0;
++
++ case V4L2_SEL_TGT_CROP_DEFAULT:
++ case V4L2_SEL_TGT_CROP_BOUNDS:
++ sel->r.top = OV64A40_PIXEL_ARRAY_TOP;
++ sel->r.left = OV64A40_PIXEL_ARRAY_LEFT;
++ sel->r.width = OV64A40_PIXEL_ARRAY_WIDTH;
++ sel->r.height = OV64A40_PIXEL_ARRAY_HEIGHT;
++
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int ov64a40_set_format(struct v4l2_subdev *sd,
++ struct v4l2_subdev_state *sd_state,
++ struct v4l2_subdev_format *fmt)
++{
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++ struct v4l2_mbus_framefmt *format;
++ struct ov64a40_mode *mode;
++
++ mode = v4l2_find_nearest_size(ov64a40_modes,
++ ARRAY_SIZE(ov64a40_modes),
++ width, height,
++ fmt->format.width, fmt->format.height);
++
++ ov64a40_update_pad_fmt(ov64a40, mode, &fmt->format);
++
++ format = v4l2_subdev_get_pad_format(sd, sd_state, 0);
++ if (ov64a40->mode == mode && format->code == fmt->format.code)
++ return 0;
++
++ if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
++ const struct ov64a40_timings *timings;
++ int vblank_max, vblank_def;
++ int hblank_val;
++ int exp_max;
++
++ ov64a40->mode = mode;
++ *v4l2_subdev_get_pad_crop(sd, sd_state, 0) = mode->analogue_crop;
++
++ /* Update control limits according to the new mode. */
++ timings = ov64a40_get_timings(ov64a40,
++ ov64a40->link_freq->cur.val);
++ vblank_max = OV64A40_VTS_MAX - mode->height;
++ vblank_def = timings->vts - mode->height;
++ __v4l2_ctrl_modify_range(ov64a40->vblank, OV64A40_VBLANK_MIN,
++ vblank_max, 1, vblank_def);
++ __v4l2_ctrl_s_ctrl(ov64a40->vblank, vblank_def);
++
++ exp_max = timings->vts - OV64A40_EXPOSURE_MARGIN;
++ __v4l2_ctrl_modify_range(ov64a40->exposure,
++ OV64A40_EXPOSURE_MIN, exp_max,
++ 1, OV64A40_EXPOSURE_MIN);
++
++ hblank_val = timings->ppl * 4 - mode->width;
++ __v4l2_ctrl_modify_range(ov64a40->hblank,
++ hblank_val, hblank_val, 1, hblank_val);
++ }
++
++ *format = fmt->format;
++
++ return 0;
++}
++
++static const struct v4l2_subdev_pad_ops ov64a40_pad_ops = {
++ .init_cfg = ov64a40_init_cfg,
++ .enum_mbus_code = ov64a40_enum_mbus_code,
++ .enum_frame_size = ov64a40_enum_frame_size,
++ .get_fmt = v4l2_subdev_get_fmt,
++ .set_fmt = ov64a40_set_format,
++ .get_selection = ov64a40_get_selection,
++};
++
++static const struct v4l2_subdev_core_ops ov64a40_core_ops = {
++ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
++ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
++};
++
++static const struct v4l2_subdev_ops ov64a40_subdev_ops = {
++ .core = &ov64a40_core_ops,
++ .video = &ov64a40_video_ops,
++ .pad = &ov64a40_pad_ops,
++};
++
++static int ov64a40_power_on(struct device *dev)
++{
++ struct v4l2_subdev *sd = dev_get_drvdata(dev);
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++ int ret;
++
++ ret = clk_prepare_enable(ov64a40->xclk);
++ if (ret)
++ return ret;
++
++ ret = regulator_bulk_enable(ARRAY_SIZE(ov64a40_supply_names),
++ ov64a40->supplies);
++ if (ret) {
++ clk_disable_unprepare(ov64a40->xclk);
++ dev_err(dev, "Failed to enable regulators: %d\n", ret);
++ return ret;
++ }
++
++ gpiod_set_value_cansleep(ov64a40->reset_gpio, 0);
++
++ fsleep(5000);
++
++ return 0;
++}
++
++static int ov64a40_power_off(struct device *dev)
++{
++ struct v4l2_subdev *sd = dev_get_drvdata(dev);
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++
++ gpiod_set_value_cansleep(ov64a40->reset_gpio, 1);
++ regulator_bulk_disable(ARRAY_SIZE(ov64a40_supply_names),
++ ov64a40->supplies);
++ clk_disable_unprepare(ov64a40->xclk);
++
++ return 0;
++}
++
++static int ov64a40_link_freq_config(struct ov64a40 *ov64a40, int link_freq_id)
++{
++ s64 link_frequency;
++ int ret = 0;
++
++ /* Default 456MHz with 24MHz input clock. */
++ cci_multi_reg_write(ov64a40->cci, ov64a40_pll_config,
++ ARRAY_SIZE(ov64a40_pll_config), &ret);
++
++ /* Decrease the PLL1 multiplier to obtain 360MHz mipi link frequency. */
++ link_frequency = ov64a40->link_frequencies[link_freq_id];
++ if (link_frequency == OV64A40_LINK_FREQ_360M)
++ cci_write(ov64a40->cci, OV64A40_PLL1_MULTIPLIER, 0x0078, &ret);
++
++ return ret;
++}
++
++static int ov64a40_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct ov64a40 *ov64a40 = container_of(ctrl->handler, struct ov64a40,
++ ctrl_handler);
++ int pm_status;
++ int ret = 0;
++
++ if (ctrl->id == V4L2_CID_VBLANK) {
++ int exp_max = ov64a40->mode->height + ctrl->val
++ - OV64A40_EXPOSURE_MARGIN;
++ int exp_val = min(ov64a40->exposure->cur.val, exp_max);
++
++ __v4l2_ctrl_modify_range(ov64a40->exposure,
++ ov64a40->exposure->minimum,
++ exp_max, 1, exp_val);
++ }
++
++ pm_status = pm_runtime_get_if_active(ov64a40->dev, true);
++ if (!pm_status)
++ return 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_EXPOSURE:
++ ret = cci_write(ov64a40->cci, OV64A40_REG_MEC_LONG_EXPO,
++ ctrl->val, NULL);
++ break;
++ case V4L2_CID_ANALOGUE_GAIN:
++ ret = cci_write(ov64a40->cci, OV64A40_REG_MEC_LONG_GAIN,
++ ctrl->val << 1, NULL);
++ break;
++ case V4L2_CID_VBLANK: {
++ int vts = ctrl->val + ov64a40->mode->height;
++
++ cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_LOW, vts, &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_MID,
++ (vts >> 8), &ret);
++ cci_write(ov64a40->cci, OV64A40_REG_TIMINGS_VTS_HIGH,
++ (vts >> 16), &ret);
++ break;
++ }
++ case V4L2_CID_VFLIP:
++ ret = cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_20,
++ OV64A40_TIMING_CTRL_20_VFLIP,
++ ctrl->val << 2,
++ NULL);
++ break;
++ case V4L2_CID_HFLIP:
++ ret = cci_update_bits(ov64a40->cci, OV64A40_REG_TIMING_CTRL_21,
++ OV64A40_TIMING_CTRL_21_HFLIP,
++ ctrl->val ? 0
++ : OV64A40_TIMING_CTRL_21_HFLIP,
++ NULL);
++ break;
++ case V4L2_CID_TEST_PATTERN:
++ ret = cci_write(ov64a40->cci, OV64A40_REG_TEST_PATTERN,
++ ov64a40_test_pattern_val[ctrl->val], NULL);
++ break;
++ case V4L2_CID_LINK_FREQ:
++ ret = ov64a40_link_freq_config(ov64a40, ctrl->val);
++ break;
++ default:
++ dev_err(ov64a40->dev, "Unhandled control: %#x\n", ctrl->id);
++ ret = -EINVAL;
++ break;
++ }
++
++ if (pm_status > 0) {
++ pm_runtime_mark_last_busy(ov64a40->dev);
++ pm_runtime_put_autosuspend(ov64a40->dev);
++ }
++
++ return ret;
++}
++
++static const struct v4l2_ctrl_ops ov64a40_ctrl_ops = {
++ .s_ctrl = ov64a40_set_ctrl,
++};
++
++static int ov64a40_init_controls(struct ov64a40 *ov64a40)
++{
++ int exp_max, hblank_val, vblank_max, vblank_def;
++ struct v4l2_ctrl_handler *hdlr = &ov64a40->ctrl_handler;
++ struct v4l2_fwnode_device_properties props;
++ const struct ov64a40_timings *timings;
++ int ret;
++
++ ret = v4l2_ctrl_handler_init(hdlr, 11);
++ if (ret)
++ return ret;
++
++ v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, V4L2_CID_PIXEL_RATE,
++ OV64A40_PIXEL_RATE, OV64A40_PIXEL_RATE, 1,
++ OV64A40_PIXEL_RATE);
++
++ ov64a40->link_freq =
++ v4l2_ctrl_new_int_menu(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_LINK_FREQ,
++ ov64a40->num_link_frequencies - 1,
++ 0, ov64a40->link_frequencies);
++
++ v4l2_ctrl_new_std_menu_items(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_TEST_PATTERN,
++ ARRAY_SIZE(ov64a40_test_pattern_menu) - 1,
++ 0, 0, ov64a40_test_pattern_menu);
++
++ timings = ov64a40_get_timings(ov64a40, 0);
++ exp_max = timings->vts - OV64A40_EXPOSURE_MARGIN;
++ ov64a40->exposure = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_EXPOSURE,
++ OV64A40_EXPOSURE_MIN, exp_max, 1,
++ OV64A40_EXPOSURE_MIN);
++
++ hblank_val = timings->ppl * 4 - ov64a40->mode->width;
++ ov64a40->hblank = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_HBLANK, hblank_val,
++ hblank_val, 1, hblank_val);
++ if (ov64a40->hblank)
++ ov64a40->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
++ vblank_def = timings->vts - ov64a40->mode->height;
++ vblank_max = OV64A40_VTS_MAX - ov64a40->mode->height;
++ ov64a40->vblank = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_VBLANK, OV64A40_VBLANK_MIN,
++ vblank_max, 1, vblank_def);
++
++ v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
++ OV64A40_ANA_GAIN_MIN, OV64A40_ANA_GAIN_MAX, 1,
++ OV64A40_ANA_GAIN_DEFAULT);
++
++ ov64a40->hflip = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_HFLIP, 0, 1, 1, 0);
++ if (ov64a40->hflip)
++ ov64a40->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ ov64a40->vflip = v4l2_ctrl_new_std(hdlr, &ov64a40_ctrl_ops,
++ V4L2_CID_VFLIP, 0, 1, 1, 0);
++ if (ov64a40->vflip)
++ ov64a40->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
++
++ if (hdlr->error) {
++ ret = hdlr->error;
++ dev_err(ov64a40->dev, "control init failed: %d\n", ret);
++ goto error_free_hdlr;
++ }
++
++ ret = v4l2_fwnode_device_parse(ov64a40->dev, &props);
++ if (ret)
++ goto error_free_hdlr;
++
++ ret = v4l2_ctrl_new_fwnode_properties(hdlr, &ov64a40_ctrl_ops,
++ &props);
++ if (ret)
++ goto error_free_hdlr;
++
++ ov64a40->sd.ctrl_handler = hdlr;
++
++ return 0;
++
++error_free_hdlr:
++ v4l2_ctrl_handler_free(hdlr);
++ return ret;
++}
++
++static int ov64a40_identify(struct ov64a40 *ov64a40)
++{
++ int ret;
++ u64 id;
++
++ ret = cci_read(ov64a40->cci, OV64A40_REG_CHIP_ID, &id, NULL);
++ if (ret) {
++ dev_err(ov64a40->dev, "Failed to read chip id: %d\n", ret);
++ return ret;
++ }
++
++ if (id != OV64A40_CHIP_ID) {
++ dev_err(ov64a40->dev, "chip id mismatch: %#llx\n", id);
++ return -ENODEV;
++ }
++
++ dev_dbg(ov64a40->dev, "OV64A40 chip identified: %#llx\n", id);
++
++ return 0;
++}
++
++static int ov64a40_parse_dt(struct ov64a40 *ov64a40)
++{
++ struct v4l2_fwnode_endpoint v4l2_fwnode = {
++ .bus_type = V4L2_MBUS_CSI2_DPHY
++ };
++ struct fwnode_handle *endpoint;
++ int ret = -EINVAL;
++ unsigned int i;
++
++ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(ov64a40->dev),
++ NULL);
++ if (!endpoint) {
++ dev_err(ov64a40->dev, "Failed to find endpoint\n");
++ return -EINVAL;
++ }
++
++ if (v4l2_fwnode_endpoint_alloc_parse(endpoint, &v4l2_fwnode)) {
++ dev_err(ov64a40->dev, "Failed to parse endpoint\n");
++ goto error_put_fwnode;
++
++ }
++
++ if (v4l2_fwnode.bus.mipi_csi2.num_data_lanes != 2) {
++ dev_err(ov64a40->dev, "Unsupported number of data lanes: %u\n",
++ v4l2_fwnode.bus.mipi_csi2.num_data_lanes);
++ goto error_free_fwnode;
++ }
++
++ if (!v4l2_fwnode.nr_of_link_frequencies) {
++ dev_warn(ov64a40->dev, "no link frequencies defined\n");
++ goto error_free_fwnode;
++ }
++
++ if (v4l2_fwnode.nr_of_link_frequencies > 2) {
++ dev_warn(ov64a40->dev,
++ "Unsupported number of link frequencies\n");
++ goto error_free_fwnode;
++ }
++
++ ov64a40->link_frequencies =
++ devm_kcalloc(ov64a40->dev, v4l2_fwnode.nr_of_link_frequencies,
++ sizeof(v4l2_fwnode.link_frequencies[0]),
++ GFP_KERNEL);
++ if (!ov64a40->link_frequencies) {
++ ret = -ENOMEM;
++ goto error_free_fwnode;
++ }
++ ov64a40->num_link_frequencies = v4l2_fwnode.nr_of_link_frequencies;
++
++ for (i = 0; i < v4l2_fwnode.nr_of_link_frequencies; ++i) {
++ if (v4l2_fwnode.link_frequencies[i] != OV64A40_LINK_FREQ_360M &&
++ v4l2_fwnode.link_frequencies[i] != OV64A40_LINK_FREQ_456M) {
++ dev_err(ov64a40->dev,
++ "Unsupported link frequency %lld\n",
++ v4l2_fwnode.link_frequencies[i]);
++ goto error_free_fwnode;
++ }
++
++ ov64a40->link_frequencies[i] = v4l2_fwnode.link_frequencies[i];
++ }
++
++ v4l2_fwnode_endpoint_free(&v4l2_fwnode);
++
++ /* Register the subdev on the endpoint, so don't put it yet. */
++ ov64a40->sd.fwnode = endpoint;
++
++ return 0;
++
++error_free_fwnode:
++ v4l2_fwnode_endpoint_free(&v4l2_fwnode);
++error_put_fwnode:
++ fwnode_handle_put(endpoint);
++ return ret;
++}
++
++static int ov64a40_get_regulators(struct ov64a40 *ov64a40)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&ov64a40->sd);
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(ov64a40_supply_names); i++)
++ ov64a40->supplies[i].supply = ov64a40_supply_names[i];
++
++ return devm_regulator_bulk_get(&client->dev,
++ ARRAY_SIZE(ov64a40_supply_names),
++ ov64a40->supplies);
++}
++
++static int ov64a40_probe(struct i2c_client *client)
++{
++ struct ov64a40 *ov64a40;
++ u32 xclk_freq;
++ int ret;
++
++ ov64a40 = devm_kzalloc(&client->dev, sizeof(*ov64a40), GFP_KERNEL);
++ if (!ov64a40)
++ return -ENOMEM;
++
++ ov64a40->dev = &client->dev;
++ v4l2_i2c_subdev_init(&ov64a40->sd, client, &ov64a40_subdev_ops);
++
++ ov64a40->cci = devm_cci_regmap_init_i2c(client, 16);
++ if (IS_ERR(ov64a40->cci)) {
++ dev_err(&client->dev, "Failed to initialize CCI\n");
++ return PTR_ERR(ov64a40->cci);
++ }
++
++ ov64a40->xclk = devm_clk_get(&client->dev, NULL);
++ if (!ov64a40->xclk)
++ return dev_err_probe(&client->dev, PTR_ERR(ov64a40->xclk),
++ "Failed to get clock\n");
++
++ xclk_freq = clk_get_rate(ov64a40->xclk);
++ if (xclk_freq != OV64A40_XCLK_FREQ) {
++ dev_err(&client->dev, "Unsupported xclk frequency %u\n",
++ xclk_freq);
++ return -EINVAL;
++ }
++
++ ret = ov64a40_get_regulators(ov64a40);
++ if (ret)
++ return ret;
++
++ ov64a40->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
++ GPIOD_OUT_LOW);
++ if (IS_ERR(ov64a40->reset_gpio))
++ return dev_err_probe(&client->dev, PTR_ERR(ov64a40->reset_gpio),
++ "Failed to get reset gpio\n");
++
++ ret = ov64a40_parse_dt(ov64a40);
++ if (ret)
++ return ret;
++
++ ret = ov64a40_power_on(&client->dev);
++ if (ret)
++ goto error_put_fwnode;
++
++ ret = ov64a40_identify(ov64a40);
++ if (ret)
++ goto error_poweroff;
++
++ ov64a40->mode = &ov64a40_modes[0];
++
++ pm_runtime_set_active(&client->dev);
++ pm_runtime_get_noresume(&client->dev);
++ pm_runtime_enable(&client->dev);
++ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
++ pm_runtime_use_autosuspend(&client->dev);
++
++ ret = ov64a40_init_controls(ov64a40);
++ if (ret)
++ goto error_poweroff;
++
++ /* Initialize subdev */
++ ov64a40->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE
++ | V4L2_SUBDEV_FL_HAS_EVENTS;
++ ov64a40->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
++
++ ov64a40->pad.flags = MEDIA_PAD_FL_SOURCE;
++ ret = media_entity_pads_init(&ov64a40->sd.entity, 1, &ov64a40->pad);
++ if (ret) {
++ dev_err(&client->dev, "failed to init entity pads: %d\n", ret);
++ goto error_handler_free;
++ }
++
++ ov64a40->sd.state_lock = ov64a40->ctrl_handler.lock;
++ ret = v4l2_subdev_init_finalize(&ov64a40->sd);
++ if (ret < 0) {
++ dev_err(&client->dev, "subdev init error: %d\n", ret);
++ goto error_media_entity;
++ }
++
++ ret = v4l2_async_register_subdev_sensor(&ov64a40->sd);
++ if (ret < 0) {
++ dev_err(&client->dev,
++ "failed to register sensor sub-device: %d\n", ret);
++ goto error_subdev_cleanup;
++ }
++
++ pm_runtime_mark_last_busy(&client->dev);
++ pm_runtime_put_autosuspend(&client->dev);
++
++ return 0;
++
++error_subdev_cleanup:
++ v4l2_subdev_cleanup(&ov64a40->sd);
++error_media_entity:
++ media_entity_cleanup(&ov64a40->sd.entity);
++error_handler_free:
++ v4l2_ctrl_handler_free(ov64a40->sd.ctrl_handler);
++error_poweroff:
++ ov64a40_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++error_put_fwnode:
++ fwnode_handle_put(ov64a40->sd.fwnode);
++
++ return ret;
++}
++
++static void ov64a40_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ov64a40 *ov64a40 = sd_to_ov64a40(sd);
++
++ v4l2_async_unregister_subdev(sd);
++ fwnode_handle_put(ov64a40->sd.fwnode);
++ v4l2_subdev_cleanup(sd);
++ media_entity_cleanup(&sd->entity);
++ v4l2_ctrl_handler_free(sd->ctrl_handler);
++
++ pm_runtime_disable(&client->dev);
++ if (!pm_runtime_status_suspended(&client->dev))
++ ov64a40_power_off(&client->dev);
++ pm_runtime_set_suspended(&client->dev);
++}
++
++static const struct of_device_id ov64a40_of_ids[] = {
++ { .compatible = "ovti,ov64a40" },
++ { /* sentinel */ }
++};
++MODULE_DEVICE_TABLE(of, ov64a40_of_ids);
++
++static const struct dev_pm_ops ov64a40_pm_ops = {
++ SET_RUNTIME_PM_OPS(ov64a40_power_off, ov64a40_power_on, NULL)
++};
++
++static struct i2c_driver ov64a40_i2c_driver = {
++ .driver = {
++ .name = "ov64a40",
++ .of_match_table = ov64a40_of_ids,
++ .pm = &ov64a40_pm_ops,
++ },
++ .probe_new = ov64a40_probe,
++ .remove = ov64a40_remove,
++};
++
++module_i2c_driver(ov64a40_i2c_driver);
++
++MODULE_AUTHOR("Jacopo Mondi <jacopo.mondi@ideasonboard.com>");
++MODULE_DESCRIPTION("OmniVision OV64A40 sensor driver");
++MODULE_LICENSE("GPL");
--- /dev/null
+From 97ec6aeb265df0bfe7193f00c249b38873fb0fb7 Mon Sep 17 00:00:00 2001
+From: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Date: Wed, 13 Sep 2023 17:53:54 +0100
+Subject: [PATCH] media: i2c: Add ROHM BU64754 Camera Autofocus Actuator
+
+Add support for the ROHM BU64754 Motor Driver for Camera Autofocus. A
+V4L2 Subdevice is registered and provides a single
+V4L2_CID_FOCUS_ABSOLUTE control.
+
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+---
+ drivers/media/i2c/Kconfig | 13 ++
+ drivers/media/i2c/Makefile | 1 +
+ drivers/media/i2c/bu64754.c | 315 ++++++++++++++++++++++++++++++++++++
+ 3 files changed, 329 insertions(+)
+ create mode 100644 drivers/media/i2c/bu64754.c
+
+--- a/drivers/media/i2c/Kconfig
++++ b/drivers/media/i2c/Kconfig
+@@ -917,6 +917,19 @@ config VIDEO_AK7375
+ capability. This is designed for linear control of
+ voice coil motors, controlled via I2C serial interface.
+
++config VIDEO_BU64754
++ tristate "BU64754 Motor Driver for Camera Autofocus"
++ depends on I2C && VIDEO_DEV
++ select MEDIA_CONTROLLER
++ select VIDEO_V4L2_SUBDEV_API
++ select V4L2_ASYNC
++ select V4L2_CCI_I2C
++ help
++ This is a driver for the BU64754 Motor Driver for Camera
++ Autofocus. The BU64754GWZ is an actuator driver IC which
++ can be controlled the actuator position precisely using
++ with internal Hall Sensor.
++
+ config VIDEO_DW9714
+ tristate "DW9714 lens voice coil support"
+ depends on I2C && VIDEO_DEV
+--- a/drivers/media/i2c/Makefile
++++ b/drivers/media/i2c/Makefile
+@@ -26,6 +26,7 @@ obj-$(CONFIG_VIDEO_ARDUCAM_PIVARIETY) +=
+ obj-$(CONFIG_VIDEO_BT819) += bt819.o
+ obj-$(CONFIG_VIDEO_BT856) += bt856.o
+ obj-$(CONFIG_VIDEO_BT866) += bt866.o
++obj-$(CONFIG_VIDEO_BU64754) += bu64754.o
+ obj-$(CONFIG_VIDEO_CCS) += ccs/
+ obj-$(CONFIG_VIDEO_CCS_PLL) += ccs-pll.o
+ obj-$(CONFIG_VIDEO_CS3308) += cs3308.o
+--- /dev/null
++++ b/drivers/media/i2c/bu64754.c
+@@ -0,0 +1,315 @@
++// SPDX-License-Identifier: GPL-2.0
++/*
++ * The BU64754GWZ is an actuator driver IC which can control the
++ * actuator position precisely using an internal Hall Sensor.
++ */
++
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++
++#include <media/v4l2-cci.h>
++#include <media/v4l2-ctrls.h>
++#include <media/v4l2-device.h>
++
++#define BU64754_REG_ACTIVE CCI_REG16(0x07)
++#define BU64754_ACTIVE_MODE 0x8080
++
++#define BU64754_REG_SERVE CCI_REG16(0xd9)
++#define BU64754_SERVE_ON 0x0404
++
++#define BU64754_REG_POSITION CCI_REG16(0x45)
++#define BU64753_POSITION_MAX 1023 /* 0x3ff */
++#define BU64753_POSITION_STEPS 1
++
++#define BU64754_POWER_ON_DELAY 800 /* uS : t1, t3 */
++
++struct bu64754 {
++ struct device *dev;
++
++ struct v4l2_ctrl_handler ctrls_vcm;
++ struct v4l2_subdev sd;
++ struct regmap *cci;
++
++ u16 current_val;
++ struct regulator *vdd;
++ struct notifier_block notifier;
++};
++
++static inline struct bu64754 *sd_to_bu64754(struct v4l2_subdev *subdev)
++{
++ return container_of(subdev, struct bu64754, sd);
++}
++
++static int bu64754_set(struct bu64754 *bu64754, u16 position)
++{
++ int ret;
++
++ position &= 0x3ff; /* BU64753_POSITION_MAX */
++ ret = cci_write(bu64754->cci, BU64754_REG_POSITION, position, NULL);
++ if (ret) {
++ dev_err(bu64754->dev, "Set position failed ret=%d\n", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int bu64754_active(struct bu64754 *bu64754)
++{
++ int ret;
++
++ /* Power on */
++ ret = cci_write(bu64754->cci, BU64754_REG_ACTIVE, BU64754_ACTIVE_MODE, NULL);
++ if (ret < 0) {
++ dev_err(bu64754->dev, "Failed to set active mode ret = %d\n",
++ ret);
++ return ret;
++ }
++
++ /* Serve on */
++ ret = cci_write(bu64754->cci, BU64754_REG_SERVE, BU64754_SERVE_ON, NULL);
++ if (ret < 0) {
++ dev_err(bu64754->dev, "Failed to enable serve ret = %d\n",
++ ret);
++ return ret;
++ }
++
++ return bu64754_set(bu64754, bu64754->current_val);
++}
++
++static int bu64754_standby(struct bu64754 *bu64754)
++{
++ int ret;
++
++ ret = cci_write(bu64754->cci, BU64754_REG_ACTIVE, 0, NULL);
++ if (ret < 0)
++ dev_err(bu64754->dev, "Failed to enter standby mode ret = %d\n",
++ ret);
++
++ return ret;
++}
++
++static int bu64754_regulator_event(struct notifier_block *nb,
++ unsigned long action, void *data)
++{
++ struct bu64754 *bu64754 = container_of(nb, struct bu64754, notifier);
++
++ if (action & REGULATOR_EVENT_ENABLE) {
++ /*
++ * Initialisation delay between VDD low->high and availability
++ * i2c operation.
++ */
++ usleep_range(BU64754_POWER_ON_DELAY,
++ BU64754_POWER_ON_DELAY + 100);
++
++ bu64754_active(bu64754);
++ } else if (action & REGULATOR_EVENT_PRE_DISABLE) {
++ bu64754_standby(bu64754);
++ }
++
++ return 0;
++}
++
++static int bu64754_set_ctrl(struct v4l2_ctrl *ctrl)
++{
++ struct bu64754 *bu64754 = container_of(ctrl->handler,
++ struct bu64754, ctrls_vcm);
++
++ if (ctrl->id == V4L2_CID_FOCUS_ABSOLUTE) {
++ bu64754->current_val = ctrl->val;
++ return bu64754_set(bu64754, ctrl->val);
++ }
++
++ return -EINVAL;
++}
++
++static const struct v4l2_ctrl_ops bu64754_vcm_ctrl_ops = {
++ .s_ctrl = bu64754_set_ctrl,
++};
++
++static int bu64754_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ return pm_runtime_resume_and_get(sd->dev);
++}
++
++static int bu64754_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
++{
++ pm_runtime_put(sd->dev);
++ return 0;
++}
++
++static const struct v4l2_subdev_internal_ops bu64754_int_ops = {
++ .open = bu64754_open,
++ .close = bu64754_close,
++};
++
++static const struct v4l2_subdev_ops bu64754_ops = { };
++
++static void bu64754_subdev_cleanup(struct bu64754 *bu64754)
++{
++ v4l2_async_unregister_subdev(&bu64754->sd);
++ v4l2_ctrl_handler_free(&bu64754->ctrls_vcm);
++ media_entity_cleanup(&bu64754->sd.entity);
++}
++
++static int bu64754_init_controls(struct bu64754 *bu64754)
++{
++ struct v4l2_ctrl_handler *hdl = &bu64754->ctrls_vcm;
++ const struct v4l2_ctrl_ops *ops = &bu64754_vcm_ctrl_ops;
++
++ v4l2_ctrl_handler_init(hdl, 1);
++
++ v4l2_ctrl_new_std(hdl, ops, V4L2_CID_FOCUS_ABSOLUTE,
++ 0, BU64753_POSITION_MAX, BU64753_POSITION_STEPS,
++ 0);
++
++ bu64754->current_val = 0;
++
++ bu64754->sd.ctrl_handler = hdl;
++ if (hdl->error) {
++ dev_err(bu64754->dev, "%s fail error: 0x%x\n",
++ __func__, hdl->error);
++ return hdl->error;
++ }
++
++ return 0;
++}
++
++static int bu64754_probe(struct i2c_client *client)
++{
++ struct bu64754 *bu64754;
++ int ret;
++
++ bu64754 = devm_kzalloc(&client->dev, sizeof(*bu64754), GFP_KERNEL);
++ if (!bu64754)
++ return -ENOMEM;
++
++ bu64754->dev = &client->dev;
++
++ bu64754->cci = devm_cci_regmap_init_i2c(client, 8);
++ if (IS_ERR(bu64754->cci)) {
++ dev_err(bu64754->dev, "Failed to initialize CCI\n");
++ return PTR_ERR(bu64754->cci);
++ }
++
++ bu64754->vdd = devm_regulator_get_optional(&client->dev, "vdd");
++ if (IS_ERR(bu64754->vdd)) {
++ if (PTR_ERR(bu64754->vdd) != -ENODEV)
++ return PTR_ERR(bu64754->vdd);
++
++ bu64754->vdd = NULL;
++ } else {
++ bu64754->notifier.notifier_call = bu64754_regulator_event;
++
++ ret = regulator_register_notifier(bu64754->vdd,
++ &bu64754->notifier);
++ if (ret) {
++ dev_err(bu64754->dev,
++ "could not register regulator notifier\n");
++ return ret;
++ }
++ }
++
++ v4l2_i2c_subdev_init(&bu64754->sd, client, &bu64754_ops);
++ bu64754->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
++ bu64754->sd.internal_ops = &bu64754_int_ops;
++ bu64754->sd.entity.function = MEDIA_ENT_F_LENS;
++
++ ret = bu64754_init_controls(bu64754);
++ if (ret)
++ goto err_cleanup;
++
++ ret = media_entity_pads_init(&bu64754->sd.entity, 0, NULL);
++ if (ret < 0)
++ goto err_cleanup;
++
++ bu64754->sd.entity.function = MEDIA_ENT_F_LENS;
++
++ ret = v4l2_async_register_subdev(&bu64754->sd);
++ if (ret < 0)
++ goto err_cleanup;
++
++ if (!bu64754->vdd)
++ pm_runtime_set_active(&client->dev);
++
++ pm_runtime_enable(&client->dev);
++ pm_runtime_idle(&client->dev);
++
++ return 0;
++
++err_cleanup:
++ v4l2_ctrl_handler_free(&bu64754->ctrls_vcm);
++ media_entity_cleanup(&bu64754->sd.entity);
++
++ return ret;
++}
++
++static void bu64754_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct bu64754 *bu64754 = sd_to_bu64754(sd);
++
++ if (bu64754->vdd)
++ regulator_unregister_notifier(bu64754->vdd,
++ &bu64754->notifier);
++
++ pm_runtime_disable(&client->dev);
++
++ bu64754_subdev_cleanup(bu64754);
++}
++
++static int __maybe_unused bu64754_vcm_suspend(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct bu64754 *bu64754 = sd_to_bu64754(sd);
++
++ if (bu64754->vdd)
++ return regulator_disable(bu64754->vdd);
++
++ return bu64754_standby(bu64754);
++}
++
++static int __maybe_unused bu64754_vcm_resume(struct device *dev)
++{
++ struct i2c_client *client = to_i2c_client(dev);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct bu64754 *bu64754 = sd_to_bu64754(sd);
++
++ if (bu64754->vdd)
++ return regulator_enable(bu64754->vdd);
++
++ return bu64754_active(bu64754);
++}
++
++static const struct of_device_id bu64754_of_table[] = {
++ { .compatible = "rohm,bu64754", },
++ { /* sentinel */ }
++};
++
++MODULE_DEVICE_TABLE(of, bu64754_of_table);
++
++static const struct dev_pm_ops bu64754_pm_ops = {
++ SET_SYSTEM_SLEEP_PM_OPS(bu64754_vcm_suspend, bu64754_vcm_resume)
++ SET_RUNTIME_PM_OPS(bu64754_vcm_suspend, bu64754_vcm_resume, NULL)
++};
++
++static struct i2c_driver bu64754_i2c_driver = {
++ .driver = {
++ .name = "bu64754",
++ .pm = &bu64754_pm_ops,
++ .of_match_table = bu64754_of_table,
++ },
++ .probe_new = bu64754_probe,
++ .remove = bu64754_remove,
++};
++
++module_i2c_driver(bu64754_i2c_driver);
++
++MODULE_AUTHOR("Kieran Bingham");
++MODULE_DESCRIPTION("BU64754 VCM driver");
++MODULE_LICENSE("GPL");
++
--- /dev/null
+From 7f67a45ee7c008c3d8e45fde6fa9c4287fb3bc9e Mon Sep 17 00:00:00 2001
+From: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Date: Thu, 20 Jul 2023 13:18:34 +0200
+Subject: [PATCH] overlays: Add overlay for the OV64A40 Arducam Camera Module
+
+Arducam have integrated an Omnivision OV64A40 with a ROHM BU64754 VCM
+with a Raspberry Pi compatible cable pinout.
+
+Provide an overlay to support the module.
+
+Also add support to the camera mux overlays.
+
+Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
+Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
+---
+ arch/arm/boot/dts/overlays/Makefile | 1 +
+ arch/arm/boot/dts/overlays/README | 25 +++++
+ .../dts/overlays/camera-mux-2port-overlay.dts | 32 +++++++
+ .../dts/overlays/camera-mux-4port-overlay.dts | 64 +++++++++++++
+ .../arm/boot/dts/overlays/ov64a40-overlay.dts | 91 +++++++++++++++++++
+ arch/arm/boot/dts/overlays/ov64a40.dtsi | 34 +++++++
+ 6 files changed, 247 insertions(+)
+ create mode 100644 arch/arm/boot/dts/overlays/ov64a40-overlay.dts
+ create mode 100644 arch/arm/boot/dts/overlays/ov64a40.dtsi
+
+--- a/arch/arm/boot/dts/overlays/Makefile
++++ b/arch/arm/boot/dts/overlays/Makefile
+@@ -175,6 +175,7 @@ dtbo-$(CONFIG_ARCH_BCM2835) += \
+ mz61581.dtbo \
+ ov2311.dtbo \
+ ov5647.dtbo \
++ ov64a40.dtbo \
+ ov7251.dtbo \
+ ov9281.dtbo \
+ papirus.dtbo \
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -817,6 +817,7 @@ Params: cam0-arducam-64mp Select A
+ cam0-imx708 Select IMX708 for camera on port 0
+ cam0-ov2311 Select OV2311 for camera on port 0
+ cam0-ov5647 Select OV5647 for camera on port 0
++ cam0-ov64a40 Select OV64A40 for camera on port 0
+ cam0-ov7251 Select OV7251 for camera on port 0
+ cam0-ov9281 Select OV9281 for camera on port 0
+ cam0-imx290-clk-freq Set clock frequency for an IMX290 on port 0
+@@ -829,6 +830,7 @@ Params: cam0-arducam-64mp Select A
+ cam1-imx708 Select IMX708 for camera on port 1
+ cam1-ov2311 Select OV2311 for camera on port 1
+ cam1-ov5647 Select OV5647 for camera on port 1
++ cam1-ov64a40 Select OV64A40 for camera on port 1
+ cam1-ov7251 Select OV7251 for camera on port 1
+ cam1-ov9281 Select OV9281 for camera on port 1
+ cam1-imx290-clk-freq Set clock frequency for an IMX290 on port 1
+@@ -850,6 +852,7 @@ Params: cam0-arducam-64mp Select A
+ cam0-imx708 Select IMX708 for camera on port 0
+ cam0-ov2311 Select OV2311 for camera on port 0
+ cam0-ov5647 Select OV5647 for camera on port 0
++ cam0-ov64a40 Select OV64A40 for camera on port 0
+ cam0-ov7251 Select OV7251 for camera on port 0
+ cam0-ov9281 Select OV9281 for camera on port 0
+ cam0-imx290-clk-freq Set clock frequency for an IMX290 on port 0
+@@ -862,6 +865,7 @@ Params: cam0-arducam-64mp Select A
+ cam1-imx708 Select IMX708 for camera on port 1
+ cam1-ov2311 Select OV2311 for camera on port 1
+ cam1-ov5647 Select OV5647 for camera on port 1
++ cam1-ov64a40 Select OV64A40 for camera on port 1
+ cam1-ov7251 Select OV7251 for camera on port 1
+ cam1-ov9281 Select OV9281 for camera on port 1
+ cam1-imx290-clk-freq Set clock frequency for an IMX290 on port 1
+@@ -874,6 +878,7 @@ Params: cam0-arducam-64mp Select A
+ cam2-imx708 Select IMX708 for camera on port 2
+ cam2-ov2311 Select OV2311 for camera on port 2
+ cam2-ov5647 Select OV5647 for camera on port 2
++ cam2-ov64a40 Select OV64A40 for camera on port 2
+ cam2-ov7251 Select OV7251 for camera on port 2
+ cam2-ov9281 Select OV9281 for camera on port 2
+ cam2-imx290-clk-freq Set clock frequency for an IMX290 on port 2
+@@ -886,6 +891,7 @@ Params: cam0-arducam-64mp Select A
+ cam3-imx708 Select IMX708 for camera on port 3
+ cam3-ov2311 Select OV2311 for camera on port 3
+ cam3-ov5647 Select OV5647 for camera on port 3
++ cam3-ov64a40 Select OV64A40 for camera on port 3
+ cam3-ov7251 Select OV7251 for camera on port 3
+ cam3-ov9281 Select OV9281 for camera on port 3
+ cam3-imx290-clk-freq Set clock frequency for an IMX290 on port 3
+@@ -3222,6 +3228,25 @@ Params: rotation Mounting
+ vcm Configure a VCM focus drive on the sensor.
+
+
++Name: ov64a40
++Info: Arducam OV64A40 camera module.
++ Uses Unicam 1, which is the standard camera connector on most Pi
++ variants.
++Load: dtoverlay=ov64a40,<param>=<val>
++Params: rotation Mounting rotation of the camera sensor (0 or
++ 180, default 0)
++ orientation Sensor orientation (0 = front, 1 = rear,
++ 2 = external, default external)
++ media-controller Configure use of Media Controller API for
++ configuring the sensor (default on)
++ cam0 Adopt the default configuration for CAM0 on a
++ Compute Module (CSI0, i2c_vc, and cam0_reg).
++ vcm Select lens driver state. Default is enabled,
++ but vcm=off will disable.
++ link-frequency Allowable link frequency values to use in Hz:
++ 456000000 (default), 360000000
++
++
+ Name: ov7251
+ Info: Omnivision OV7251 camera module.
+ Uses Unicam 1, which is the standard camera connector on most Pi
+--- a/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-2port-overlay.dts
+@@ -189,6 +189,16 @@
+ #undef cam_node
+ #undef cam_endpoint
+ #undef cam1_clk
++
++ #define cam_node ov64a40_0
++ #define cam_endpoint ov64a40_0_ep
++ #define vcm_node ov64a40_0_vcm
++ #define cam1_clk clk_24mhz
++ #include "ov64a40.dtsi"
++ #undef cam_node
++ #undef cam_endpoint
++ #undef vcm_node
++ #undef cam1_clk
+ };
+
+ i2c@1 {
+@@ -289,6 +299,16 @@
+ #undef cam_node
+ #undef cam_endpoint
+ #undef cam1_clk
++
++ #define cam_node ov64a40_1
++ #define cam_endpoint ov64a40_1_ep
++ #define vcm_node ov64a40_1_vcm
++ #define cam1_clk clk_24mhz
++ #include "ov64a40.dtsi"
++ #undef cam_node
++ #undef cam_endpoint
++ #undef vcm_node
++ #undef cam1_clk
+ };
+ };
+ };
+@@ -450,6 +470,12 @@
+ cam0-ov2311 = <&mux_in0>, "remote-endpoint:0=",<&ov2311_0_ep>,
+ <&ov2311_0_ep>, "remote-endpoint:0=",<&mux_in0>,
+ <&ov2311_0>, "status=okay";
++ cam0-ov64a40 = <&mux_in0>, "remote-endpoint:0=",<&ov64a40_0_ep>,
++ <&ov64a40_0_ep>, "remote-endpoint:0=",<&mux_in0>,
++ <&mux_in0>, "clock-noncontinuous?",
++ <&ov64a40_0>, "status=okay",
++ <&ov64a40_0_vcm>, "status=okay",
++ <&ov64a40_0>,"lens-focus:0=", <&ov64a40_0_vcm>;
+
+ cam1-arducam-64mp = <&mux_in1>, "remote-endpoint:0=",<&arducam_64mp_1_ep>,
+ <&arducam_64mp_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+@@ -496,6 +522,12 @@
+ cam1-ov2311 = <&mux_in1>, "remote-endpoint:0=",<&ov2311_1_ep>,
+ <&ov2311_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+ <&ov2311_1>, "status=okay";
++ cam1-ov64a40 = <&mux_in1>, "remote-endpoint:0=",<&ov64a40_1_ep>,
++ <&ov64a40_1_ep>, "remote-endpoint:0=",<&mux_in1>,
++ <&mux_in1>, "clock-noncontinuous?",
++ <&ov64a40_1>, "status=okay",
++ <&ov64a40_1_vcm>, "status=okay",
++ <&ov64a40_1>,"lens-focus:0=", <&ov64a40_1_vcm>;
+
+ cam0-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+ <&imx290_0>,"clock-frequency:0";
+--- a/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
++++ b/arch/arm/boot/dts/overlays/camera-mux-4port-overlay.dts
+@@ -247,6 +247,16 @@
+ #undef cam_node
+ #undef cam_endpoint
+ #undef cam1_clk
++
++ #define cam_node ov64a40_0
++ #define cam_endpoint ov64a40_0_ep
++ #define vcm_node ov64a40_0_vcm
++ #define cam1_clk clk_24mhz
++ #include "ov64a40.dtsi"
++ #undef cam_node
++ #undef cam_endpoint
++ #undef vcm_node
++ #undef cam1_clk
+ };
+
+ i2c@1 {
+@@ -347,6 +357,16 @@
+ #undef cam_node
+ #undef cam_endpoint
+ #undef cam1_clk
++
++ #define cam_node ov64a40_1
++ #define cam_endpoint ov64a40_1_ep
++ #define vcm_node ov64a40_1_vcm
++ #define cam1_clk clk_24mhz
++ #include "ov64a40.dtsi"
++ #undef cam_node
++ #undef cam_endpoint
++ #undef vcm_node
++ #undef cam1_clk
+ };
+
+ i2c@2 {
+@@ -447,6 +467,16 @@
+ #undef cam_node
+ #undef cam_endpoint
+ #undef cam1_clk
++
++ #define cam_node ov64a40_2
++ #define cam_endpoint ov64a40_2_ep
++ #define vcm_node ov64a40_2_vcm
++ #define cam1_clk clk_24mhz
++ #include "ov64a40.dtsi"
++ #undef cam_node
++ #undef cam_endpoint
++ #undef vcm_node
++ #undef cam1_clk
+ };
+
+ i2c@3 {
+@@ -547,6 +577,16 @@
+ #undef cam_node
+ #undef cam_endpoint
+ #undef cam1_clk
++
++ #define cam_node ov64a40_3
++ #define cam_endpoint ov64a40_3_ep
++ #define vcm_node ov64a40_3_vcm
++ #define cam1_clk clk_24mhz
++ #include "ov64a40.dtsi"
++ #undef cam_node
++ #undef cam_endpoint
++ #undef vcm_node
++ #undef cam1_clk
+ };
+ };
+ };
+@@ -725,6 +765,12 @@
+ cam0-ov2311 = <&mux_in0>, "remote-endpoint:0=",<&ov2311_0_ep>,
+ <&ov2311_0_ep>, "remote-endpoint:0=",<&mux_in0>,
+ <&ov2311_0>, "status=okay";
++ cam0-ov64a40 = <&mux_in0>, "remote-endpoint:0=",<&ov64a40_0_ep>,
++ <&ov64a40_0_ep>, "remote-endpoint:0=",<&mux_in0>,
++ <&mux_in0>, "clock-noncontinuous?",
++ <&ov64a40_0>, "status=okay",
++ <&ov64a40_0_vcm>, "status=okay",
++ <&ov64a40_0>,"lens-focus:0=", <&ov64a40_0_vcm>;
+
+ cam1-arducam-64mp = <&mux_in1>, "remote-endpoint:0=",<&arducam_64mp_1_ep>,
+ <&arducam_64mp_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+@@ -771,6 +817,12 @@
+ cam1-ov2311 = <&mux_in1>, "remote-endpoint:0=",<&ov2311_1_ep>,
+ <&ov2311_1_ep>, "remote-endpoint:0=",<&mux_in1>,
+ <&ov2311_1>, "status=okay";
++ cam1-ov64a40 = <&mux_in1>, "remote-endpoint:0=",<&ov64a40_1_ep>,
++ <&ov64a40_1_ep>, "remote-endpoint:0=",<&mux_in1>,
++ <&mux_in1>, "clock-noncontinuous?",
++ <&ov64a40_1>, "status=okay",
++ <&ov64a40_1_vcm>, "status=okay",
++ <&ov64a40_1>,"lens-focus:0=", <&ov64a40_1_vcm>;
+
+ cam2-arducam-64mp = <&mux_in2>, "remote-endpoint:0=",<&arducam_64mp_2_ep>,
+ <&arducam_64mp_2_ep>, "remote-endpoint:0=",<&mux_in2>,
+@@ -817,6 +869,12 @@
+ cam2-ov2311 = <&mux_in2>, "remote-endpoint:0=",<&ov2311_2_ep>,
+ <&ov2311_2_ep>, "remote-endpoint:0=",<&mux_in2>,
+ <&ov2311_2>, "status=okay";
++ cam2-ov64a40 = <&mux_in2>, "remote-endpoint:0=",<&ov64a40_2_ep>,
++ <&ov64a40_2_ep>, "remote-endpoint:0=",<&mux_in2>,
++ <&mux_in2>, "clock-noncontinuous?",
++ <&ov64a40_2>, "status=okay",
++ <&ov64a40_2_vcm>, "status=okay",
++ <&ov64a40_2>,"lens-focus:0=", <&ov64a40_2_vcm>;
+
+ cam3-arducam-64mp = <&mux_in3>, "remote-endpoint:0=",<&arducam_64mp_3_ep>,
+ <&arducam_64mp_3_ep>, "remote-endpoint:0=",<&mux_in3>,
+@@ -863,6 +921,12 @@
+ cam3-ov2311 = <&mux_in3>, "remote-endpoint:0=",<&ov2311_3_ep>,
+ <&ov2311_3_ep>, "remote-endpoint:0=",<&mux_in3>,
+ <&ov2311_3>, "status=okay";
++ cam3-ov64a40 = <&mux_in3>, "remote-endpoint:0=",<&ov64a40_3_ep>,
++ <&ov64a40_3_ep>, "remote-endpoint:0=",<&mux_in3>,
++ <&mux_in3>, "clock-noncontinuous?",
++ <&ov64a40_3>, "status=okay",
++ <&ov64a40_3_vcm>, "status=okay",
++ <&ov64a40_3>,"lens-focus:0=", <&ov64a40_3_vcm>;
+
+ cam0-imx290-clk-freq = <&clk_imx290>,"clock-frequency:0",
+ <&imx290_0>,"clock-frequency:0";
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/ov64a40-overlay.dts
+@@ -0,0 +1,91 @@
++// SPDX-License-Identifier: GPL-2.0-only
++// Definitions for OV64A40 camera module on VC I2C bus
++/dts-v1/;
++/plugin/;
++
++/{
++ compatible = "brcm,bcm2835";
++
++ i2c_frag: fragment@0 {
++ target = <&i2c_csi_dsi>;
++ __overlay__ {
++ #address-cells = <1>;
++ #size-cells = <0>;
++ status = "okay";
++
++ #include "ov64a40.dtsi"
++ };
++ };
++
++ csi_frag: fragment@1 {
++ target = <&csi1>;
++ csi: __overlay__ {
++ status = "okay";
++ brcm,media-controller;
++
++ port{
++ csi_ep: endpoint{
++ remote-endpoint = <&cam_endpoint>;
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ };
++ };
++ };
++ };
++
++ fragment@2 {
++ target = <&i2c0if>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ clk_frag: fragment@3 {
++ target = <&cam1_clk>;
++ __overlay__ {
++ clock-frequency = <24000000>;
++ status = "okay";
++ };
++ };
++
++ fragment@4 {
++ target = <&i2c0mux>;
++ __overlay__ {
++ status = "okay";
++ };
++ };
++
++ fragment@5 {
++ target = <&cam_node>;
++ __overlay__ {
++ lens-focus = <&vcm_node>;
++ };
++ };
++
++ __overrides__ {
++ rotation = <&cam_node>,"rotation:0";
++ orientation = <&cam_node>,"orientation:0";
++ media-controller = <&csi>,"brcm,media-controller?";
++ cam0 = <&i2c_frag>, "target:0=",<&i2c_csi_dsi0>,
++ <&csi_frag>, "target:0=",<&csi0>,
++ <&clk_frag>, "target:0=",<&cam0_clk>,
++ <&cam_node>, "clocks:0=",<&cam0_clk>,
++ <&cam_node>, "avdd-supply:0=",<&cam0_reg>,
++ <&vcm_node>, "vdd-supply:0=",<&cam0_reg>;
++ vcm = <&vcm_node>, "status",
++ <0>, "=5";
++ link-frequency = <&cam_endpoint>,"link-frequencies#0";
++ };
++};
++
++&cam_node {
++ status = "okay";
++};
++
++&cam_endpoint {
++ remote-endpoint = <&csi_ep>;
++};
++
++&vcm_node {
++ status = "okay";
++};
+--- /dev/null
++++ b/arch/arm/boot/dts/overlays/ov64a40.dtsi
+@@ -0,0 +1,34 @@
++// Fragment that configures an OV64A40
++
++cam_node: ov64a40@36 {
++ compatible = "ovti,ov64a40";
++ reg = <0x36>;
++ status = "disabled";
++
++ clocks = <&cam1_clk>;
++ clock-names = "xclk";
++
++ avdd-supply = <&cam1_reg>; /* 2.8v */
++ dovdd-supply = <&cam_dummy_reg>;/* 1.8v */
++ dvdd-supply = <&cam_dummy_reg>; /* 1.1v */
++
++ rotation = <180>;
++ orientation = <2>;
++
++ port {
++ cam_endpoint: endpoint {
++ bus-type = <4>;
++ clock-lanes = <0>;
++ data-lanes = <1 2>;
++ link-frequencies =
++ /bits/ 64 <456000000>;
++ };
++ };
++};
++
++vcm_node: bu64754@76 {
++ compatible = "rohm,bu64754";
++ reg = <0x76>;
++ status = "disabled";
++ vdd-supply = <&cam1_reg>;
++};
--- /dev/null
+From 8ef68aadaa3aa29bc2661ab44db4ddc50e77cef5 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 4 Oct 2023 11:25:16 +0300
+Subject: [PATCH] media: rp1: cfe: Fix verbose debug print
+
+Switch a debug print from cfe_dbg() to cfe_dbg_verbose() as it will be
+printed often while streaming.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -975,8 +975,8 @@ static void cfe_buffer_queue(struct vb2_
+
+ if (!cfe->job_queued && cfe->job_ready &&
+ test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
+- cfe_dbg("Preparing job immediately for channel %u\n",
+- node->id);
++ cfe_dbg_verbose("Preparing job immediately for channel %u\n",
++ node->id);
+ cfe_prepare_next_job(cfe);
+ }
+
--- /dev/null
+From d978e784f433346d3676b5de805b3cea36b835c4 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 16:23:58 +0300
+Subject: [PATCH] media: rp1: cfe: Expose find_format_by_pix()
+
+Make find_format_by_pix() accessible to other files in the driver.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.c | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe.h | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -461,7 +461,7 @@ const struct cfe_fmt *find_format_by_cod
+ return NULL;
+ }
+
+-static const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
++const struct cfe_fmt *find_format_by_pix(u32 pixelformat)
+ {
+ unsigned int i;
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -36,5 +36,6 @@ extern const struct v4l2_mbus_framefmt c
+ extern const struct v4l2_mbus_framefmt cfe_default_meta_format;
+
+ const struct cfe_fmt *find_format_by_code(u32 code);
++const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
+
+ #endif
--- /dev/null
+From cbed711f05a228d0f8f54b1b01f43d4d6489eccc Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 16:24:14 +0300
+Subject: [PATCH] media: rp1: cfe: Add missing remaps
+
+8-bit bayer formats are missing remap definitions. Add them.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -85,24 +85,28 @@ static const struct cfe_fmt formats[] =
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
++ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG8,
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
++ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG8,
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
++ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB8,
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .depth = 8,
+ .csi_dt = 0x2a,
++ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SBGGR10P,
--- /dev/null
+From 93c40564b94367c6ce072d66479af58afa3f08e0 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 17:14:31 +0300
+Subject: [PATCH] media: rp1: cfe: Add missing compressed remaps
+
+16-bit bayer formats are missing compressed remap definitions. Add them.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe_fmts.h
+@@ -197,24 +197,28 @@ static const struct cfe_fmt formats[] =
+ .code = MEDIA_BUS_FMT_SBGGR16_1X16,
+ .depth = 16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ .remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGBRG16,
+ .code = MEDIA_BUS_FMT_SGBRG16_1X16,
+ .depth = 16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ .remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SGRBG16,
+ .code = MEDIA_BUS_FMT_SGRBG16_1X16,
+ .depth = 16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ .remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
+ },
+ {
+ .fourcc = V4L2_PIX_FMT_SRGGB16,
+ .code = MEDIA_BUS_FMT_SRGGB16_1X16,
+ .depth = 16,
+ .flags = CFE_FORMAT_FLAG_FE_OUT,
++ .remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
+ },
+ /* PiSP Compressed Mode 1 */
+ {
--- /dev/null
+From 2e0e1d7b493dffe7baa763d499e51ba42f0bad19 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 17:14:11 +0300
+Subject: [PATCH] media: rp1: cfe: Add cfe_find_16bit_code() and
+ cfe_find_compressed_code()
+
+Add helper functions which, given an mbus code, return the 16-bit
+remapped mbus code or the compressed mbus code.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 40 +++++++++++++++++++
+ .../media/platform/raspberrypi/rp1_cfe/cfe.h | 2 +
+ 2 files changed, 42 insertions(+)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -473,6 +473,46 @@ const struct cfe_fmt *find_format_by_pix
+ return NULL;
+ }
+
++/*
++ * Given the mbus code, find the 16 bit remapped code. Returns 0 if no remap
++ * possible.
++ */
++u32 cfe_find_16bit_code(u32 code)
++{
++ const struct cfe_fmt *cfe_fmt;
++
++ cfe_fmt = find_format_by_code(code);
++
++ if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_16BIT])
++ return 0;
++
++ cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_16BIT]);
++ if (!cfe_fmt)
++ return 0;
++
++ return cfe_fmt->code;
++}
++
++/*
++ * Given the mbus code, find the 8 bit compressed code. Returns 0 if no remap
++ * possible.
++ */
++u32 cfe_find_compressed_code(u32 code)
++{
++ const struct cfe_fmt *cfe_fmt;
++
++ cfe_fmt = find_format_by_code(code);
++
++ if (!cfe_fmt || !cfe_fmt->remap[CFE_REMAP_COMPRESSED])
++ return 0;
++
++ cfe_fmt = find_format_by_pix(cfe_fmt->remap[CFE_REMAP_COMPRESSED]);
++ if (!cfe_fmt)
++ return 0;
++
++ return cfe_fmt->code;
++}
++
+ static int cfe_calc_format_size_bpl(struct cfe_device *cfe,
+ const struct cfe_fmt *fmt,
+ struct v4l2_format *f)
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.h
+@@ -37,5 +37,7 @@ extern const struct v4l2_mbus_framefmt c
+
+ const struct cfe_fmt *find_format_by_code(u32 code);
+ const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
++u32 cfe_find_16bit_code(u32 code);
++u32 cfe_find_compressed_code(u32 code);
+
+ #endif
--- /dev/null
+From eaa8a0ae14a1ca797c1896e9dafbefa1fa51a617 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 16:25:10 +0300
+Subject: [PATCH] media: rp1: csi2: Fix csi2_pad_set_fmt()
+
+The CSI-2 subdev's set_fmt currently allows setting the source and sink
+pad formats quite freely. This is not right, as the CSI-2 block can only
+do one of the following when processing the stream: 1) pass through as
+is, 2) expand to 16-bits, 3) compress.
+
+The csi2_pad_set_fmt() should take this into account, and only allow
+changing the source side mbus code, compared to the sink side format.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 61 +++++++++++++++----
+ 1 file changed, 48 insertions(+), 13 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -438,25 +438,60 @@ static int csi2_pad_set_fmt(struct v4l2_
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_format *format)
+ {
+- struct v4l2_mbus_framefmt *fmt;
+- const struct cfe_fmt *cfe_fmt;
+-
+- /* TODO: format validation */
++ if (format->pad < CSI2_NUM_CHANNELS) {
++ /*
++ * Store the sink pad format and propagate it to the source pad.
++ */
++
++ struct v4l2_mbus_framefmt *fmt;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++ if (!fmt)
++ return -EINVAL;
+
+- cfe_fmt = find_format_by_code(format->format.code);
+- if (!cfe_fmt)
+- cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SBGGR10_1X10);
++ *fmt = format->format;
+
+- format->format.code = cfe_fmt->code;
++ fmt = v4l2_subdev_get_pad_format(sd, state,
++ format->pad + CSI2_NUM_CHANNELS);
++ if (!fmt)
++ return -EINVAL;
+
+- fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
+- *fmt = format->format;
++ format->format.field = V4L2_FIELD_NONE;
+
+- if (format->pad < CSI2_NUM_CHANNELS) {
+- /* Propagate to the source pad */
+- fmt = v4l2_subdev_get_pad_format(sd, state,
+- format->pad + CSI2_NUM_CHANNELS);
+ *fmt = format->format;
++ } else {
++ /*
++ * Only allow changing the source pad mbus code.
++ */
++
++ struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
++ u32 sink_code;
++ u32 code;
++
++ sink_fmt = v4l2_subdev_get_pad_format(sd, state,
++ format->pad - CSI2_NUM_CHANNELS);
++ if (!sink_fmt)
++ return -EINVAL;
++
++ source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++ if (!source_fmt)
++ return -EINVAL;
++
++ sink_code = sink_fmt->code;
++ code = format->format.code;
++
++ /*
++ * If the source code from the user does not match the code in
++ * the sink pad, check that the source code matches either the
++ * 16-bit version or the compressed version of the sink code.
++ */
++
++ if (code != sink_code &&
++ (code == cfe_find_16bit_code(sink_code) ||
++ code == cfe_find_compressed_code(sink_code)))
++ source_fmt->code = code;
++
++ format->format.code = source_fmt->code;
+ }
+
+ return 0;
--- /dev/null
+From 214e8134842a338215831f2efa6d730f413c5ec4 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 29 Sep 2023 17:15:20 +0300
+Subject: [PATCH] media: rp1: fe: Fix pisp_fe_pad_set_fmt()
+
+pisp_fe_pad_set_fmt() allows setting the pad formats quite freely. This
+is not correct, and the function should only allow formats as supported
+by the hardware. Fix this by:
+
+Allow no format changes for FE_CONFIG_PAD and FE_STATS_PAD. They should
+always be the hardcoded initial ones.
+
+Allow setting FE_STREAM_PAD freely (but the mbus code must be
+supported), and propagate the format to the FE_OUTPUT0_PAD and
+FE_OUTPUT1_PAD pads.
+
+Allow changing the mbus code for FE_OUTPUT0_PAD and FE_OUTPUT1_PAD pads
+only if the mbus code is the compressed version of the sink side code.
+
+TODO: FE supports scaling and cropping. This should be represented here
+too?
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 59 +++++++++++++++----
+ 1 file changed, 48 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -433,26 +433,63 @@ static int pisp_fe_pad_set_fmt(struct v4
+
+ switch (format->pad) {
+ case FE_STREAM_PAD:
+- case FE_OUTPUT0_PAD:
+- case FE_OUTPUT1_PAD:
+ cfe_fmt = find_format_by_code(format->format.code);
+ if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
+ cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
+
+ format->format.code = cfe_fmt->code;
++ format->format.field = V4L2_FIELD_NONE;
+
+- break;
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD);
++ *fmt = format->format;
+
+- case FE_STATS_PAD:
+- case FE_CONFIG_PAD:
+- format->format.code = MEDIA_BUS_FMT_FIXED;
+- break;
+- }
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT0_PAD);
++ *fmt = format->format;
++
++ fmt = v4l2_subdev_get_pad_format(sd, state, FE_OUTPUT1_PAD);
++ *fmt = format->format;
++
++ return 0;
+
+- fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
+- *fmt = format->format;
++ case FE_OUTPUT0_PAD:
++ case FE_OUTPUT1_PAD: {
++ /*
++ * TODO: we should allow scaling and cropping by allowing the
++ * user to set the size here.
++ */
++ struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
++ u32 sink_code;
++ u32 code;
++
++ sink_fmt = v4l2_subdev_get_pad_format(sd, state, FE_STREAM_PAD);
++ if (!sink_fmt)
++ return -EINVAL;
++
++ source_fmt = v4l2_subdev_get_pad_format(sd, state, format->pad);
++ if (!source_fmt)
++ return -EINVAL;
++
++ sink_code = sink_fmt->code;
++ code = format->format.code;
++
++ /*
++ * If the source code from the user does not match the code in
++ * the sink pad, check that the source code matches the
++ * compressed version of the sink code.
++ */
++
++ if (code != sink_code &&
++ code == cfe_find_compressed_code(sink_code))
++ source_fmt->code = code;
++
++ return 0;
++ }
+
+- return 0;
++ case FE_CONFIG_PAD:
++ case FE_STATS_PAD:
++ default:
++ return v4l2_subdev_get_fmt(sd, state, format);
++ }
+ }
+
+ static int pisp_fe_link_validate(struct v4l2_subdev *sd,
--- /dev/null
+From 425a6b752c38b50c97220db37a67b18b281f56e5 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Thu, 21 Sep 2023 15:28:20 +0300
+Subject: [PATCH] media: rp1: csi2: Use get_frame_desc to get CSI-2 VC and DT
+
+Use get_frame_desc pad op for asking the CSI-2 VC and DT from the source
+device driver, instead of hardcoding to VC 0, and getting the DT from a
+formats table. To keep backward compatibility with sources that do not
+implement get_frame_desc, implement a fallback mechanism that always
+uses VC 0, and gets the DT from the formats table, based on the CSI2's
+sink pad's format.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 4 +-
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 75 ++++++++++++++++++-
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h | 2 +-
+ 3 files changed, 77 insertions(+), 4 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -838,7 +838,7 @@ static void cfe_start_channel(struct cfe
+ * this is handled by the CSI2 AUTO_ARM mode.
+ */
+ csi2_start_channel(&cfe->csi2, cfe->fe_csi2_channel,
+- fmt->csi_dt, CSI2_MODE_FE_STREAMING,
++ CSI2_MODE_FE_STREAMING,
+ true, false, width, height);
+ csi2_set_buffer(&cfe->csi2, cfe->fe_csi2_channel, 0, 0, -1);
+ pisp_fe_start(&cfe->fe);
+@@ -872,7 +872,7 @@ static void cfe_start_channel(struct cfe
+ }
+ }
+ /* Unconditionally start this CSI2 channel. */
+- csi2_start_channel(&cfe->csi2, node->id, fmt->csi_dt,
++ csi2_start_channel(&cfe->csi2, node->id,
+ mode,
+ /* Auto arm */
+ false,
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -324,12 +324,84 @@ void csi2_set_compression(struct csi2_de
+ csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
+ }
+
++static int csi2_get_vc_dt_fallback(struct csi2_device *csi2,
++ unsigned int channel, u8 *vc, u8 *dt)
++{
++ struct v4l2_subdev *sd = &csi2->sd;
++ struct v4l2_subdev_state *state;
++ struct v4l2_mbus_framefmt *fmt;
++ const struct cfe_fmt *cfe_fmt;
++
++ state = v4l2_subdev_get_locked_active_state(sd);
++
++ /* Without Streams API, the channel number matches the sink pad */
++ fmt = v4l2_subdev_get_pad_format(sd, state, channel);
++ if (!fmt)
++ return -EINVAL;
++
++ cfe_fmt = find_format_by_code(fmt->code);
++ if (!cfe_fmt)
++ return -EINVAL;
++
++ *vc = 0;
++ *dt = cfe_fmt->csi_dt;
++
++ return 0;
++}
++
++static int csi2_get_vc_dt(struct csi2_device *csi2, unsigned int channel,
++ u8 *vc, u8 *dt)
++{
++ struct v4l2_mbus_frame_desc remote_desc;
++ const struct media_pad *remote_pad;
++ struct v4l2_subdev *source_sd;
++ int ret;
++
++ /* Without Streams API, the channel number matches the sink pad */
++ remote_pad = media_pad_remote_pad_first(&csi2->pad[channel]);
++ if (!remote_pad)
++ return -EPIPE;
++
++ source_sd = media_entity_to_v4l2_subdev(remote_pad->entity);
++
++ ret = v4l2_subdev_call(source_sd, pad, get_frame_desc,
++ remote_pad->index, &remote_desc);
++ if (ret == -ENOIOCTLCMD) {
++ csi2_dbg("source does not support get_frame_desc, use fallback\n");
++ return csi2_get_vc_dt_fallback(csi2, channel, vc, dt);
++ } else if (ret) {
++ csi2_err("Failed to get frame descriptor\n");
++ return ret;
++ }
++
++ if (remote_desc.type != V4L2_MBUS_FRAME_DESC_TYPE_CSI2) {
++ csi2_err("Frame descriptor does not describe CSI-2 link");
++ return -EINVAL;
++ }
++
++ if (remote_desc.num_entries != 1) {
++ csi2_err("Frame descriptor does not have a single entry");
++ return -EINVAL;
++ }
++
++ *vc = remote_desc.entry[0].bus.csi2.vc;
++ *dt = remote_desc.entry[0].bus.csi2.dt;
++
++ return 0;
++}
++
+ void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
+- u16 dt, enum csi2_mode mode, bool auto_arm,
++ enum csi2_mode mode, bool auto_arm,
+ bool pack_bytes, unsigned int width,
+ unsigned int height)
+ {
+ u32 ctrl;
++ int ret;
++ u8 vc, dt;
++
++ ret = csi2_get_vc_dt(csi2, channel, &vc, &dt);
++ if (ret)
++ return;
+
+ csi2_dbg("%s [%u]\n", __func__, channel);
+
+@@ -369,6 +441,7 @@ void csi2_start_channel(struct csi2_devi
+ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+ }
+
++ set_field(&ctrl, vc, VC_MASK);
+ set_field(&ctrl, dt, DT_MASK);
+ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
+ csi2->num_lines[channel] = height;
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -79,7 +79,7 @@ void csi2_set_compression(struct csi2_de
+ enum csi2_compression_mode mode, unsigned int shift,
+ unsigned int offset);
+ void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
+- u16 dt, enum csi2_mode mode, bool auto_arm,
++ enum csi2_mode mode, bool auto_arm,
+ bool pack_bytes, unsigned int width,
+ unsigned int height);
+ void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel);
--- /dev/null
+From 2b6570e66f2769110311593f52f88dba3271a278 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Fri, 22 Sep 2023 13:47:10 +0300
+Subject: [PATCH] media: rp1: cfe: Add is_image_node()
+
+The hardware supports streaming from memory (in addition to streaming
+from the CSI-2 RX), but the driver does not support this at the moment.
+
+There are multiple places in the driver which uses
+is_image_output_node(), even if the "output" part is not relevant. Thus,
+in a minor preparation for the possible support for streaming from
+memory, and to make it more obvious that the pieces of code are not
+about the "output", add is_image_node() which will return true for both
+input and output video nodes.
+
+While at it, reformat also the metadata related macros to fit inside 80
+columns.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 28 +++++++++++--------
+ 1 file changed, 17 insertions(+), 11 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -199,13 +199,20 @@ static const struct node_description nod
+
+ #define is_fe_node(node) (((node)->id) >= FE_OUT0)
+ #define is_csi2_node(node) (!is_fe_node(node))
+-#define is_image_output_node(node) \
++
++#define is_image_output_node(node) \
+ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+-#define is_meta_output_node(node) \
++#define is_image_input_node(node) \
++ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
++#define is_image_node(node) \
++ (is_image_output_node(node) || is_image_input_node(node))
++
++#define is_meta_output_node(node) \
+ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_CAPTURE)
+-#define is_meta_input_node(node) \
++#define is_meta_input_node(node) \
+ (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_OUTPUT)
+-#define is_meta_node(node) (is_meta_output_node(node) || is_meta_input_node(node))
++#define is_meta_node(node) \
++ (is_meta_output_node(node) || is_meta_input_node(node))
+
+ /* To track state across all nodes. */
+ #define NUM_STATES 5
+@@ -426,7 +433,7 @@ static int format_show(struct seq_file *
+ seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i,
+ node_desc[i].name, state);
+
+- if (is_image_output_node(node))
++ if (is_image_node(node))
+ seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\n"
+ "resolution: %ux%u\nbpl: %u\nsize: %u\n",
+ V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat),
+@@ -940,9 +947,8 @@ static int cfe_queue_setup(struct vb2_qu
+ {
+ struct cfe_node *node = vb2_get_drv_priv(vq);
+ struct cfe_device *cfe = node->cfe;
+- unsigned int size = is_image_output_node(node) ?
+- node->fmt.fmt.pix.sizeimage :
+- node->fmt.fmt.meta.buffersize;
++ unsigned int size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
++ node->fmt.fmt.meta.buffersize;
+
+ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+
+@@ -973,8 +979,8 @@ static int cfe_buffer_prepare(struct vb2
+ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
+ node_desc[node->id].name, vb);
+
+- size = is_image_output_node(node) ? node->fmt.fmt.pix.sizeimage :
+- node->fmt.fmt.meta.buffersize;
++ size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
++ node->fmt.fmt.meta.buffersize;
+ if (vb2_plane_size(vb, 0) < size) {
+ cfe_err("data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+@@ -1757,7 +1763,7 @@ static int cfe_register_node(struct cfe_
+ node->cfe = cfe;
+ node->id = id;
+
+- if (is_image_output_node(node)) {
++ if (is_image_node(node)) {
+ fmt = find_format_by_code(cfe_default_format.code);
+ if (!fmt) {
+ cfe_err("Failed to find format code\n");
--- /dev/null
+From c54b8d2fc79c684deacc81a94f6baa1cb56c62be Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Wed, 27 Sep 2023 17:18:09 +0300
+Subject: [PATCH] media: rp1: cfe: Dual purpose video nodes
+
+The RP1 CSI-2 DMA can capture both video and metadata just fine, but at
+the moment the video nodes are only set to support either video or
+metadata.
+
+Make the changes to support both video and metadata. This mostly means
+tracking both video format and metadata format separately for each video
+node, and using vb2_queue_change_type() to change the vb2 queue type
+when needed.
+
+Briefly, this means that the user can get/set both video and meta
+formats to a single video node. The vb2 queue buffer type will be
+changed when the user calls REQBUFS or CREATE_BUFS ioctls. This buffer
+type will be then used as the "mode" for the video node when the user
+starts the streaming, and based on that either the video or the meta
+format will be used.
+
+A bunch of macros are added (node_supports_xxx()), which tell if a node
+can support a particular mode, whereas the existing macros
+(is_xxx_node()) will tell if the node is currently in a particular mode.
+Note that the latter will only work correctly between the start of the
+streaming and the end of the streaming, and thus should be only used in
+those code paths.
+
+However, as the userspace (libcamera) does not support dual purpose
+video nodes, for the time being let's keep the second video node as
+V4L2_CAP_META_CAPTURE only to keep the userspace working.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 271 ++++++++++++------
+ 1 file changed, 182 insertions(+), 89 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -116,7 +116,7 @@ const struct v4l2_mbus_framefmt cfe_defa
+ enum node_ids {
+ /* CSI2 HW output nodes first. */
+ CSI2_CH0,
+- CSI2_CH1_EMBEDDED,
++ CSI2_CH1,
+ CSI2_CH2,
+ CSI2_CH3,
+ /* FE only nodes from here on. */
+@@ -130,8 +130,7 @@ enum node_ids {
+ struct node_description {
+ unsigned int id;
+ const char *name;
+- enum v4l2_buf_type buf_type;
+- unsigned int cap;
++ unsigned int caps;
+ unsigned int pad_flags;
+ unsigned int link_pad;
+ };
+@@ -140,58 +139,55 @@ struct node_description {
+ static const struct node_description node_desc[NUM_NODES] = {
+ [CSI2_CH0] = {
+ .name = "csi2_ch0",
+- .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+- .cap = V4L2_CAP_VIDEO_CAPTURE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_NUM_CHANNELS + 0
+ },
+- /* This node is assigned for the embedded data channel! */
+- [CSI2_CH1_EMBEDDED] = {
++ /*
++ * TODO: This node should be named "csi2_ch1" and the caps should be set
++ * to both video and meta capture. However, to keep compatibility with
++ * the current libcamera, keep the name as "embedded" and support
++ * only meta capture.
++ */
++ [CSI2_CH1] = {
+ .name = "embedded",
+- .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+- .cap = V4L2_CAP_META_CAPTURE,
++ .caps = V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_NUM_CHANNELS + 1
+ },
+ [CSI2_CH2] = {
+ .name = "csi2_ch2",
+- .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+- .cap = V4L2_CAP_META_CAPTURE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_NUM_CHANNELS + 2
+ },
+ [CSI2_CH3] = {
+ .name = "csi2_ch3",
+- .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+- .cap = V4L2_CAP_META_CAPTURE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = CSI2_NUM_CHANNELS + 3
+ },
+ [FE_OUT0] = {
+ .name = "fe_image0",
+- .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+- .cap = V4L2_CAP_VIDEO_CAPTURE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_OUTPUT0_PAD
+ },
+ [FE_OUT1] = {
+ .name = "fe_image1",
+- .buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+- .cap = V4L2_CAP_VIDEO_CAPTURE,
++ .caps = V4L2_CAP_VIDEO_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_OUTPUT1_PAD
+ },
+ [FE_STATS] = {
+ .name = "fe_stats",
+- .buf_type = V4L2_BUF_TYPE_META_CAPTURE,
+- .cap = V4L2_CAP_META_CAPTURE,
++ .caps = V4L2_CAP_META_CAPTURE,
+ .pad_flags = MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_STATS_PAD
+ },
+ [FE_CONFIG] = {
+ .name = "fe_config",
+- .buf_type = V4L2_BUF_TYPE_META_OUTPUT,
+- .cap = V4L2_CAP_META_OUTPUT,
++ .caps = V4L2_CAP_META_OUTPUT,
+ .pad_flags = MEDIA_PAD_FL_SOURCE | MEDIA_PAD_FL_MUST_CONNECT,
+ .link_pad = FE_CONFIG_PAD
+ },
+@@ -200,17 +196,29 @@ static const struct node_description nod
+ #define is_fe_node(node) (((node)->id) >= FE_OUT0)
+ #define is_csi2_node(node) (!is_fe_node(node))
+
++#define node_supports_image_output(node) \
++ (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_CAPTURE))
++#define node_supports_meta_output(node) \
++ (!!(node_desc[(node)->id].caps & V4L2_CAP_META_CAPTURE))
++#define node_supports_image_input(node) \
++ (!!(node_desc[(node)->id].caps & V4L2_CAP_VIDEO_OUTPUT))
++#define node_supports_meta_input(node) \
++ (!!(node_desc[(node)->id].caps & V4L2_CAP_META_OUTPUT))
++#define node_supports_image(node) \
++ (node_supports_image_output(node) || node_supports_image_input(node))
++#define node_supports_meta(node) \
++ (node_supports_meta_output(node) || node_supports_meta_input(node))
++
+ #define is_image_output_node(node) \
+- (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ #define is_image_input_node(node) \
+- (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
++ ((node)->buffer_queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
+ #define is_image_node(node) \
+ (is_image_output_node(node) || is_image_input_node(node))
+-
+ #define is_meta_output_node(node) \
+- (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_CAPTURE)
++ ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_CAPTURE)
+ #define is_meta_input_node(node) \
+- (node_desc[(node)->id].buf_type == V4L2_BUF_TYPE_META_OUTPUT)
++ ((node)->buffer_queue.type == V4L2_BUF_TYPE_META_OUTPUT)
+ #define is_meta_node(node) \
+ (is_meta_output_node(node) || is_meta_input_node(node))
+
+@@ -250,7 +258,9 @@ struct cfe_node {
+ /* Pointer pointing to next v4l2_buffer */
+ struct cfe_buffer *next_frm;
+ /* Used to store current pixel format */
+- struct v4l2_format fmt;
++ struct v4l2_format vid_fmt;
++ /* Used to store current meta format */
++ struct v4l2_format meta_fmt;
+ /* Buffer queue used in video-buf */
+ struct vb2_queue buffer_queue;
+ /* Queue of filled frames */
+@@ -433,20 +443,21 @@ static int format_show(struct seq_file *
+ seq_printf(s, "\nNode %u (%s) state: 0x%lx\n", i,
+ node_desc[i].name, state);
+
+- if (is_image_node(node))
++ if (node_supports_image(node))
+ seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\n"
+ "resolution: %ux%u\nbpl: %u\nsize: %u\n",
+- V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat),
+- node->fmt.fmt.pix.pixelformat,
+- node->fmt.fmt.pix.width,
+- node->fmt.fmt.pix.height,
+- node->fmt.fmt.pix.bytesperline,
+- node->fmt.fmt.pix.sizeimage);
+- else
++ V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat),
++ node->vid_fmt.fmt.pix.pixelformat,
++ node->vid_fmt.fmt.pix.width,
++ node->vid_fmt.fmt.pix.height,
++ node->vid_fmt.fmt.pix.bytesperline,
++ node->vid_fmt.fmt.pix.sizeimage);
++
++ if (node_supports_meta(node))
+ seq_printf(s, "format: " V4L2_FOURCC_CONV " 0x%x\nsize: %u\n",
+- V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat),
+- node->fmt.fmt.meta.dataformat,
+- node->fmt.fmt.meta.buffersize);
++ V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat),
++ node->meta_fmt.fmt.meta.dataformat,
++ node->meta_fmt.fmt.meta.buffersize);
+ }
+
+ return 0;
+@@ -571,11 +582,11 @@ static void cfe_schedule_next_csi2_job(s
+ node_desc[node->id].name, &buf->vb.vb2_buf);
+
+ if (is_meta_node(node)) {
+- size = node->fmt.fmt.meta.buffersize;
++ size = node->meta_fmt.fmt.meta.buffersize;
+ stride = 0;
+ } else {
+- size = node->fmt.fmt.pix.sizeimage;
+- stride = node->fmt.fmt.pix.bytesperline;
++ size = node->vid_fmt.fmt.pix.sizeimage;
++ stride = node->vid_fmt.fmt.pix.bytesperline;
+ }
+
+ addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0);
+@@ -867,10 +878,10 @@ static void cfe_start_channel(struct cfe
+ width = source_fmt->width;
+ height = source_fmt->height;
+
+- if (node->fmt.fmt.pix.pixelformat ==
++ if (node->vid_fmt.fmt.pix.pixelformat ==
+ fmt->remap[CFE_REMAP_16BIT])
+ mode = CSI2_MODE_REMAP;
+- else if (node->fmt.fmt.pix.pixelformat ==
++ else if (node->vid_fmt.fmt.pix.pixelformat ==
+ fmt->remap[CFE_REMAP_COMPRESSED]) {
+ mode = CSI2_MODE_COMPRESSED;
+ csi2_set_compression(&cfe->csi2, node->id,
+@@ -884,7 +895,7 @@ static void cfe_start_channel(struct cfe
+ /* Auto arm */
+ false,
+ /* Pack bytes */
+- node->id == CSI2_CH1_EMBEDDED ? true : false,
++ is_meta_node(node) ? true : false,
+ width, height);
+ }
+
+@@ -947,10 +958,11 @@ static int cfe_queue_setup(struct vb2_qu
+ {
+ struct cfe_node *node = vb2_get_drv_priv(vq);
+ struct cfe_device *cfe = node->cfe;
+- unsigned int size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
+- node->fmt.fmt.meta.buffersize;
++ unsigned int size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage :
++ node->meta_fmt.fmt.meta.buffersize;
+
+- cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++ cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
++ node->buffer_queue.type);
+
+ if (vq->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - vq->num_buffers;
+@@ -979,8 +991,8 @@ static int cfe_buffer_prepare(struct vb2
+ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
+ node_desc[node->id].name, vb);
+
+- size = is_image_node(node) ? node->fmt.fmt.pix.sizeimage :
+- node->fmt.fmt.meta.buffersize;
++ size = is_image_node(node) ? node->vid_fmt.fmt.pix.sizeimage :
++ node->meta_fmt.fmt.meta.buffersize;
+ if (vb2_plane_size(vb, 0) < size) {
+ cfe_err("data will not fit into plane (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+@@ -995,8 +1007,8 @@ static int cfe_buffer_prepare(struct vb2
+
+ memcpy(&b->config, addr, sizeof(struct pisp_fe_config));
+ return pisp_fe_validate_config(&cfe->fe, &b->config,
+- &cfe->node[FE_OUT0].fmt,
+- &cfe->node[FE_OUT1].fmt);
++ &cfe->node[FE_OUT0].vid_fmt,
++ &cfe->node[FE_OUT1].vid_fmt);
+ }
+
+ return 0;
+@@ -1256,7 +1268,7 @@ static int cfe_enum_fmt_vid_cap(struct f
+ struct cfe_device *cfe = node->cfe;
+ unsigned int i, j;
+
+- if (!is_image_output_node(node))
++ if (!node_supports_image_output(node))
+ return -EINVAL;
+
+ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+@@ -1292,10 +1304,10 @@ static int cfe_g_fmt(struct file *file,
+
+ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+
+- if (f->type != node->buffer_queue.type)
++ if (!node_supports_image(node))
+ return -EINVAL;
+
+- *f = node->fmt;
++ *f = node->vid_fmt;
+
+ return 0;
+ }
+@@ -1310,7 +1322,7 @@ static int try_fmt_vid_cap(struct cfe_no
+ f->fmt.pix.width, f->fmt.pix.height,
+ V4L2_FOURCC_CONV_ARGS(f->fmt.pix.pixelformat));
+
+- if (!is_image_output_node(node))
++ if (!node_supports_image_output(node))
+ return -EINVAL;
+
+ /*
+@@ -1351,11 +1363,11 @@ static int cfe_s_fmt_vid_cap(struct file
+ if (ret)
+ return ret;
+
+- node->fmt = *f;
++ node->vid_fmt = *f;
+
+ cfe_dbg("%s: Set %ux%u, V4L2 pix " V4L2_FOURCC_CONV "\n", __func__,
+- node->fmt.fmt.pix.width, node->fmt.fmt.pix.height,
+- V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.pix.pixelformat));
++ node->vid_fmt.fmt.pix.width, node->vid_fmt.fmt.pix.height,
++ V4L2_FOURCC_CONV_ARGS(node->vid_fmt.fmt.pix.pixelformat));
+
+ return 0;
+ }
+@@ -1379,11 +1391,11 @@ static int cfe_enum_fmt_meta(struct file
+
+ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
+
+- if (!is_meta_node(node) || f->index != 0)
++ if (!node_supports_meta(node) || f->index != 0)
+ return -EINVAL;
+
+ switch (node->id) {
+- case CSI2_CH1_EMBEDDED:
++ case CSI2_CH0...CSI2_CH3:
+ f->pixelformat = V4L2_META_FMT_SENSOR_DATA;
+ return 0;
+ case FE_STATS:
+@@ -1399,8 +1411,11 @@ static int cfe_enum_fmt_meta(struct file
+
+ static int try_fmt_meta(struct cfe_node *node, struct v4l2_format *f)
+ {
++ if (!node_supports_meta(node))
++ return -EINVAL;
++
+ switch (node->id) {
+- case CSI2_CH1_EMBEDDED:
++ case CSI2_CH0...CSI2_CH3:
+ f->fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA;
+ if (!f->fmt.meta.buffersize)
+ f->fmt.meta.buffersize = DEFAULT_EMBEDDED_SIZE;
+@@ -1422,6 +1437,21 @@ static int try_fmt_meta(struct cfe_node
+ return -EINVAL;
+ }
+
++static int cfe_g_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
++{
++ struct cfe_node *node = video_drvdata(file);
++ struct cfe_device *cfe = node->cfe;
++
++ cfe_dbg("%s: [%s]\n", __func__, node_desc[node->id].name);
++
++ if (!node_supports_meta(node))
++ return -EINVAL;
++
++ *f = node->meta_fmt;
++
++ return 0;
++}
++
+ static int cfe_s_fmt_meta(struct file *file, void *priv, struct v4l2_format *f)
+ {
+ struct cfe_node *node = video_drvdata(file);
+@@ -1434,17 +1464,17 @@ static int cfe_s_fmt_meta(struct file *f
+ if (vb2_is_busy(q))
+ return -EBUSY;
+
+- if (f->type != node->buffer_queue.type)
++ if (!node_supports_meta(node))
+ return -EINVAL;
+
+ ret = try_fmt_meta(node, f);
+ if (ret)
+ return ret;
+
+- node->fmt = *f;
++ node->meta_fmt = *f;
+
+ cfe_dbg("%s: Set " V4L2_FOURCC_CONV "\n", __func__,
+- V4L2_FOURCC_CONV_ARGS(node->fmt.fmt.meta.dataformat));
++ V4L2_FOURCC_CONV_ARGS(node->meta_fmt.fmt.meta.dataformat));
+
+ return 0;
+ }
+@@ -1491,6 +1521,52 @@ static int cfe_enum_framesizes(struct fi
+ return 0;
+ }
+
++static int cfe_vb2_ioctl_reqbufs(struct file *file, void *priv,
++ struct v4l2_requestbuffers *p)
++{
++ struct video_device *vdev = video_devdata(file);
++ struct cfe_node *node = video_get_drvdata(vdev);
++ struct cfe_device *cfe = node->cfe;
++ int ret;
++
++ cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
++ p->type);
++
++ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
++ p->type != V4L2_BUF_TYPE_META_CAPTURE &&
++ p->type != V4L2_BUF_TYPE_META_OUTPUT)
++ return -EINVAL;
++
++ ret = vb2_queue_change_type(vdev->queue, p->type);
++ if (ret)
++ return ret;
++
++ return vb2_ioctl_reqbufs(file, priv, p);
++}
++
++static int cfe_vb2_ioctl_create_bufs(struct file *file, void *priv,
++ struct v4l2_create_buffers *p)
++{
++ struct video_device *vdev = video_devdata(file);
++ struct cfe_node *node = video_get_drvdata(vdev);
++ struct cfe_device *cfe = node->cfe;
++ int ret;
++
++ cfe_dbg("%s: [%s] type:%u\n", __func__, node_desc[node->id].name,
++ p->format.type);
++
++ if (p->format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
++ p->format.type != V4L2_BUF_TYPE_META_CAPTURE &&
++ p->format.type != V4L2_BUF_TYPE_META_OUTPUT)
++ return -EINVAL;
++
++ ret = vb2_queue_change_type(vdev->queue, p->format.type);
++ if (ret)
++ return ret;
++
++ return vb2_ioctl_create_bufs(file, priv, p);
++}
++
+ static int cfe_subscribe_event(struct v4l2_fh *fh,
+ const struct v4l2_event_subscription *sub)
+ {
+@@ -1498,12 +1574,13 @@ static int cfe_subscribe_event(struct v4
+
+ switch (sub->type) {
+ case V4L2_EVENT_FRAME_SYNC:
+- if (!is_image_output_node(node))
++ if (!node_supports_image_output(node))
+ break;
+
+ return v4l2_event_subscribe(fh, sub, 2, NULL);
+ case V4L2_EVENT_SOURCE_CHANGE:
+- if (is_meta_input_node(node))
++ if (!node_supports_image_output(node) &&
++ !node_supports_meta_output(node))
+ break;
+
+ return v4l2_event_subscribe(fh, sub, 4, NULL);
+@@ -1520,19 +1597,19 @@ static const struct v4l2_ioctl_ops cfe_i
+ .vidioc_try_fmt_vid_cap = cfe_try_fmt_vid_cap,
+
+ .vidioc_enum_fmt_meta_cap = cfe_enum_fmt_meta,
+- .vidioc_g_fmt_meta_cap = cfe_g_fmt,
++ .vidioc_g_fmt_meta_cap = cfe_g_fmt_meta,
+ .vidioc_s_fmt_meta_cap = cfe_s_fmt_meta,
+ .vidioc_try_fmt_meta_cap = cfe_try_fmt_meta,
+
+ .vidioc_enum_fmt_meta_out = cfe_enum_fmt_meta,
+- .vidioc_g_fmt_meta_out = cfe_g_fmt,
++ .vidioc_g_fmt_meta_out = cfe_g_fmt_meta,
+ .vidioc_s_fmt_meta_out = cfe_s_fmt_meta,
+ .vidioc_try_fmt_meta_out = cfe_try_fmt_meta,
+
+ .vidioc_enum_framesizes = cfe_enum_framesizes,
+
+- .vidioc_reqbufs = vb2_ioctl_reqbufs,
+- .vidioc_create_bufs = vb2_ioctl_create_bufs,
++ .vidioc_reqbufs = cfe_vb2_ioctl_reqbufs,
++ .vidioc_create_bufs = cfe_vb2_ioctl_create_bufs,
+ .vidioc_prepare_buf = vb2_ioctl_prepare_buf,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+@@ -1610,7 +1687,7 @@ static int cfe_video_link_validate(struc
+ }
+
+ if (is_image_output_node(node)) {
+- struct v4l2_pix_format *pix_fmt = &node->fmt.fmt.pix;
++ struct v4l2_pix_format *pix_fmt = &node->vid_fmt.fmt.pix;
+ const struct cfe_fmt *fmt = NULL;
+ unsigned int i;
+
+@@ -1636,8 +1713,8 @@ static int cfe_video_link_validate(struc
+ ret = -EINVAL;
+ goto out;
+ }
+- } else if (node->id == CSI2_CH1_EMBEDDED) {
+- struct v4l2_meta_format *meta_fmt = &node->fmt.fmt.meta;
++ } else if (is_csi2_node(node) && is_meta_output_node(node)) {
++ struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta;
+
+ if (source_fmt->width * source_fmt->height !=
+ meta_fmt->buffersize ||
+@@ -1698,15 +1775,17 @@ static int cfe_video_link_notify(struct
+
+ if (link->source->entity != csi2)
+ return 0;
+- if (link->sink->index != 0)
++ if (link->sink->entity != fe)
+ return 0;
+- if (link->source->index == node_desc[CSI2_CH1_EMBEDDED].link_pad)
++ if (link->sink->index != 0)
+ return 0;
+
+ cfe->fe_csi2_channel = -1;
+- if (link->sink->entity == fe && (link->flags & MEDIA_LNK_FL_ENABLED)) {
++ if (link->flags & MEDIA_LNK_FL_ENABLED) {
+ if (link->source->index == node_desc[CSI2_CH0].link_pad)
+ cfe->fe_csi2_channel = CSI2_CH0;
++ else if (link->source->index == node_desc[CSI2_CH1].link_pad)
++ cfe->fe_csi2_channel = CSI2_CH1;
+ else if (link->source->index == node_desc[CSI2_CH2].link_pad)
+ cfe->fe_csi2_channel = CSI2_CH2;
+ else if (link->source->index == node_desc[CSI2_CH3].link_pad)
+@@ -1763,30 +1842,42 @@ static int cfe_register_node(struct cfe_
+ node->cfe = cfe;
+ node->id = id;
+
+- if (is_image_node(node)) {
++ if (node_supports_image(node)) {
++ if (node_supports_image_output(node))
++ node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ else
++ node->vid_fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++
+ fmt = find_format_by_code(cfe_default_format.code);
+ if (!fmt) {
+ cfe_err("Failed to find format code\n");
+ return -EINVAL;
+ }
+
+- node->fmt.fmt.pix.pixelformat = fmt->fourcc;
+- v4l2_fill_pix_format(&node->fmt.fmt.pix, &cfe_default_format);
++ node->vid_fmt.fmt.pix.pixelformat = fmt->fourcc;
++ v4l2_fill_pix_format(&node->vid_fmt.fmt.pix, &cfe_default_format);
+
+- ret = try_fmt_vid_cap(node, &node->fmt);
++ ret = try_fmt_vid_cap(node, &node->vid_fmt);
+ if (ret)
+ return ret;
+- } else {
+- ret = try_fmt_meta(node, &node->fmt);
++ }
++
++ if (node_supports_meta(node)) {
++ if (node_supports_meta_output(node))
++ node->meta_fmt.type = V4L2_BUF_TYPE_META_CAPTURE;
++ else
++ node->meta_fmt.type = V4L2_BUF_TYPE_META_OUTPUT;
++
++ ret = try_fmt_meta(node, &node->meta_fmt);
+ if (ret)
+ return ret;
+ }
+- node->fmt.type = node_desc[id].buf_type;
+
+ mutex_init(&node->lock);
+
+ q = &node->buffer_queue;
+- q->type = node_desc[id].buf_type;
++ q->type = node_supports_image(node) ? node->vid_fmt.type :
++ node->meta_fmt.type;
+ q->io_modes = VB2_MMAP | VB2_DMABUF;
+ q->drv_priv = node;
+ q->ops = &cfe_video_qops;
+@@ -1812,11 +1903,13 @@ static int cfe_register_node(struct cfe_
+ vdev->ioctl_ops = &cfe_ioctl_ops;
+ vdev->entity.ops = &cfe_media_entity_ops;
+ vdev->v4l2_dev = &cfe->v4l2_dev;
+- vdev->vfl_dir = (is_image_output_node(node) || is_meta_output_node(node))
+- ? VFL_DIR_RX : VFL_DIR_TX;
++ vdev->vfl_dir = (node_supports_image_output(node) ||
++ node_supports_meta_output(node)) ?
++ VFL_DIR_RX :
++ VFL_DIR_TX;
+ vdev->queue = q;
+ vdev->lock = &node->lock;
+- vdev->device_caps = node_desc[id].cap;
++ vdev->device_caps = node_desc[id].caps;
+ vdev->device_caps |= V4L2_CAP_STREAMING | V4L2_CAP_IO_MC;
+
+ /* Define the device names */
+@@ -1829,7 +1922,7 @@ static int cfe_register_node(struct cfe_
+ node->pad.flags = node_desc[id].pad_flags;
+ media_entity_pads_init(&vdev->entity, 1, &node->pad);
+
+- if (is_meta_node(node)) {
++ if (!node_supports_image(node)) {
+ v4l2_disable_ioctl(&node->video_dev,
+ VIDIOC_ENUM_FRAMEINTERVALS);
+ v4l2_disable_ioctl(&node->video_dev,
+@@ -1907,7 +2000,7 @@ static int cfe_link_node_pads(struct cfe
+ if (ret)
+ return ret;
+
+- if (node->id != CSI2_CH1_EMBEDDED) {
++ if (node_supports_image(node)) {
+ /* CSI2 channel # -> FE Input */
+ ret = media_create_pad_link(&cfe->csi2.sd.entity,
+ node_desc[i].link_pad,
--- /dev/null
+From dad296088dffbaf55c1e61cbdc3f7cb1eb504ca6 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 16 May 2023 15:51:54 +0300
+Subject: [PATCH] media: rp1: Drop LE handling
+
+The driver registers for line-end interrupts, but never uses them. This
+just causes extra interrupt load, with more complexity in the driver.
+
+Drop the LE handling. It can easily be added back if later needed.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 6 ++--
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 28 ++++---------------
+ .../media/platform/raspberrypi/rp1_cfe/csi2.h | 2 +-
+ 3 files changed, 10 insertions(+), 26 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -734,13 +734,13 @@ static irqreturn_t cfe_isr(int irq, void
+ {
+ struct cfe_device *cfe = dev;
+ unsigned int i;
+- bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0}, lci[NUM_NODES] = {0};
++ bool sof[NUM_NODES] = {0}, eof[NUM_NODES] = {0};
+ u32 sts;
+
+ sts = cfg_reg_read(cfe, MIPICFG_INTS);
+
+ if (sts & MIPICFG_INT_CSI_DMA)
+- csi2_isr(&cfe->csi2, sof, eof, lci);
++ csi2_isr(&cfe->csi2, sof, eof);
+
+ if (sts & MIPICFG_INT_PISP_FE)
+ pisp_fe_isr(&cfe->fe, sof + CSI2_NUM_CHANNELS,
+@@ -757,7 +757,7 @@ static irqreturn_t cfe_isr(int irq, void
+ * generate interrupts even though the node is not streaming.
+ */
+ if (!check_state(cfe, NODE_STREAMING, i) ||
+- !(sof[i] || eof[i] || lci[i]))
++ !(sof[i] || eof[i]))
+ continue;
+
+ /*
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -258,7 +258,7 @@ static void csi2_isr_handle_errors(struc
+ spin_unlock(&csi2->errors_lock);
+ }
+
+-void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci)
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof)
+ {
+ unsigned int i;
+ u32 status;
+@@ -290,7 +290,6 @@ void csi2_isr(struct csi2_device *csi2,
+
+ sof[i] = !!(status & IRQ_FS(i));
+ eof[i] = !!(status & IRQ_FE_ACK(i));
+- lci[i] = !!(status & IRQ_LE_ACK(i));
+ }
+
+ if (csi2_track_errors)
+@@ -405,16 +404,12 @@ void csi2_start_channel(struct csi2_devi
+
+ csi2_dbg("%s [%u]\n", __func__, channel);
+
+- /*
+- * Disable the channel, but ensure N != 0! Otherwise we end up with a
+- * spurious LE + LE_ACK interrupt when re-enabling the channel.
+- */
+- csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0x100 << __ffs(LC_MASK));
++ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0);
+ csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0);
+ csi2_reg_write(csi2, CSI2_STATUS, IRQ_CH_MASK(channel));
+
+- /* Enable channel and FS/FE/LE interrupts. */
+- ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | IRQ_EN_LE_ACK | PACK_LINE;
++ /* Enable channel and FS/FE interrupts. */
++ ctrl = DMA_EN | IRQ_EN_FS | IRQ_EN_FE_ACK | PACK_LINE;
+ /* PACK_BYTES ensures no striding for embedded data. */
+ if (pack_bytes)
+ ctrl |= PACK_BYTES;
+@@ -423,21 +418,11 @@ void csi2_start_channel(struct csi2_devi
+ ctrl |= AUTO_ARM;
+
+ if (width && height) {
+- int line_int_freq = height >> 2;
+-
+- line_int_freq = min(max(0x80, line_int_freq), 0x3ff);
+- set_field(&ctrl, line_int_freq, LC_MASK);
+ set_field(&ctrl, mode, CH_MODE_MASK);
+ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel),
+ (height << 16) | width);
+ } else {
+- /*
+- * Do not disable line interrupts for the embedded data channel,
+- * set it to the maximum value. This avoids spamming the ISR
+- * with spurious line interrupts.
+- */
+- set_field(&ctrl, 0x3ff, LC_MASK);
+- set_field(&ctrl, 0x00, CH_MODE_MASK);
++ set_field(&ctrl, 0x0, CH_MODE_MASK);
+ csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
+ }
+
+@@ -452,8 +437,7 @@ void csi2_stop_channel(struct csi2_devic
+ csi2_dbg("%s [%u]\n", __func__, channel);
+
+ /* Channel disable. Use FORCE to allow stopping mid-frame. */
+- csi2_reg_write(csi2, CSI2_CH_CTRL(channel),
+- (0x100 << __ffs(LC_MASK)) | FORCE);
++ csi2_reg_write(csi2, CSI2_CH_CTRL(channel), FORCE);
+ /* Latch the above change by writing to the ADDR0 register. */
+ csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
+ /* Write this again, the HW needs it! */
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.h
+@@ -71,7 +71,7 @@ struct csi2_device {
+ u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
+ };
+
+-void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof, bool *lci);
++void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof);
+ void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
+ dma_addr_t dmaaddr, unsigned int stride,
+ unsigned int size);
--- /dev/null
+From b6316a8450d3cb99b7599175d59b1b7f710770f5 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 3 Oct 2023 13:59:02 +0300
+Subject: [PATCH] media: rp1: csi2: Use standard link_validate
+
+The current csi2_link_validate() skips some important checks. Let's
+rather use the standard v4l2_subdev_link_validate_default() as the
+link_validate hook.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/csi2.c | 41 +------------------
+ 1 file changed, 1 insertion(+), 40 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/csi2.c
+@@ -462,11 +462,6 @@ void csi2_close_rx(struct csi2_device *c
+ csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
+ }
+
+-static struct csi2_device *to_csi2_device(struct v4l2_subdev *subdev)
+-{
+- return container_of(subdev, struct csi2_device, sd);
+-}
+-
+ static int csi2_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+ {
+@@ -554,45 +549,11 @@ static int csi2_pad_set_fmt(struct v4l2_
+ return 0;
+ }
+
+-static int csi2_link_validate(struct v4l2_subdev *sd, struct media_link *link,
+- struct v4l2_subdev_format *source_fmt,
+- struct v4l2_subdev_format *sink_fmt)
+-{
+- struct csi2_device *csi2 = to_csi2_device(sd);
+-
+- csi2_dbg("%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
+- link->source->entity->name, link->source->index,
+- link->sink->entity->name, link->sink->index);
+-
+- if ((link->source->entity == &csi2->sd.entity &&
+- link->source->index == 1) ||
+- (link->sink->entity == &csi2->sd.entity &&
+- link->sink->index == 1)) {
+- csi2_dbg("Ignore metadata pad for now\n");
+- return 0;
+- }
+-
+- /* The width, height and code must match. */
+- if (source_fmt->format.width != sink_fmt->format.width ||
+- source_fmt->format.width != sink_fmt->format.width ||
+- source_fmt->format.code != sink_fmt->format.code) {
+- csi2_err("%s: format does not match (source %ux%u 0x%x, sink %ux%u 0x%x)\n",
+- __func__,
+- source_fmt->format.width, source_fmt->format.height,
+- source_fmt->format.code,
+- sink_fmt->format.width, sink_fmt->format.height,
+- sink_fmt->format.code);
+- return -EPIPE;
+- }
+-
+- return 0;
+-}
+-
+ static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {
+ .init_cfg = csi2_init_cfg,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi2_pad_set_fmt,
+- .link_validate = csi2_link_validate,
++ .link_validate = v4l2_subdev_link_validate_default,
+ };
+
+ static const struct media_entity_operations csi2_entity_ops = {
--- /dev/null
+From 0eeb351222adbc5b534c86f7815ee787babc3485 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 3 Oct 2023 14:34:43 +0300
+Subject: [PATCH] media: rp1: fe: Use standard link_validate
+
+The current pisp_fe_link_validate() skips some important checks. Let's
+rather use the standard v4l2_subdev_link_validate_default() as the
+link_validate hook.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../platform/raspberrypi/rp1_cfe/pisp_fe.c | 36 +------------------
+ 1 file changed, 1 insertion(+), 35 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe.c
+@@ -388,11 +388,6 @@ void pisp_fe_stop(struct pisp_fe_device
+ pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
+ }
+
+-static struct pisp_fe_device *to_pisp_fe_device(struct v4l2_subdev *subdev)
+-{
+- return container_of(subdev, struct pisp_fe_device, sd);
+-}
+-
+ static int pisp_fe_init_cfg(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state)
+ {
+@@ -492,40 +487,11 @@ static int pisp_fe_pad_set_fmt(struct v4
+ }
+ }
+
+-static int pisp_fe_link_validate(struct v4l2_subdev *sd,
+- struct media_link *link,
+- struct v4l2_subdev_format *source_fmt,
+- struct v4l2_subdev_format *sink_fmt)
+-{
+- struct pisp_fe_device *fe = to_pisp_fe_device(sd);
+-
+- pisp_fe_dbg("%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
+- link->source->entity->name, link->source->index,
+- link->sink->entity->name, link->sink->index);
+-
+- /* The width, height and code must match. */
+- if (source_fmt->format.width != sink_fmt->format.width ||
+- source_fmt->format.width != sink_fmt->format.width ||
+- source_fmt->format.code != sink_fmt->format.code) {
+- pisp_fe_err("%s: format does not match (source %ux%u 0x%x, sink %ux%u 0x%x)\n",
+- __func__,
+- source_fmt->format.width,
+- source_fmt->format.height,
+- source_fmt->format.code,
+- sink_fmt->format.width,
+- sink_fmt->format.height,
+- sink_fmt->format.code);
+- return -EPIPE;
+- }
+-
+- return 0;
+-}
+-
+ static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = {
+ .init_cfg = pisp_fe_init_cfg,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = pisp_fe_pad_set_fmt,
+- .link_validate = pisp_fe_link_validate,
++ .link_validate = v4l2_subdev_link_validate_default,
+ };
+
+ static const struct media_entity_operations pisp_fe_entity_ops = {
--- /dev/null
+From e0f52ccfe1e383622fb30708acd38921e84fbff4 Mon Sep 17 00:00:00 2001
+From: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+Date: Tue, 3 Oct 2023 14:29:44 +0300
+Subject: [PATCH] media: rp1: cfe: Improve link validation for metadata
+
+Improve the link validation for metadata by:
+- Allowing capture buffers that are larger than the incoming frame
+ (instead of requiring exact match).
+
+- Instead of assuming that a metadata unit ("pixel") is 8 bits, use
+ find_format_by_code() to get the format and use the bit depth from
+ there. E.g. bit depth for RAW10 metadata will be 10 bits, when we
+ move to the upstream metadata formats.
+
+Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 32 +++++++++++++------
+ 1 file changed, 22 insertions(+), 10 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -1715,17 +1715,29 @@ static int cfe_video_link_validate(struc
+ }
+ } else if (is_csi2_node(node) && is_meta_output_node(node)) {
+ struct v4l2_meta_format *meta_fmt = &node->meta_fmt.fmt.meta;
++ const struct cfe_fmt *fmt;
++ u32 source_size;
+
+- if (source_fmt->width * source_fmt->height !=
+- meta_fmt->buffersize ||
+- source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) {
+- cfe_err("WARNING: Wrong metadata width/height/code %ux%u %08x (remote pad set to %ux%u %08x)\n",
+- meta_fmt->buffersize, 1,
+- MEDIA_BUS_FMT_SENSOR_DATA,
+- source_fmt->width,
+- source_fmt->height,
+- source_fmt->code);
+- /* TODO: this should throw an error eventually */
++ fmt = find_format_by_code(source_fmt->code);
++ if (!fmt || fmt->fourcc != meta_fmt->dataformat) {
++ cfe_err("Metadata format mismatch!\n");
++ ret = -EINVAL;
++ goto out;
++ }
++
++ source_size = DIV_ROUND_UP(source_fmt->width * source_fmt->height * fmt->depth, 8);
++
++ if (source_fmt->code != MEDIA_BUS_FMT_SENSOR_DATA) {
++ cfe_err("Bad metadata mbus format\n");
++ ret = -EINVAL;
++ goto out;
++ }
++
++ if (source_size > meta_fmt->buffersize) {
++ cfe_err("Metadata buffer too small: %u < %u\n",
++ meta_fmt->buffersize, source_size);
++ ret = -EINVAL;
++ goto out;
+ }
+ }
+
--- /dev/null
+From 046d03c87ecce5db074eae6ffa2d5298c5f7d5a7 Mon Sep 17 00:00:00 2001
+From: Leon Anavi <leon.anavi@konsulko.com>
+Date: Tue, 12 Dec 2023 13:53:37 +0200
+Subject: [PATCH] drivers/pinctrl/bcm/Kconfig: Fix BCM2712 help
+
+Replace "Broadcom BCM2835 GPIO" with "Broadcom BCM2712 PINCONF"
+in the help message. This work was sponsored by GOVCERT.LU.
+
+Signed-off-by: Leon Anavi <leon.anavi@konsulko.com>
+---
+ drivers/pinctrl/bcm/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/pinctrl/bcm/Kconfig
++++ b/drivers/pinctrl/bcm/Kconfig
+@@ -10,7 +10,7 @@ config PINCTRL_BCM2712
+ select PINCONF
+ select GENERIC_PINCONF
+ help
+- Say Y here to enable the Broadcom BCM2835 GPIO driver.
++ Say Y here to enable the Broadcom BCM2712 PINCONF driver.
+
+ config PINCTRL_BCM281XX
+ bool "Broadcom BCM281xx pinctrl driver"
--- /dev/null
+From a6872b25f18fe46ef9979f9d4a3635a9c3966afd Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Mon, 11 Dec 2023 15:06:45 +0800
+Subject: [PATCH] drivers/gpu/drm/panel:fix waveshare panel software
+ restart/shutdown display is abnormal Fixed the screen stays white when the
+ user restarts or shuts down
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -362,9 +362,18 @@ static void ws_panel_remove(struct i2c_c
+ {
+ struct ws_panel *ts = i2c_get_clientdata(i2c);
+
++ ws_panel_disable(&ts->base);
++
+ drm_panel_remove(&ts->base);
+ }
+
++static void ws_panel_shutdown(struct i2c_client *i2c)
++{
++ struct ws_panel *ts = i2c_get_clientdata(i2c);
++
++ ws_panel_disable(&ts->base);
++}
++
+ static const struct of_device_id ws_panel_of_ids[] = {
+ {
+ .compatible = "waveshare,2.8inch-panel",
+@@ -403,6 +412,7 @@ static struct i2c_driver ws_panel_driver
+ },
+ .probe = ws_panel_probe,
+ .remove = ws_panel_remove,
++ .shutdown = ws_panel_shutdown,
+ };
+ module_i2c_driver(ws_panel_driver);
+
--- /dev/null
+From 6988ae7c909dc342322a82daaa3a95b78d038305 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 11 Dec 2023 16:58:07 +0000
+Subject: [PATCH] firmware/psci: Pass given partition number through
+
+Pi 5 uses BL31 as its armstub file, so the reset goes via PSCI. Parse
+any "reboot" parameter as a partition number to reboot into.
+N.B. This code path is only used if reboot mode has been set to warm
+or soft.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/firmware/psci/psci.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/firmware/psci/psci.c
++++ b/drivers/firmware/psci/psci.c
+@@ -314,7 +314,14 @@ static int psci_sys_reset(struct notifie
+ * reset_type[30:0] = 0 (SYSTEM_WARM_RESET)
+ * cookie = 0 (ignored by the implementation)
+ */
+- invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0);
++ // Allow extra arguments separated by spaces after
++ // the partition number.
++ unsigned long val;
++ u8 partition = 0;
++
++ if (data && sscanf(data, "%lu", &val) == 1 && val < 63)
++ partition = val;
++ invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, partition, 0);
+ } else {
+ invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
+ }
--- /dev/null
+From 5d87d7f91cb4f1d0f391f6fe9dd0524b363b78e3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 11 Dec 2023 17:00:56 +0000
+Subject: [PATCH] dts: bcm2712-rpi-5-b: Enable warm reboot mode
+
+Switch to warm reboot mode so that the partition number is preserved.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -428,7 +428,7 @@ dpi_16bit_gpio2: &rp1_dpi_16bit_g
+
+ / {
+ chosen: chosen {
+- bootargs = "coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
++ bootargs = "reboot=w coherent_pool=1M 8250.nr_uarts=1 pci=pcie_bus_safe snd_bcm2835.enable_compat_alsa=0 snd_bcm2835.enable_hdmi=1";
+ stdout-path = "serial10:115200n8";
+ };
+
--- /dev/null
+From 083f39e40d980b47ab12b451d40b9f935bb22a5b Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 6 Dec 2023 14:27:57 +0000
+Subject: [PATCH] drivers: media: i2c: imx296,imx477: Configure tigger_mode
+ every time
+
+Don't assume the camera has been reset each time we start streaming,
+but always write registers relating to trigger_mode, even in mode 0.
+
+IMX477: Stop driving XVS on stop streaming, to avoid spurious pulses.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/media/i2c/imx296.c | 9 +++++----
+ drivers/media/i2c/imx477.c | 29 ++++++++++++++---------------
+ 2 files changed, 19 insertions(+), 19 deletions(-)
+
+--- a/drivers/media/i2c/imx296.c
++++ b/drivers/media/i2c/imx296.c
+@@ -650,10 +650,11 @@ static int imx296_stream_on(struct imx29
+ imx296_write(sensor, IMX296_CTRL00, 0, &ret);
+ usleep_range(2000, 5000);
+
+- if (trigger_mode == 1) {
+- imx296_write(sensor, IMX296_CTRL0B, IMX296_CTRL0B_TRIGEN, &ret);
+- imx296_write(sensor, IMX296_LOWLAGTRG, IMX296_LOWLAGTRG_FAST, &ret);
+- }
++ /* external trigger mode: 0=normal, 1=triggered */
++ imx296_write(sensor, IMX296_CTRL0B,
++ (trigger_mode == 1) ? IMX296_CTRL0B_TRIGEN : 0, &ret);
++ imx296_write(sensor, IMX296_LOWLAGTRG,
++ (trigger_mode == 1) ? IMX296_LOWLAGTRG_FAST : 0, &ret);
+
+ imx296_write(sensor, IMX296_CTRL0A, 0, &ret);
+
+--- a/drivers/media/i2c/imx477.c
++++ b/drivers/media/i2c/imx477.c
+@@ -1742,26 +1742,21 @@ static int imx477_start_streaming(struct
+ imx477_write_reg(imx477, 0x0b05, IMX477_REG_VALUE_08BIT, !!dpc_enable);
+ imx477_write_reg(imx477, 0x0b06, IMX477_REG_VALUE_08BIT, !!dpc_enable);
+
+- /* Set vsync trigger mode */
+- if (trigger_mode != 0) {
+- /* trigger_mode == 1 for source, 2 for sink */
+- const u32 val = (trigger_mode == 1) ? 1 : 0;
+-
+- imx477_write_reg(imx477, IMX477_REG_MC_MODE,
+- IMX477_REG_VALUE_08BIT, 1);
+- imx477_write_reg(imx477, IMX477_REG_MS_SEL,
+- IMX477_REG_VALUE_08BIT, val);
+- imx477_write_reg(imx477, IMX477_REG_XVS_IO_CTRL,
+- IMX477_REG_VALUE_08BIT, val);
+- imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN,
+- IMX477_REG_VALUE_08BIT, val);
+- }
+-
+ /* Apply customized values from user */
+ ret = __v4l2_ctrl_handler_setup(imx477->sd.ctrl_handler);
+ if (ret)
+ return ret;
+
++ /* Set vsync trigger mode: 0=standalone, 1=source, 2=sink */
++ imx477_write_reg(imx477, IMX477_REG_MC_MODE,
++ IMX477_REG_VALUE_08BIT, (trigger_mode > 0) ? 1 : 0);
++ imx477_write_reg(imx477, IMX477_REG_MS_SEL,
++ IMX477_REG_VALUE_08BIT, (trigger_mode <= 1) ? 1 : 0);
++ imx477_write_reg(imx477, IMX477_REG_XVS_IO_CTRL,
++ IMX477_REG_VALUE_08BIT, (trigger_mode == 1) ? 1 : 0);
++ imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN,
++ IMX477_REG_VALUE_08BIT, (trigger_mode == 1) ? 1 : 0);
++
+ /* set stream on register */
+ return imx477_write_reg(imx477, IMX477_REG_MODE_SELECT,
+ IMX477_REG_VALUE_08BIT, IMX477_MODE_STREAMING);
+@@ -1778,6 +1773,10 @@ static void imx477_stop_streaming(struct
+ IMX477_REG_VALUE_08BIT, IMX477_MODE_STANDBY);
+ if (ret)
+ dev_err(&client->dev, "%s failed to set stream\n", __func__);
++
++ /* Stop driving XVS out (there is still a weak pull-up) */
++ imx477_write_reg(imx477, IMX477_REG_EXTOUT_EN,
++ IMX477_REG_VALUE_08BIT, 0);
+ }
+
+ static int imx477_set_stream(struct v4l2_subdev *sd, int enable)
--- /dev/null
+From 3ed57f25f3074f6abfe570fc11aa7d370fdc499a Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Wed, 6 Dec 2023 14:38:03 +0000
+Subject: [PATCH] overlays: Add "always-on" parameter to imx477 and imx296
+
+Leave the camera's power supplies up, to prevent the camera
+clamping its 1.8V digital I/Os to ground. This may be useful
+when synchronizing multiple camera systems using XVS or XTRIG.
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ arch/arm/boot/dts/overlays/README | 6 ++++++
+ arch/arm/boot/dts/overlays/imx296-overlay.dts | 9 +++++++++
+ arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi | 9 +++++++++
+ 3 files changed, 24 insertions(+)
+
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -2520,6 +2520,8 @@ Params: rotation Mounting
+ clock-frequency Sets the clock frequency to match that used on
+ the board, which should be one of 54000000
+ (the default), 37125000 or 74250000.
++ always-on Leave the regulator powered up, to stop the
++ camera clamping I/Os such as XTRIG to 0V.
+
+
+ Name: imx327
+@@ -2558,6 +2560,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).
++ always-on Leave the regulator powered up, to stop the
++ camera clamping I/Os such as XVS to 0V.
+
+
+ Name: imx462
+@@ -2596,6 +2600,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).
++ always-on Leave the regulator powered up, to stop the
++ camera clamping I/Os such as XVS to 0V.
+
+
+ Name: imx519
+--- a/arch/arm/boot/dts/overlays/imx296-overlay.dts
++++ b/arch/arm/boot/dts/overlays/imx296-overlay.dts
+@@ -37,6 +37,13 @@
+ };
+ };
+
++ reg_alwayson_frag: fragment@99 {
++ target = <&cam1_reg>;
++ __dormant__ {
++ regulator-always-on;
++ };
++ };
++
+ i2c_frag: fragment@100 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+@@ -98,8 +105,10 @@
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <®_frag>, "target:0=",<&cam0_reg>,
++ <®_alwayson_frag>, "target:0=",<&cam0_reg>,
+ <&imx296>, "clocks:0=",<&cam0_clk>,
+ <&imx296>, "avdd-supply:0=",<&cam0_reg>;
+ clock-frequency = <&clk_over>, "clock-frequency:0";
++ always-on = <0>, "+99";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
++++ b/arch/arm/boot/dts/overlays/imx477_378-overlay.dtsi
+@@ -33,6 +33,13 @@
+ };
+ };
+
++ reg_alwayson_frag: fragment@99 {
++ target = <&cam1_reg>;
++ __dormant__ {
++ regulator-always-on;
++ };
++ };
++
+ i2c_frag: fragment@100 {
+ target = <&i2c_csi_dsi>;
+ __overlay__ {
+@@ -69,8 +76,10 @@
+ <&csi_frag>, "target:0=",<&csi0>,
+ <&clk_frag>, "target:0=",<&cam0_clk>,
+ <®_frag>, "target:0=",<&cam0_reg>,
++ <®_alwayson_frag>, "target:0=",<&cam0_reg>,
+ <&cam_node>, "clocks:0=",<&cam0_clk>,
+ <&cam_node>, "VANA-supply:0=",<&cam0_reg>;
++ always-on = <0>, "+99";
+ };
+ };
+
--- /dev/null
+From e0ecfaf1abfd5ef63ceec7606352020a80a2742b Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Mon, 18 Dec 2023 11:49:36 +0000
+Subject: [PATCH] input: edt-ft5x06: Correct prefix length in snprintf
+
+snprintf takes the length of the array that we can print into,
+and has to fit the NULL terminator in there too.
+Printing the prefix is generally "12-3456 " which is 8 desired
+characters (the length of EDT_NAME_PREFIX_LEN) and the NULL.
+The space is therefore being truncated to fit the NULL in.
+
+Increase the length snprintf is allowed to use.
+
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/input/touchscreen/edt-ft5x06.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/input/touchscreen/edt-ft5x06.c
++++ b/drivers/input/touchscreen/edt-ft5x06.c
+@@ -966,7 +966,7 @@ static int edt_ft5x06_ts_identify(struct
+ char *model_name = tsdata->name;
+ char *fw_version = tsdata->fw_version;
+
+- snprintf(model_name, EDT_NAME_PREFIX_LEN, "%s ", dev_name(&client->dev));
++ snprintf(model_name, EDT_NAME_PREFIX_LEN + 1, "%s ", dev_name(&client->dev));
+ model_name += strlen(model_name);
+
+ /* see what we find if we assume it is a M06 *
--- /dev/null
+From da5fd98469edd797ed77d9a8690a608c6b54f9e6 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Dec 2023 14:55:21 +0000
+Subject: [PATCH] ARM: dts: bcm2712-rpi-5-b: Allow RTC to be disabled
+
+Add a dtparam "rtc", so that "dtparam=rtc=off" can be used to disable
+the Pi 5's onboard RTC.
+
+See: https://forums.raspberrypi.com/viewtopic.php?t=361813
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 1 +
+ arch/arm/boot/dts/overlays/README | 3 +++
+ 2 files changed, 4 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -819,6 +819,7 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ pciex1_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
+ pcie_tperst_clk_ms = <&pciex1>, "brcm,tperst-clk-ms:0";
+ random = <&random>, "status";
++ rtc = <&rpi_rtc>, "status";
+ rtc_bbat_vchg = <&rpi_rtc>, "trickle-charge-microvolt:0";
+ spi = <&spi0>, "status";
+ suspend = <&pwr_key>, "linux,code:0=205";
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -307,6 +307,9 @@ Params:
+ random Set to "on" to enable the hardware random
+ number generator (default "on")
+
++ rtc Set to "off" to disable the onboard Real Time
++ Clock (2712 only, default "on")
++
+ rtc_bbat_vchg Set the RTC backup battery charging voltage in
+ microvolts. If set to 0 or not specified, the
+ trickle charger is disabled.
--- /dev/null
+From 0a09088e24c013ef608b1bb79501ef890cefc767 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Dec 2023 11:16:25 +0000
+Subject: [PATCH] i2c: designware: Look for *CNT values in DT
+
+The i2c-designware driver supports reading precise timing values from
+ACPI, but the Device Tree support relies on a combination of standard
+rise and fall times and hard-coded minimum timings. The result of this
+is that it is difficult to get optimum timings, particularly given that
+the values are bus speed-specific and only one set can be stored in
+DT at a time.
+
+Add support for initialisation from DT that is similar to that for
+ACPI.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/i2c/busses/i2c-designware-platdrv.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-designware-platdrv.c
++++ b/drivers/i2c/busses/i2c-designware-platdrv.c
+@@ -132,9 +132,18 @@ static int mscc_twi_set_sda_hold_time(st
+ return 0;
+ }
+
++static void dw_i2c_read_of_cnt(struct device_node *np, const char *name, u16 *pval)
++{
++ u32 val;
++
++ if (!of_property_read_u32(np, name, &val))
++ *pval = (u16)val;
++}
++
+ static int dw_i2c_of_configure(struct platform_device *pdev)
+ {
+ struct dw_i2c_dev *dev = platform_get_drvdata(pdev);
++ struct device_node *np = pdev->dev.of_node;
+
+ switch (dev->flags & MODEL_MASK) {
+ case MODEL_MSCC_OCELOT:
+@@ -146,6 +155,15 @@ static int dw_i2c_of_configure(struct pl
+ break;
+ }
+
++ dw_i2c_read_of_cnt(np, "snps,ss_hcnt", &dev->ss_hcnt);
++ dw_i2c_read_of_cnt(np, "snps,ss_lcnt", &dev->ss_lcnt);
++ dw_i2c_read_of_cnt(np, "snps,fs_hcnt", &dev->fs_hcnt);
++ dw_i2c_read_of_cnt(np, "snps,fs_lcnt", &dev->fs_lcnt);
++ dw_i2c_read_of_cnt(np, "snps,fp_hcnt", &dev->fp_hcnt);
++ dw_i2c_read_of_cnt(np, "snps,fp_lcnt", &dev->fp_lcnt);
++ dw_i2c_read_of_cnt(np, "snps,hs_hcnt", &dev->hs_hcnt);
++ dw_i2c_read_of_cnt(np, "snps,hs_lcnt", &dev->hs_lcnt);
++
+ return 0;
+ }
+
--- /dev/null
+From 660d569b1a623e4b64350e608bbf8bc2cc6332e9 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Tue, 19 Dec 2023 11:27:20 +0000
+Subject: [PATCH] dts: rp1: Add I2C timings
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/rp1.dtsi | 42 ++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 42 insertions(+)
+
+--- a/arch/arm/boot/dts/rp1.dtsi
++++ b/arch/arm/boot/dts/rp1.dtsi
+@@ -305,6 +305,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C0 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
+@@ -313,6 +319,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C1 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
+@@ -321,6 +333,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C2 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
+@@ -329,6 +347,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C3 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
+@@ -337,6 +361,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C4 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
+@@ -345,6 +375,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C5 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
+@@ -353,6 +389,12 @@
+ compatible = "snps,designware-i2c";
+ interrupts = <RP1_INT_I2C6 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&rp1_clocks RP1_CLK_SYS>;
++ snps,ss_hcnt = <978>;
++ snps,ss_lcnt = <990>;
++ snps,fs_hcnt = <200>;
++ snps,fs_lcnt = <268>;
++ snps,fp_hcnt = <60>;
++ snps,fp_lcnt = <107>;
+ status = "disabled";
+ };
+
--- /dev/null
+From 74c6c159c2b499bdf3c39961bd40eb9243624226 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Wed, 29 Nov 2023 13:09:05 +0000
+Subject: [PATCH] drivers: media: pisp_be: pisp_fe: Update UAPI header licenses
+
+Update the license tags on the pisp UAPI header files with the
+"Linux-syscall-note" clause. Also replace the "GPL-2.0" tag with the
+preferred "GPL-2.0-only" tag.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h | 2 +-
+ drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
++++ b/drivers/media/platform/raspberrypi/pisp_be/pisp_be_config.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0-only */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+ * PiSP Back End configuration definitions.
+ *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_common.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+ * RP1 PiSP common definitions.
+ *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_fe_config.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+ * RP1 PiSP Front End Driver Configuration structures
+ *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_statistics.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+ * RP1 PiSP Front End statistics definitions
+ *
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/pisp_types.h
+@@ -1,4 +1,4 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
++/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
+ /*
+ * RP1 PiSP Front End image definitions.
+ *
--- /dev/null
+From 6fb7a0b4c1dd6cf5b12ec2b2c197dcf8e58cd2b9 Mon Sep 17 00:00:00 2001
+From: Naushir Patuck <naush@raspberrypi.com>
+Date: Mon, 18 Dec 2023 09:52:45 +0000
+Subject: [PATCH] drivers: media: cfe: Add more robust ISR handlers
+
+Update the ISR logic to be more robust to sensors in problematic states
+where interrupts may start arriving overlapped and/or missing.
+
+1) Test for cur_frame in the FE handler, and if present, dequeue it in
+an error state so that it does not get orphaned.
+
+2) Move the sequence counter and timestamp variables to the node
+structures. This allows the ISR to track channels running ahead when
+interrupts arrive unordered.
+
+3) Add a test to ensure we don't have a spurios (but harmlesS) call to
+the FE handler in some circumstances.
+
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ .../media/platform/raspberrypi/rp1_cfe/cfe.c | 92 ++++++++++---------
+ 1 file changed, 49 insertions(+), 43 deletions(-)
+
+--- a/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
++++ b/drivers/media/platform/raspberrypi/rp1_cfe/cfe.c
+@@ -272,6 +272,8 @@ struct cfe_node {
+ /* Pointer to the parent handle */
+ struct cfe_device *cfe;
+ struct media_pad pad;
++ unsigned int fs_count;
++ u64 ts;
+ };
+
+ struct cfe_device {
+@@ -311,9 +313,6 @@ struct cfe_device {
+ struct pisp_fe_device fe;
+
+ int fe_csi2_channel;
+-
+- unsigned int sequence;
+- u64 ts;
+ };
+
+ static inline bool is_fe_enabled(struct cfe_device *cfe)
+@@ -393,17 +392,6 @@ static bool test_all_nodes(struct cfe_de
+ return true;
+ }
+
+-static void clear_all_nodes(struct cfe_device *cfe, unsigned long precond,
+- unsigned long state)
+-{
+- unsigned int i;
+-
+- for (i = 0; i < NUM_NODES; i++) {
+- if (check_state(cfe, precond, i))
+- clear_state(cfe, state, i);
+- }
+-}
+-
+ static int mipi_cfg_regs_show(struct seq_file *s, void *data)
+ {
+ struct cfe_device *cfe = s->private;
+@@ -656,22 +644,22 @@ static void cfe_prepare_next_job(struct
+ }
+
+ static void cfe_process_buffer_complete(struct cfe_node *node,
+- unsigned int sequence)
++ enum vb2_buffer_state state)
+ {
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg_verbose("%s: [%s] buffer:%p\n", __func__,
+ node_desc[node->id].name, &node->cur_frm->vb.vb2_buf);
+
+- node->cur_frm->vb.sequence = sequence;
+- vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE);
++ node->cur_frm->vb.sequence = node->fs_count - 1;
++ vb2_buffer_done(&node->cur_frm->vb.vb2_buf, state);
+ }
+
+ static void cfe_queue_event_sof(struct cfe_node *node)
+ {
+ struct v4l2_event event = {
+ .type = V4L2_EVENT_FRAME_SYNC,
+- .u.frame_sync.frame_sequence = node->cfe->sequence,
++ .u.frame_sync.frame_sequence = node->fs_count - 1,
+ };
+
+ v4l2_event_queue(&node->video_dev, &event);
+@@ -680,28 +668,53 @@ static void cfe_queue_event_sof(struct c
+ static void cfe_sof_isr_handler(struct cfe_node *node)
+ {
+ struct cfe_device *cfe = node->cfe;
++ bool matching_fs = true;
++ unsigned int i;
+
+ cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+- cfe->sequence);
+-
+- node->cur_frm = node->next_frm;
+- node->next_frm = NULL;
++ node->fs_count);
+
+ /*
+- * If this is the first node to see a frame start, sample the
+- * timestamp to use for all frames across all channels.
++ * If the sensor is producing unexpected frame event ordering over a
++ * sustained period of time, guard against the possibility of coming
++ * here and orphaning the cur_frm if it's not been dequeued already.
++ * Unfortunately, there is not enough hardware state to tell if this
++ * may have occurred.
+ */
+- if (!test_any_node(cfe, NODE_STREAMING | FS_INT))
+- cfe->ts = ktime_get_ns();
++ if (WARN(node->cur_frm, "%s: [%s] Orphanded frame at seq %u\n",
++ __func__, node_desc[node->id].name, node->fs_count))
++ cfe_process_buffer_complete(node, VB2_BUF_STATE_ERROR);
+
+- set_state(cfe, FS_INT, node->id);
++ node->cur_frm = node->next_frm;
++ node->next_frm = NULL;
++ node->fs_count++;
+
+- /* If all nodes have seen a frame start, we can queue another job. */
+- if (test_all_nodes(cfe, NODE_STREAMING, FS_INT))
++ node->ts = ktime_get_ns();
++ for (i = 0; i < NUM_NODES; i++) {
++ if (!check_state(cfe, NODE_STREAMING, i) || i == node->id)
++ continue;
++ /*
++ * This checks if any other node has seen a FS. If yes, use the
++ * same timestamp, eventually across all node buffers.
++ */
++ if (cfe->node[i].fs_count >= node->fs_count)
++ node->ts = cfe->node[i].ts;
++ /*
++ * This checks if all other node have seen a matching FS. If
++ * yes, we can flag another job to be queued.
++ */
++ if (matching_fs && cfe->node[i].fs_count != node->fs_count)
++ matching_fs = false;
++ }
++
++ if (matching_fs)
+ cfe->job_queued = false;
+
+ if (node->cur_frm)
+- node->cur_frm->vb.vb2_buf.timestamp = cfe->ts;
++ node->cur_frm->vb.vb2_buf.timestamp = node->ts;
++
++ set_state(cfe, FS_INT, node->id);
++ clear_state(cfe, FE_INT, node->id);
+
+ if (is_image_output_node(node))
+ cfe_queue_event_sof(node);
+@@ -712,22 +725,14 @@ static void cfe_eof_isr_handler(struct c
+ struct cfe_device *cfe = node->cfe;
+
+ cfe_dbg_verbose("%s: [%s] seq %u\n", __func__, node_desc[node->id].name,
+- cfe->sequence);
++ node->fs_count - 1);
+
+ if (node->cur_frm)
+- cfe_process_buffer_complete(node, cfe->sequence);
++ cfe_process_buffer_complete(node, VB2_BUF_STATE_DONE);
+
+ node->cur_frm = NULL;
+ set_state(cfe, FE_INT, node->id);
+-
+- /*
+- * If all nodes have seen a frame end, we can increment
+- * the sequence counter now.
+- */
+- if (test_all_nodes(cfe, NODE_STREAMING, FE_INT)) {
+- cfe->sequence++;
+- clear_all_nodes(cfe, NODE_STREAMING, FE_INT | FS_INT);
+- }
++ clear_state(cfe, FS_INT, node->id);
+ }
+
+ static irqreturn_t cfe_isr(int irq, void *dev)
+@@ -794,7 +799,8 @@ static irqreturn_t cfe_isr(int irq, void
+ * frame first before the FS handler for the current
+ * frame.
+ */
+- if (check_state(cfe, FS_INT, node->id)) {
++ if (check_state(cfe, FS_INT, node->id) &&
++ !check_state(cfe, FE_INT, node->id)) {
+ cfe_dbg("%s: [%s] Handling missing previous FE interrupt\n",
+ __func__, node_desc[node->id].name);
+ cfe_eof_isr_handler(node);
+@@ -1131,6 +1137,7 @@ static int cfe_start_streaming(struct vb
+
+ clear_state(cfe, FS_INT | FE_INT, node->id);
+ set_state(cfe, NODE_STREAMING, node->id);
++ node->fs_count = 0;
+ cfe_start_channel(node);
+
+ if (!test_all_nodes(cfe, NODE_ENABLED, NODE_STREAMING)) {
+@@ -1166,7 +1173,6 @@ static int cfe_start_streaming(struct vb
+ csi2_open_rx(&cfe->csi2);
+
+ cfe_dbg("Starting sensor streaming\n");
+- cfe->sequence = 0;
+ ret = v4l2_subdev_call(cfe->sensor, video, s_stream, 1);
+ if (ret < 0) {
+ cfe_err("stream on failed in subdev\n");
--- /dev/null
+From dfc04900c40eb14f9364d56e96db2cc3340a1f21 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Wed, 3 Jan 2024 14:43:43 +0000
+Subject: [PATCH] ASoC: dwc: Defer bclk_ratio handling to hw_params
+
+bclk_ratio is only a factor in clock producer mode, and needs to
+override the default value of num_channels * sample_size.
+Move the bclk_ratio handling into the hw_params method, only latching
+the value in set_bclk_ratio, to address both of those matters.
+
+See: https://github.com/raspberrypi/linux/issues/5817
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ sound/soc/dwc/dwc-i2s.c | 38 +++++++++++++++++++++-----------------
+ sound/soc/dwc/local.h | 1 +
+ 2 files changed, 22 insertions(+), 17 deletions(-)
+
+--- a/sound/soc/dwc/dwc-i2s.c
++++ b/sound/soc/dwc/dwc-i2s.c
+@@ -263,6 +263,25 @@ static int dw_i2s_hw_params(struct snd_p
+ return -EINVAL;
+ }
+
++ if ((dev->capability & DW_I2S_MASTER) && dev->bclk_ratio) {
++ switch (dev->bclk_ratio) {
++ case 32:
++ dev->ccr = 0x00;
++ break;
++
++ case 48:
++ dev->ccr = 0x08;
++ break;
++
++ case 64:
++ dev->ccr = 0x10;
++ break;
++
++ default:
++ return -EINVAL;
++ }
++ }
++
+ config->chan_nr = params_channels(params);
+
+ switch (config->chan_nr) {
+@@ -436,23 +455,7 @@ static int dw_i2s_set_bclk_ratio(struct
+
+ dev_dbg(dev->dev, "%s(%d)\n", __func__, ratio);
+
+- switch (ratio) {
+- case 32:
+- dev->ccr = 0x00;
+- break;
+-
+- case 48:
+- dev->ccr = 0x08;
+- break;
+-
+- case 64:
+- dev->ccr = 0x10;
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- i2s_write_reg(dev->i2s_base, CCR, dev->ccr);
++ dev->bclk_ratio = ratio;
+
+ return 0;
+ }
+@@ -746,6 +749,7 @@ static int dw_i2s_probe(struct platform_
+ }
+ }
+
++ dev->bclk_ratio = 0;
+ dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
+ dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
+ if (pdata) {
+--- a/sound/soc/dwc/local.h
++++ b/sound/soc/dwc/local.h
+@@ -107,6 +107,7 @@ struct dw_i2s_dev {
+ unsigned int quirks;
+ unsigned int i2s_reg_comp1;
+ unsigned int i2s_reg_comp2;
++ unsigned int bclk_ratio;
+ struct device *dev;
+ u32 ccr;
+ u32 xfer_resolution;
--- /dev/null
+From d5066442e39dd9bf4ba6431ffb3f99e3d5085d3f Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 4 Jan 2024 12:02:43 +0000
+Subject: [PATCH] drm/vc4: Fix reading of frame count on GEN5 / Pi4
+
+The frame count values moved within registers DISPSTAT1 and
+DISPSTAT2 with GEN5, so update the accessor function to
+accommodate that.
+
+Fixes: b51cd7ad143d ("drm/vc4: hvs: Fix frame count register readout")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_hvs.c | 23 +++++++++++++++++++++--
+ drivers/gpu/drm/vc4/vc4_regs.h | 6 ++++++
+ 2 files changed, 27 insertions(+), 2 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_hvs.c
++++ b/drivers/gpu/drm/vc4/vc4_hvs.c
+@@ -823,10 +823,28 @@ u8 vc4_hvs_get_fifo_frame_count(struct v
+ if (!drm_dev_enter(drm, &idx))
+ return 0;
+
+- if (vc4->gen >= VC4_GEN_6) {
++ switch (vc4->gen) {
++ case VC4_GEN_6:
+ field = VC4_GET_FIELD(HVS_READ(SCALER6_DISPX_STATUS(fifo)),
+ SCALER6_DISPX_STATUS_FRCNT);
+- } else {
++ break;
++ case VC4_GEN_5:
++ switch (fifo) {
++ case 0:
++ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++ SCALER5_DISPSTAT1_FRCNT0);
++ break;
++ case 1:
++ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
++ SCALER5_DISPSTAT1_FRCNT1);
++ break;
++ case 2:
++ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT2),
++ SCALER5_DISPSTAT2_FRCNT2);
++ break;
++ }
++ break;
++ case VC4_GEN_4:
+ switch (fifo) {
+ case 0:
+ field = VC4_GET_FIELD(HVS_READ(SCALER_DISPSTAT1),
+@@ -841,6 +859,7 @@ u8 vc4_hvs_get_fifo_frame_count(struct v
+ SCALER_DISPSTAT2_FRCNT2);
+ break;
+ }
++ break;
+ }
+
+ drm_dev_exit(idx);
+--- a/drivers/gpu/drm/vc4/vc4_regs.h
++++ b/drivers/gpu/drm/vc4/vc4_regs.h
+@@ -424,6 +424,10 @@
+ # define SCALER_DISPSTAT1_FRCNT0_SHIFT 18
+ # define SCALER_DISPSTAT1_FRCNT1_MASK VC4_MASK(17, 12)
+ # define SCALER_DISPSTAT1_FRCNT1_SHIFT 12
++# define SCALER5_DISPSTAT1_FRCNT0_MASK VC4_MASK(25, 20)
++# define SCALER5_DISPSTAT1_FRCNT0_SHIFT 20
++# define SCALER5_DISPSTAT1_FRCNT1_MASK VC4_MASK(19, 14)
++# define SCALER5_DISPSTAT1_FRCNT1_SHIFT 14
+
+ #define SCALER_DISPSTATX(x) (SCALER_DISPSTAT0 + \
+ (x) * (SCALER_DISPSTAT1 - \
+@@ -442,6 +446,8 @@
+ #define SCALER_DISPSTAT2 0x00000068
+ # define SCALER_DISPSTAT2_FRCNT2_MASK VC4_MASK(17, 12)
+ # define SCALER_DISPSTAT2_FRCNT2_SHIFT 12
++# define SCALER5_DISPSTAT2_FRCNT2_MASK VC4_MASK(19, 14)
++# define SCALER5_DISPSTAT2_FRCNT2_SHIFT 14
+
+ #define SCALER_DISPBASE2 0x0000006c
+ #define SCALER_DISPALPHA2 0x00000070
--- /dev/null
+From eeb5969ae34df16024f39a77496a44ae94bfab13 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 4 Jan 2024 13:56:39 +0000
+Subject: [PATCH] ARM: dts: bcm2712-rpi-5-b: Add eth_ledx parameters
+
+Include the dtparams controlling the Ethernet jack LEDs, as used on
+other Pis.
+
+See: https://github.com/raspberrypi/linux/issues/5825
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 2 ++
+ arch/arm/boot/dts/overlays/README | 6 +++---
+ 2 files changed, 5 insertions(+), 3 deletions(-)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -830,6 +830,8 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ act_led_trigger = <&act_led>, "linux,default-trigger";
+ pwr_led_activelow = <&pwr_led>, "gpios:8";
+ pwr_led_trigger = <&pwr_led>, "linux,default-trigger";
++ eth_led0 = <&phy1>,"led-modes:0";
++ eth_led1 = <&phy1>,"led-modes:4";
+ drm_fb0_rp1_dsi0 = <&aliases>, "drm-fb0=",&dsi0;
+ drm_fb0_rp1_dsi1 = <&aliases>, "drm-fb0=",&dsi1;
+ drm_fb0_rp1_dpi = <&aliases>, "drm-fb0=",&dpi;
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -207,7 +207,7 @@ Params:
+ 0 means never downshift (default 2). Pi3B+ only.
+
+ eth_led0 Set mode of LED0 - amber on Pi3B+ (default "1"),
+- green on Pi4 (default "0").
++ green on Pi4/5 (default "0").
+ The legal values are:
+
+ Pi3B+
+@@ -217,7 +217,7 @@ Params:
+ 4=link100/1000/activity 5=link10/1000/activity
+ 6=link10/100/activity 14=off 15=on
+
+- Pi4
++ Pi4/5
+
+ 0=Speed/Activity 1=Speed
+ 2=Flash activity 3=FDX
+@@ -226,7 +226,7 @@ Params:
+ 8=Link 9=Activity
+
+ eth_led1 Set mode of LED1 - green on Pi3B+ (default "6"),
+- amber on Pi4 (default "8"). See eth_led0 for
++ amber on Pi4/5 (default "8"). See eth_led0 for
+ legal values.
+
+ eth_max_speed Set the maximum speed a link is allowed
--- /dev/null
+From 2c085a1ff40521ab89d8f1894757aa83b59b4607 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 4 Jan 2024 12:09:10 +0000
+Subject: [PATCH] ARM: dts: bcm2712-rpi-5-b: Add fan speed dtparams
+
+Add dtparams for adjusting the Pi 5 cooling fan speeds and temperature
+thresholds.
+
+See: https://github.com/raspberrypi/linux/issues/5820
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ arch/arm/boot/dts/bcm2712-rpi-5-b.dts | 13 +++++++++++++
+ arch/arm/boot/dts/overlays/README | 25 +++++++++++++++++++++++++
+ 2 files changed, 38 insertions(+)
+
+--- a/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
++++ b/arch/arm/boot/dts/bcm2712-rpi-5-b.dts
+@@ -844,5 +844,18 @@ spi10_cs_pins: &spi10_cs_gpio1 {};
+ drm_fb2_rp1_dsi1 = <&aliases>, "drm-fb2=",&dsi1;
+ drm_fb2_rp1_dpi = <&aliases>, "drm-fb2=",&dpi;
+ drm_fb2_vc4 = <&aliases>, "drm-fb2=",&vc4;
++
++ fan_temp0 = <&cpu_tepid>,"temperature:0";
++ fan_temp1 = <&cpu_warm>,"temperature:0";
++ fan_temp2 = <&cpu_hot>,"temperature:0";
++ fan_temp3 = <&cpu_vhot>,"temperature:0";
++ fan_temp0_hyst = <&cpu_tepid>,"hysteresis:0";
++ fan_temp1_hyst = <&cpu_warm>,"hysteresis:0";
++ fan_temp2_hyst = <&cpu_hot>,"hysteresis:0";
++ fan_temp3_hyst = <&cpu_vhot>,"hysteresis:0";
++ fan_temp0_speed = <&fan>, "cooling-levels:4";
++ fan_temp1_speed = <&fan>, "cooling-levels:8";
++ fan_temp2_speed = <&fan>, "cooling-levels:12";
++ fan_temp3_speed = <&fan>, "cooling-levels:16";
+ };
+ };
+--- a/arch/arm/boot/dts/overlays/README
++++ b/arch/arm/boot/dts/overlays/README
+@@ -233,6 +233,31 @@ Params:
+ to negotiate. Legal values are 10, 100 and
+ 1000 (default 1000). Pi3B+ only.
+
++ fan_temp0 Temperature threshold (in millicelcius) for
++ 1st cooling level (default 50000). Pi5 only.
++ fan_temp0_hyst Temperature hysteresis (in millicelcius) for
++ 1st cooling level (default 5000). Pi5 only.
++ fan_temp0_speed Fan PWM setting for 1st cooling level (0-255,
++ default 75). Pi5 only.
++ fan_temp1 Temperature threshold (in millicelcius) for
++ 2nd cooling level (default 60000). Pi5 only.
++ fan_temp1_hyst Temperature hysteresis (in millicelcius) for
++ 2nd cooling level (default 5000). Pi5 only.
++ fan_temp1_speed Fan PWM setting for 2nd cooling level (0-255,
++ default 125). Pi5 only.
++ fan_temp2 Temperature threshold (in millicelcius) for
++ 3rd cooling level (default 67500). Pi5 only.
++ fan_temp2_hyst Temperature hysteresis (in millicelcius) for
++ 3rd cooling level (default 5000). Pi5 only.
++ fan_temp2_speed Fan PWM setting for 3rd cooling level (0-255,
++ default 175). Pi5 only.
++ fan_temp3 Temperature threshold (in millicelcius) for
++ 4th cooling level (default 75000). Pi5 only.
++ fan_temp3_hyst Temperature hysteresis (in millicelcius) for
++ 4th cooling level (default 5000). Pi5 only.
++ fan_temp3_speed Fan PWM setting for 4th cooling level (0-255,
++ default 250). Pi5 only.
++
+ hdmi Set to "off" to disable the HDMI interface
+ (default "on")
+
--- /dev/null
+From 146bbf9627f6c37816939de29538ec8ee9a7be1a Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Ma=C3=ADra=20Canal?= <mcanal@igalia.com>
+Date: Fri, 5 Jan 2024 15:07:34 -0300
+Subject: [PATCH] drm/vc4: don't check if plane->state->fb == state->fb
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Currently, when using non-blocking commits, we can see the following
+kernel warning:
+
+[ 110.908514] ------------[ cut here ]------------
+[ 110.908529] refcount_t: underflow; use-after-free.
+[ 110.908620] WARNING: CPU: 0 PID: 1866 at lib/refcount.c:87 refcount_dec_not_one+0xb8/0xc0
+[ 110.908664] Modules linked in: rfcomm snd_seq_dummy snd_hrtimer snd_seq snd_seq_device cmac algif_hash aes_arm64 aes_generic algif_skcipher af_alg bnep hid_logitech_hidpp vc4 brcmfmac hci_uart btbcm brcmutil bluetooth snd_soc_hdmi_codec cfg80211 cec drm_display_helper drm_dma_helper drm_kms_helper snd_soc_core snd_compress snd_pcm_dmaengine fb_sys_fops sysimgblt syscopyarea sysfillrect raspberrypi_hwmon ecdh_generic ecc rfkill libaes i2c_bcm2835 binfmt_misc joydev snd_bcm2835(C) bcm2835_codec(C) bcm2835_isp(C) v4l2_mem2mem videobuf2_dma_contig snd_pcm bcm2835_v4l2(C) raspberrypi_gpiomem bcm2835_mmal_vchiq(C) videobuf2_v4l2 snd_timer videobuf2_vmalloc videobuf2_memops videobuf2_common snd videodev vc_sm_cma(C) mc hid_logitech_dj uio_pdrv_genirq uio i2c_dev drm fuse dm_mod drm_panel_orientation_quirks backlight ip_tables x_tables ipv6
+[ 110.909086] CPU: 0 PID: 1866 Comm: kodi.bin Tainted: G C 6.1.66-v8+ #32
+[ 110.909104] Hardware name: Raspberry Pi 3 Model B Rev 1.2 (DT)
+[ 110.909114] pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--)
+[ 110.909132] pc : refcount_dec_not_one+0xb8/0xc0
+[ 110.909152] lr : refcount_dec_not_one+0xb4/0xc0
+[ 110.909170] sp : ffffffc00913b9c0
+[ 110.909177] x29: ffffffc00913b9c0 x28: 000000556969bbb0 x27: 000000556990df60
+[ 110.909205] x26: 0000000000000002 x25: 0000000000000004 x24: ffffff8004448480
+[ 110.909230] x23: ffffff800570b500 x22: ffffff802e03a7bc x21: ffffffecfca68c78
+[ 110.909257] x20: ffffff8002b42000 x19: ffffff802e03a600 x18: 0000000000000000
+[ 110.909283] x17: 0000000000000011 x16: ffffffffffffffff x15: 0000000000000004
+[ 110.909308] x14: 0000000000000fff x13: ffffffed577e47e0 x12: 0000000000000003
+[ 110.909333] x11: 0000000000000000 x10: 0000000000000027 x9 : c912d0d083728c00
+[ 110.909359] x8 : c912d0d083728c00 x7 : 65646e75203a745f x6 : 746e756f63666572
+[ 110.909384] x5 : ffffffed579f62ee x4 : ffffffed579eb01e x3 : 0000000000000000
+[ 110.909409] x2 : 0000000000000000 x1 : ffffffc00913b750 x0 : 0000000000000001
+[ 110.909434] Call trace:
+[ 110.909441] refcount_dec_not_one+0xb8/0xc0
+[ 110.909461] vc4_bo_dec_usecnt+0x4c/0x1b0 [vc4]
+[ 110.909903] vc4_cleanup_fb+0x44/0x50 [vc4]
+[ 110.910315] drm_atomic_helper_cleanup_planes+0x88/0xa4 [drm_kms_helper]
+[ 110.910669] vc4_atomic_commit_tail+0x390/0x9dc [vc4]
+[ 110.911079] commit_tail+0xb0/0x164 [drm_kms_helper]
+[ 110.911397] drm_atomic_helper_commit+0x1d0/0x1f0 [drm_kms_helper]
+[ 110.911716] drm_atomic_commit+0xb0/0xdc [drm]
+[ 110.912569] drm_mode_atomic_ioctl+0x348/0x4b8 [drm]
+[ 110.913330] drm_ioctl_kernel+0xec/0x15c [drm]
+[ 110.914091] drm_ioctl+0x24c/0x3b0 [drm]
+[ 110.914850] __arm64_sys_ioctl+0x9c/0xd4
+[ 110.914873] invoke_syscall+0x4c/0x114
+[ 110.914897] el0_svc_common+0xd0/0x118
+[ 110.914917] do_el0_svc+0x38/0xd0
+[ 110.914936] el0_svc+0x30/0x8c
+[ 110.914958] el0t_64_sync_handler+0x84/0xf0
+[ 110.914979] el0t_64_sync+0x18c/0x190
+[ 110.914996] ---[ end trace 0000000000000000 ]---
+
+This happens because, although `prepare_fb` and `cleanup_fb` are
+perfectly balanced, we cannot guarantee consistency in the check
+plane->state->fb == state->fb. This means that sometimes we can increase
+the refcount in `prepare_fb` and don't decrease it in `cleanup_fb`. The
+opposite can also be true.
+
+In fact, the struct drm_plane .state shouldn't be accessed directly
+but instead, the `drm_atomic_get_new_plane_state()` helper function should
+be used. So, we could stick to this check, but using
+`drm_atomic_get_new_plane_state()`. But actually, this check is not really
+needed. We can increase and decrease the refcount symmetrically without
+problems.
+
+This is going to make the code more simple and consistent.
+
+Signed-off-by: Maíra Canal <mcanal@igalia.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2225,9 +2225,6 @@ static int vc4_prepare_fb(struct drm_pla
+
+ drm_gem_plane_helper_prepare_fb(plane, state);
+
+- if (plane->state->fb == state->fb)
+- return 0;
+-
+ return vc4_bo_inc_usecnt(bo);
+ }
+
+@@ -2236,7 +2233,7 @@ static void vc4_cleanup_fb(struct drm_pl
+ {
+ struct vc4_bo *bo;
+
+- if (plane->state->fb == state->fb || !state->fb)
++ if (!state->fb)
+ return;
+
+ bo = to_vc4_bo(&drm_fb_dma_get_gem_obj(state->fb, 0)->base);
--- /dev/null
+From 5d9075ed7e73dc6ccebf78710c78f39ddc2dd78e Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Mon, 8 Jan 2024 11:42:57 +0000
+Subject: [PATCH] spi: bcm2835: Support spi0-0cs and SPI_NO_CS mode
+
+The forced conversion of native CS lines into software CS lines is done
+whether or not the controller has been given any CS lines to use. This
+breaks the use of the spi0-0cs overlay to prevent SPI from claiming any
+CS lines, particularly with spidev which doesn't pass in the SPI_NO_CS
+flag at creation.
+
+Use the presence of an empty cs-gpios property as an indication that no
+CS lines should be used, bypassing the native CS conversion code.
+
+See: https://github.com/raspberrypi/linux/issues/5835
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/spi/spi-bcm2835.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/spi/spi-bcm2835.c
++++ b/drivers/spi/spi-bcm2835.c
+@@ -1222,6 +1222,7 @@ static int bcm2835_spi_setup(struct spi_
+ struct bcm2835_spi *bs = spi_controller_get_devdata(ctlr);
+ struct bcm2835_spidev *slv = spi_get_ctldata(spi);
+ struct gpio_chip *chip;
++ int len;
+ int ret;
+ u32 cs;
+
+@@ -1287,6 +1288,10 @@ static int bcm2835_spi_setup(struct spi_
+ goto err_cleanup;
+ }
+
++ /* Skip forced CS conversion if controller has an empty cs-gpios property */
++ if (of_find_property(ctlr->dev.of_node, "cs-gpios", &len) && len == 0)
++ return 0;
++
+ /*
+ * Translate native CS to GPIO
+ *
--- /dev/null
+From 55ed8cded4af6530276b26f567601bed868ae8f5 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Wed, 10 Jan 2024 08:52:54 +0800
+Subject: [PATCH] drivers: media: imx519: Add V4L2_CID_LINK_FREQ control
+
+Add V4L2_CID_LINK_FREQ as a read-only control with a value of 408 Mhz.
+This will be used by the CFE driver to corretly setup the DPHY timing
+parameters in the CSI-2 block.
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/imx519.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/media/i2c/imx519.c
++++ b/drivers/media/i2c/imx519.c
+@@ -145,6 +145,10 @@ struct imx519_mode {
+ struct imx519_reg_list reg_list;
+ };
+
++static const s64 imx519_link_freq_menu[] = {
++ IMX519_DEFAULT_LINK_FREQ,
++};
++
+ static const struct imx519_reg mode_common_regs[] = {
+ {0x0100, 0x00},
+ {0x0136, 0x18},
+@@ -1819,6 +1823,7 @@ static int imx519_init_controls(struct i
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ struct i2c_client *client = v4l2_get_subdevdata(&imx519->sd);
+ struct v4l2_fwnode_device_properties props;
++ struct v4l2_ctrl *link_freq;
+ unsigned int i;
+ int ret;
+
+@@ -1837,6 +1842,15 @@ static int imx519_init_controls(struct i
+ IMX519_PIXEL_RATE, 1,
+ IMX519_PIXEL_RATE);
+
++ /* LINK_FREQ is also read only */
++ link_freq =
++ v4l2_ctrl_new_int_menu(ctrl_hdlr, &imx519_ctrl_ops,
++ V4L2_CID_LINK_FREQ,
++ ARRAY_SIZE(imx519_link_freq_menu) - 1, 0,
++ imx519_link_freq_menu);
++ if (link_freq)
++ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+ /*
+ * Create the controls here, but mode specific limits are setup
+ * in the imx519_set_framing_limits() call below.
--- /dev/null
+From 5e339e1502c9be0f624398cf774e5880a6d1a677 Mon Sep 17 00:00:00 2001
+From: Lee Jackson <lee.jackson@arducam.com>
+Date: Wed, 10 Jan 2024 09:06:16 +0800
+Subject: [PATCH] drivers: media: arducam_64mp: Add V4L2_CID_LINK_FREQ control
+
+Add V4L2_CID_LINK_FREQ as a read-only control with a value of 456 Mhz.
+This will be used by the CFE driver to corretly setup the DPHY timing
+parameters in the CSI-2 block.
+
+Signed-off-by: Lee Jackson <lee.jackson@arducam.com>
+---
+ drivers/media/i2c/arducam_64mp.c | 16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/drivers/media/i2c/arducam_64mp.c
++++ b/drivers/media/i2c/arducam_64mp.c
+@@ -143,6 +143,10 @@ struct arducam_64mp_mode {
+ struct arducam_64mp_reg_list reg_list;
+ };
+
++static const s64 arducam_64mp_link_freq_menu[] = {
++ ARDUCAM_64MP_DEFAULT_LINK_FREQ,
++};
++
+ static const struct arducam_64mp_reg mode_common_regs[] = {
+ {0x0100, 0x00},
+ {0x0136, 0x18},
+@@ -2272,9 +2276,11 @@ static int arducam_64mp_init_controls(st
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ struct i2c_client *client = v4l2_get_subdevdata(&arducam_64mp->sd);
+ struct v4l2_fwnode_device_properties props;
++ struct v4l2_ctrl *link_freq;
+ unsigned int i;
+ int ret;
+ u8 test_pattern_max;
++ u8 link_freq_max;
+
+ ctrl_hdlr = &arducam_64mp->ctrl_handler;
+ ret = v4l2_ctrl_handler_init(ctrl_hdlr, 16);
+@@ -2292,6 +2298,16 @@ static int arducam_64mp_init_controls(st
+ ARDUCAM_64MP_PIXEL_RATE, 1,
+ ARDUCAM_64MP_PIXEL_RATE);
+
++ /* LINK_FREQ is also read only */
++ link_freq_max = ARRAY_SIZE(arducam_64mp_link_freq_menu) - 1;
++ link_freq =
++ v4l2_ctrl_new_int_menu(ctrl_hdlr, &arducam_64mp_ctrl_ops,
++ V4L2_CID_LINK_FREQ,
++ link_freq_max, 0,
++ arducam_64mp_link_freq_menu);
++ if (link_freq)
++ link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
++
+ /*
+ * Create the controls here, but mode specific limits are setup
+ * in the arducam_64mp_set_framing_limits() call below.
--- /dev/null
+From 7af1e18d2e90ec64f7cb78cdb5163b63b9fbb056 Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Wed, 3 Jan 2024 11:45:04 +0800
+Subject: [PATCH] drivers/gpu/drm/panel:Modify the DSI mode to fix the problem
+ that 7.9inch cannot be displayed
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -340,9 +340,8 @@ static int ws_panel_probe(struct i2c_cli
+ */
+ drm_panel_add(&ts->base);
+
+- ts->dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+- MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+- MIPI_DSI_MODE_LPM);
++ ts->dsi->mode_flags = MIPI_DSI_MODE_VIDEO_HSE | MIPI_DSI_MODE_VIDEO |
++ MIPI_DSI_CLOCK_NON_CONTINUOUS;
+ ts->dsi->format = MIPI_DSI_FMT_RGB888;
+ ts->dsi->lanes = 2;
+
--- /dev/null
+From aec3f9b6058448c54b74349f16b21185148a8c6e Mon Sep 17 00:00:00 2001
+From: eng33 <eng33@waveshare.com>
+Date: Wed, 3 Jan 2024 11:46:50 +0800
+Subject: [PATCH] drivers/gpu/drm/panel:Modified the timing of 11.9inch to fix
+ the issue that 11.9inch was displayed abnormally
+
+Signed-off-by: eng33 <eng33@waveshare.com>
+---
+ drivers/gpu/drm/panel/panel-waveshare-dsi.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/panel/panel-waveshare-dsi.c
++++ b/drivers/gpu/drm/panel/panel-waveshare-dsi.c
+@@ -131,7 +131,7 @@ static const struct drm_display_mode ws_
+ .hdisplay = 320,
+ .hsync_start = 320 + 60,
+ .hsync_end = 320 + 60 + 60,
+- .htotal = 320 + 60 + 60 + 120,
++ .htotal = 320 + 60 + 60 + 60,
+ .vdisplay = 1480,
+ .vsync_start = 1480 + 60,
+ .vsync_end = 1480 + 60 + 60,
--- /dev/null
+From a14da0fdb0e052a672c269df7f18c9de698bd827 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Thu, 4 Jan 2024 15:02:42 +0000
+Subject: [PATCH] drm/vc4: Add 2712 support to vc4_plane_async_set_fb
+
+vc4_plane_async_set_fb directly overwrites the plane address in
+the dlist entry, but hadn't been updated for the GEN6 / 2712
+dlist format, corrupting the address in the process.
+
+Add support for the 2712 dlist format to the function.
+
+Fixes: 1ab1fbbb7e76 ("drm/vc4: hvs: Support BCM2712 HVS")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 46 +++++++++++++++++++++++----------
+ 1 file changed, 33 insertions(+), 13 deletions(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -1865,7 +1865,7 @@ static int vc6_plane_mode_set(struct drm
+ * The UPM buffer will be allocated in
+ * vc6_plane_allocate_upm().
+ */
+- VC4_SET_FIELD(upper_32_bits(paddr) & 0xf,
++ VC4_SET_FIELD(upper_32_bits(paddr) & 0xff,
+ SCALER6_PTR0_UPPER_ADDR));
+
+ /* Pointer Word 1 */
+@@ -2068,7 +2068,8 @@ void vc4_plane_async_set_fb(struct drm_p
+ {
+ struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
+ struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
+- uint32_t addr;
++ struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
++ dma_addr_t dma_addr = bo->dma_addr + fb->offsets[0];
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+@@ -2078,19 +2079,38 @@ void vc4_plane_async_set_fb(struct drm_p
+ * because this is only called on the primary plane.
+ */
+ WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
+- addr = bo->dma_addr + fb->offsets[0];
+
+- /* Write the new address into the hardware immediately. The
+- * scanout will start from this address as soon as the FIFO
+- * needs to refill with pixels.
+- */
+- writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
++ if (vc4->gen == VC4_GEN_6) {
++ u32 value;
+
+- /* Also update the CPU-side dlist copy, so that any later
+- * atomic updates that don't do a new modeset on our plane
+- * also use our updated address.
+- */
+- vc4_state->dlist[vc4_state->ptr0_offset[0]] = addr;
++ value = vc4_state->dlist[vc4_state->ptr0_offset[0]] &
++ ~SCALER6_PTR0_UPPER_ADDR_MASK;
++ value |= VC4_SET_FIELD(upper_32_bits(dma_addr) & 0xff,
++ SCALER6_PTR0_UPPER_ADDR);
++
++ writel(value, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
++ vc4_state->dlist[vc4_state->ptr0_offset[0]] = value;
++
++ value = lower_32_bits(dma_addr);
++ writel(value, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0] + 1]);
++ vc4_state->dlist[vc4_state->ptr0_offset[0] + 1] = value;
++ } else {
++ u32 addr;
++
++ addr = (u32)dma_addr;
++
++ /* Write the new address into the hardware immediately. The
++ * scanout will start from this address as soon as the FIFO
++ * needs to refill with pixels.
++ */
++ writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset[0]]);
++
++ /* Also update the CPU-side dlist copy, so that any later
++ * atomic updates that don't do a new modeset on our plane
++ * also use our updated address.
++ */
++ vc4_state->dlist[vc4_state->ptr0_offset[0]] = addr;
++ }
+
+ drm_dev_exit(idx);
+ }
--- /dev/null
+From bfe927647253ab3a86be16320baa1579518c6786 Mon Sep 17 00:00:00 2001
+From: Dave Stevenson <dave.stevenson@raspberrypi.com>
+Date: Wed, 17 Jan 2024 17:00:35 +0000
+Subject: [PATCH] drm/vc4: Fix atomic_async_check to call the right mode_set
+ function
+
+vc4_plane_atomic_async_check was always calling vc4_plane_mode_set
+to validate and generate the dlist for the check. If async_check
+decided it had to fall back to a sync commit, then this GEN4/5
+dlist could get used on GEN6.
+
+Call either vc4_plane_mode_set or vc6_plane_mode_set as appropriate.
+
+Fixes: 1ab1fbbb7e76 ("drm/vc4: hvs: Support BCM2712 HVS")
+Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
+---
+ drivers/gpu/drm/vc4/vc4_plane.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/vc4/vc4_plane.c
++++ b/drivers/gpu/drm/vc4/vc4_plane.c
+@@ -2194,11 +2194,15 @@ static int vc4_plane_atomic_async_check(
+ {
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
+ plane);
++ struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
+ struct vc4_plane_state *old_vc4_state, *new_vc4_state;
+ int ret;
+ u32 i;
+
+- ret = vc4_plane_mode_set(plane, new_plane_state);
++ if (vc4->gen >= VC4_GEN_6)
++ ret = vc6_plane_mode_set(plane, new_plane_state);
++ else
++ ret = vc4_plane_mode_set(plane, new_plane_state);
+ if (ret)
+ return ret;
+
--- /dev/null
+From 04b88e1a8b867b045bc90d997e8e0260b00dbb15 Mon Sep 17 00:00:00 2001
+From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+Date: Thu, 11 Jan 2024 12:13:03 +0000
+Subject: [PATCH] drm: rp1: rp1-vec: Allow non-standard modes with various
+ crops
+
+Tweak sync timings in the advertised modelines.
+
+Accept other, custom modes, provided they fit within the active
+area of one of the existing hardware-supported TV modes.
+
+Instead of always padding symmetrically, try to respect the user's
+[hv]sync_start values, allowing the image to be shifted around
+the screen (to fine-tune overscan correction).
+
+Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
+---
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c | 66 ++++++++++++---------
+ drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c | 74 ++++++++++++++++++------
+ 2 files changed, 96 insertions(+), 44 deletions(-)
+
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec.c
+@@ -19,7 +19,6 @@
+ #include <linux/list.h>
+ #include <linux/platform_device.h>
+ #include <linux/clk.h>
+-#include <linux/printk.h>
+ #include <linux/console.h>
+ #include <linux/debugfs.h>
+ #include <linux/uaccess.h>
+@@ -208,26 +207,26 @@ static void rp1vec_connector_destroy(str
+ static const struct drm_display_mode rp1vec_modes[4] = {
+ { /* Full size 525/60i with Rec.601 pixel rate */
+ DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500,
+- 720, 720 + 14, 720 + 14 + 64, 858, 0,
+- 480, 480 + 7, 480 + 7 + 6, 525, 0,
++ 720, 720 + 16, 720 + 16 + 64, 858, 0,
++ 480, 480 + 6, 480 + 6 + 6, 525, 0,
+ DRM_MODE_FLAG_INTERLACE)
+ },
+ { /* Cropped and horizontally squashed to be TV-safe */
+ DRM_MODE("704x432i", DRM_MODE_TYPE_DRIVER, 15429,
+- 704, 704 + 72, 704 + 72 + 72, 980, 0,
+- 432, 432 + 31, 432 + 31 + 6, 525, 0,
++ 704, 704 + 76, 704 + 76 + 72, 980, 0,
++ 432, 432 + 30, 432 + 30 + 6, 525, 0,
+ DRM_MODE_FLAG_INTERLACE)
+ },
+ { /* Full size 625/50i with Rec.601 pixel rate */
+ DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500,
+ 720, 720 + 20, 720 + 20 + 64, 864, 0,
+- 576, 576 + 4, 576 + 4 + 6, 625, 0,
++ 576, 576 + 5, 576 + 5 + 5, 625, 0,
+ DRM_MODE_FLAG_INTERLACE)
+ },
+ { /* Cropped and squashed, for square(ish) pixels */
+ DRM_MODE("704x512i", DRM_MODE_TYPE_DRIVER, 15429,
+ 704, 704 + 80, 704 + 80 + 72, 987, 0,
+- 512, 512 + 36, 512 + 36 + 6, 625, 0,
++ 512, 512 + 37, 512 + 37 + 5, 625, 0,
+ DRM_MODE_FLAG_INTERLACE)
+ }
+ };
+@@ -298,27 +297,42 @@ static enum drm_mode_status rp1vec_mode_
+ const struct drm_display_mode *mode)
+ {
+ /*
+- * Check the mode roughly matches one of our standard modes
+- * (optionally half-height and progressive). Ignore H/V sync
+- * timings which for interlaced TV are approximate at best.
++ * Check the mode roughly matches something we can generate.
++ * The hardware driver is very prescriptive about pixel clocks,
++ * line and frame durations, but we'll tolerate rounding errors.
++ * Within each hardware mode, allow image size and position to vary
++ * (to fine-tune overscan correction or emulate retro devices).
++ * Don't check sync timings here: the HW driver will sanitize them.
+ */
+- int i, prog;
+
+- prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
+-
+- for (i = 0; i < ARRAY_SIZE(rp1vec_modes); i++) {
+- const struct drm_display_mode *ref = rp1vec_modes + i;
++ int prog = !(mode->flags & DRM_MODE_FLAG_INTERLACE);
++ int vtotal_full = mode->vtotal << prog;
++ int vdisplay_full = mode->vdisplay << prog;
++
++ /* Reject very small frames */
++ if (vtotal_full < 256 || mode->hdisplay < 256)
++ return MODE_BAD;
++
++ /* Check lines, frame period (ms) and vertical size limit */
++ if (vtotal_full >= 524 && vtotal_full <= 526 &&
++ mode->htotal * vtotal_full > 33 * mode->clock &&
++ mode->htotal * vtotal_full < 34 * mode->clock &&
++ vdisplay_full <= 480)
++ goto vgood;
++ if (vtotal_full >= 624 && vtotal_full <= 626 &&
++ mode->htotal * vtotal_full > 39 * mode->clock &&
++ mode->htotal * vtotal_full < 41 * mode->clock &&
++ vdisplay_full <= 576)
++ goto vgood;
++ return MODE_BAD;
+
+- if (mode->hdisplay == ref->hdisplay &&
+- mode->vdisplay == (ref->vdisplay >> prog) &&
+- mode->clock + 2 >= ref->clock &&
+- mode->clock <= ref->clock + 2 &&
+- mode->htotal + 2 >= ref->htotal &&
+- mode->htotal <= ref->htotal + 2 &&
+- mode->vtotal + 2 >= (ref->vtotal >> prog) &&
+- mode->vtotal <= (ref->vtotal >> prog) + 2)
+- return MODE_OK;
+- }
++vgood:
++ /* Check pixel rate (kHz) and horizontal size limit */
++ if (mode->clock == 13500 && mode->hdisplay <= 720)
++ return MODE_OK;
++ if (mode->clock >= 15428 && mode->clock <= 15429 &&
++ mode->hdisplay <= 800)
++ return MODE_OK;
+ return MODE_BAD;
+ }
+
+@@ -440,7 +454,7 @@ static int rp1vec_platform_probe(struct
+ ret = drmm_mode_config_init(drm);
+ if (ret)
+ goto err_free_drm;
+- drm->mode_config.max_width = 768;
++ drm->mode_config.max_width = 800;
+ drm->mode_config.max_height = 576;
+ drm->mode_config.fb_base = 0;
+ drm->mode_config.preferred_depth = 32;
+--- a/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
++++ b/drivers/gpu/drm/rp1/rp1-vec/rp1_vec_hw.c
+@@ -11,7 +11,6 @@
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+-#include <linux/printk.h>
+ #include <drm/drm_fourcc.h>
+ #include <drm/drm_print.h>
+ #include <drm/drm_vblank.h>
+@@ -82,13 +81,13 @@ static const struct rp1vec_ipixfmt my_fo
+ /*
+ * Hardware mode descriptions (@ 108 MHz clock rate).
+ * These rely largely on "canned" register settings.
+- * TODO: Port the generating software from FP to integer,
+- * or better factorize the differences between modes.
+ */
+
+ struct rp1vec_hwmode {
+- u16 total_cols; /* active columns, plus padding for filter context */
++ u16 total_cols; /* max active columns incl. padding and windowing */
+ u16 rows_per_field; /* active lines per field (including partial ones) */
++ u16 ref_hfp; /* nominal (hsync_start - hdisplay) when max width */
++ u16 ref_vfp; /* nominal (vsync_start - vdisplay) when max height */
+ bool interlaced; /* set for interlaced */
+ bool first_field_odd; /* set for interlaced and 30fps */
+ u32 yuv_scaling; /* three 10-bit fields {Y, U, V} in 2.8 format */
+@@ -103,6 +102,8 @@ static const struct rp1vec_hwmode rp1vec
+ {
+ .total_cols = 724,
+ .rows_per_field = 240,
++ .ref_hfp = 12,
++ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .yuv_scaling = 0x1071d0cf,
+@@ -118,6 +119,8 @@ static const struct rp1vec_hwmode rp1vec
+ }, {
+ .total_cols = 815,
+ .rows_per_field = 240,
++ .ref_hfp = 16,
++ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .yuv_scaling = 0x1c131962,
+@@ -135,6 +138,8 @@ static const struct rp1vec_hwmode rp1vec
+ {
+ .total_cols = 724,
+ .rows_per_field = 243,
++ .ref_hfp = 12,
++ .ref_vfp = 3,
+ .interlaced = true,
+ .first_field_odd = true,
+ .yuv_scaling = 0x1071d0cf,
+@@ -150,6 +155,8 @@ static const struct rp1vec_hwmode rp1vec
+ }, {
+ .total_cols = 815,
+ .rows_per_field = 243,
++ .ref_hfp = 16,
++ .ref_vfp = 3,
+ .interlaced = true,
+ .first_field_odd = true,
+ .yuv_scaling = 0x1c131962,
+@@ -170,6 +177,8 @@ static const struct rp1vec_hwmode rp1vec
+ {
+ .total_cols = 724,
+ .rows_per_field = 288,
++ .ref_hfp = 16,
++ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .yuv_scaling = 0x11c1f8e0,
+@@ -185,6 +194,8 @@ static const struct rp1vec_hwmode rp1vec
+ }, {
+ .total_cols = 804,
+ .rows_per_field = 288,
++ .ref_hfp = 24,
++ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .yuv_scaling = 0x1e635d7f,
+@@ -202,6 +213,8 @@ static const struct rp1vec_hwmode rp1vec
+ {
+ .total_cols = 724,
+ .rows_per_field = 288,
++ .ref_hfp = 16,
++ .ref_vfp = 5,
+ .interlaced = true,
+ .first_field_odd = false,
+ .yuv_scaling = 0x11c1f8e0,
+@@ -217,6 +230,8 @@ static const struct rp1vec_hwmode rp1vec
+ }, {
+ .total_cols = 804,
+ .rows_per_field = 288,
++ .ref_hfp = 24,
++ .ref_vfp = 5,
+ .interlaced = true,
+ .first_field_odd = false,
+ .yuv_scaling = 0x1e635d7f,
+@@ -237,6 +252,8 @@ static const struct rp1vec_hwmode rp1vec
+ {
+ .total_cols = 724,
+ .rows_per_field = 240,
++ .ref_hfp = 12,
++ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .yuv_scaling = 0x11c1f8e0,
+@@ -252,6 +269,8 @@ static const struct rp1vec_hwmode rp1vec
+ }, {
+ .total_cols = 815,
+ .rows_per_field = 240,
++ .ref_hfp = 16,
++ .ref_vfp = 2,
+ .interlaced = false,
+ .first_field_odd = false,
+ .yuv_scaling = 0x1e635d7f,
+@@ -269,6 +288,8 @@ static const struct rp1vec_hwmode rp1vec
+ {
+ .total_cols = 724,
+ .rows_per_field = 243,
++ .ref_hfp = 12,
++ .ref_vfp = 3,
+ .interlaced = true,
+ .first_field_odd = true,
+ .yuv_scaling = 0x11c1f8e0,
+@@ -284,6 +305,8 @@ static const struct rp1vec_hwmode rp1vec
+ }, {
+ .total_cols = 815,
+ .rows_per_field = 243,
++ .ref_hfp = 16,
++ .ref_vfp = 3,
+ .interlaced = true,
+ .first_field_odd = true,
+ .yuv_scaling = 0x1e635d7f,
+@@ -308,11 +331,11 @@ void rp1vec_hw_setup(struct rp1_vec *vec
+ {
+ unsigned int i, mode_family, mode_ilaced, mode_narrow;
+ const struct rp1vec_hwmode *hwm;
+- unsigned int w, h;
++ int w, h, hpad, vpad;
+
+ /* Pick the appropriate "base" mode, which we may modify */
+ mode_ilaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
+- if (mode->vtotal > 263 * (1 + mode_ilaced))
++ if (mode->vtotal >= 272 * (1 + mode_ilaced))
+ mode_family = 1;
+ else
+ mode_family = (tvstd == RP1VEC_TVSTD_PAL_M || tvstd == RP1VEC_TVSTD_PAL60) ? 2 : 0;
+@@ -326,13 +349,28 @@ void rp1vec_hw_setup(struct rp1_vec *vec
+ tvstd, rp1vec_tvstd_names[tvstd]);
+
+ w = mode->hdisplay;
+- h = mode->vdisplay;
+- if (mode_ilaced)
+- h >>= 1;
++ h = mode->vdisplay >> mode_ilaced;
+ if (w > hwm->total_cols)
+ w = hwm->total_cols;
+ if (h > hwm->rows_per_field)
+- w = hwm->rows_per_field;
++ h = hwm->rows_per_field;
++
++ /*
++ * Add padding so a framebuffer with the given dimensions and
++ * [hv]sync_start can be displayed in the chosen hardware mode.
++ *
++ * |<----- mode->hsync_start ----->|
++ * |<------ w ------>| |
++ * | | >|--|< ref_hfp
++ * |<- hpad ->|
++ * |<------------ total_cols ----------->|
++ * ________FRAMEBUFFERCONTENTS__________
++ * ' `--\____/-<\/\/\>-'
++ */
++ hpad = max(0, mode->hsync_start - hwm->ref_hfp - w);
++ hpad = min(hpad, hwm->total_cols - w);
++ vpad = max(0, ((mode->vsync_start - hwm->ref_vfp) >> mode_ilaced) - h);
++ vpad = min(vpad, hwm->rows_per_field - h);
+
+ /* Configure the hardware */
+ VEC_WRITE(VEC_APB_TIMEOUT, 0x38);
+@@ -347,18 +385,18 @@ void rp1vec_hw_setup(struct rp1_vec *vec
+ BITS(VEC_DMA_AREA_ROWS_PER_FIELD_MINUS1, h - 1));
+ VEC_WRITE(VEC_YUV_SCALING, hwm->yuv_scaling);
+ VEC_WRITE(VEC_BACK_PORCH,
+- BITS(VEC_BACK_PORCH_HBP_MINUS1, (hwm->total_cols - w - 1) >> 1) |
+- BITS(VEC_BACK_PORCH_VBP_MINUS1, (hwm->rows_per_field - h - 1) >> 1));
++ BITS(VEC_BACK_PORCH_HBP_MINUS1, hwm->total_cols - w - hpad - 1) |
++ BITS(VEC_BACK_PORCH_VBP_MINUS1, hwm->rows_per_field - h - vpad - 1));
+ VEC_WRITE(VEC_FRONT_PORCH,
+- BITS(VEC_FRONT_PORCH_HFP_MINUS1, (hwm->total_cols - w - 2) >> 1) |
+- BITS(VEC_FRONT_PORCH_VFP_MINUS1, (hwm->rows_per_field - h - 2) >> 1));
++ BITS(VEC_FRONT_PORCH_HFP_MINUS1, hpad - 1) |
++ BITS(VEC_FRONT_PORCH_VFP_MINUS1, vpad - 1));
+ VEC_WRITE(VEC_MODE,
+ BITS(VEC_MODE_HIGH_WATER, 0xE0) |
+ BITS(VEC_MODE_ALIGN16, !((w | mode->hdisplay) & 15)) |
+- BITS(VEC_MODE_VFP_EN, (hwm->rows_per_field > h + 1)) |
+- BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h)) |
+- BITS(VEC_MODE_HFP_EN, (hwm->total_cols > w + 1)) |
+- BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w)) |
++ BITS(VEC_MODE_VFP_EN, (vpad > 0)) |
++ BITS(VEC_MODE_VBP_EN, (hwm->rows_per_field > h + vpad)) |
++ BITS(VEC_MODE_HFP_EN, (hpad > 0)) |
++ BITS(VEC_MODE_HBP_EN, (hwm->total_cols > w + hpad)) |
+ BITS(VEC_MODE_FIELDS_PER_FRAME_MINUS1, hwm->interlaced) |
+ BITS(VEC_MODE_FIRST_FIELD_ODD, hwm->first_field_odd));
+ for (i = 0; i < ARRAY_SIZE(hwm->back_end_regs); ++i) {
--- /dev/null
+From ab8ba584f91576c41d921076221ceb48e2930ae0 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 18 Jan 2024 11:08:03 +0000
+Subject: [PATCH] ARM: pl011: Add rs485 to the RP1 support
+
+pl011_axi_probe, added for RP1 support, lacks the rs485 additions that
+appeared during its development.
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/tty/serial/amba-pl011.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/tty/serial/amba-pl011.c
++++ b/drivers/tty/serial/amba-pl011.c
+@@ -3026,6 +3026,8 @@ static int pl011_axi_probe(struct platfo
+ uap->port.iotype = vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
+ uap->port.irq = irq;
+ uap->port.ops = &amba_pl011_pops;
++ uap->port.rs485_config = pl011_rs485_config;
++ uap->port.rs485_supported = pl011_rs485_supported;
+
+ snprintf(uap->type, sizeof(uap->type), "PL011 AXI");
+
--- /dev/null
+From 3bb5880ab3dd31f75c07c3c33bf29c5d469b28f3 Mon Sep 17 00:00:00 2001
+From: Phil Elwell <phil@raspberrypi.com>
+Date: Thu, 18 Jan 2024 15:41:21 +0000
+Subject: [PATCH] fixup! irqchip: irq-bcm2712-mip: Support for 2712's MIP
+
+Use irq_domain_add_hierarchy so that the root pointer is initialised
+correctly. Failure to do so leads to (at least) lockdep warnings when
+they are enabled.
+
+See: https://github.com/raspberrypi/linux/issues/5866
+
+Signed-off-by: Phil Elwell <phil@raspberrypi.com>
+---
+ drivers/irqchip/irq-bcm2712-mip.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+--- a/drivers/irqchip/irq-bcm2712-mip.c
++++ b/drivers/irqchip/irq-bcm2712-mip.c
+@@ -209,16 +209,14 @@ static int mip_init_domains(struct mip_p
+ return -ENXIO;
+ }
+
+- middle_domain = irq_domain_add_tree(NULL,
+- &mip_irq_domain_ops,
+- priv);
++ middle_domain = irq_domain_add_hierarchy(gic_domain, 0, 0, NULL,
++ &mip_irq_domain_ops,
++ priv);
+ if (!middle_domain) {
+ pr_err("Failed to create the MIP middle domain\n");
+ return -ENOMEM;
+ }
+
+- middle_domain->parent = gic_domain;
+-
+ msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(node),
+ &mip_msi_domain_info,
+ middle_domain);
--- a/drivers/char/hw_random/iproc-rng200.c
+++ b/drivers/char/hw_random/iproc-rng200.c
-@@ -252,6 +252,7 @@ static int iproc_rng200_probe(struct pla
+@@ -253,6 +253,7 @@ static int iproc_rng200_probe(struct pla
priv->rng.name = pdev->name;
priv->rng.cleanup = iproc_rng200_cleanup;
# CONFIG_BAYCOM_SER_FDX is not set
# CONFIG_BAYCOM_SER_HDX is not set
# CONFIG_BCACHE is not set
+# CONFIG_BCM2712_MIP is not set
# CONFIG_BCM47XX is not set
# CONFIG_BCM54140_PHY is not set
# CONFIG_BCM63XX is not set
# CONFIG_COMMON_CLK_PWM is not set
# CONFIG_COMMON_CLK_PXA is not set
# CONFIG_COMMON_CLK_QCOM is not set
+# CONFIG_COMMON_CLK_RP1 is not set
+# CONFIG_COMMON_CLK_RP1_SDIO is not set
# CONFIG_COMMON_CLK_RS9_PCIE is not set
# CONFIG_COMMON_CLK_SI514 is not set
# CONFIG_COMMON_CLK_SI5341 is not set
# CONFIG_DRM_RCAR_USE_LVDS is not set
# CONFIG_DRM_RCAR_USE_MIPI_DSI is not set
# CONFIG_DRM_ROCKCHIP is not set
+# CONFIG_DRM_RP1_DPI is not set
+# CONFIG_DRM_RP1_DSI is not set
+# CONFIG_DRM_RP1_VEC is not set
# CONFIG_DRM_SII902X is not set
# CONFIG_DRM_SII9234 is not set
# CONFIG_DRM_SIL_SII8620 is not set
# CONFIG_FB_PXA is not set
# CONFIG_FB_RADEON is not set
# CONFIG_FB_RIVA is not set
+# CONFIG_FB_RPISENSE is not set
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_S3 is not set
# CONFIG_FB_SAVAGE is not set
# CONFIG_GPIO_AMDPT is not set
# CONFIG_GPIO_AMD_FCH is not set
# CONFIG_GPIO_BCM_KONA is not set
+# CONFIG_GPIO_BRCMSTB is not set
# CONFIG_GPIO_BT8XX is not set
# CONFIG_GPIO_CADENCE is not set
# CONFIG_GPIO_CASCADE is not set
# CONFIG_INPUT_POWERMATE is not set
# CONFIG_INPUT_PWM_BEEPER is not set
# CONFIG_INPUT_PWM_VIBRA is not set
+# CONFIG_INPUT_RASPBERRYPI_BUTTON is not set
# CONFIG_INPUT_REGULATOR_HAPTIC is not set
# CONFIG_INPUT_SOC_BUTTON_ARRAY is not set
# CONFIG_INPUT_SPARSEKMAP is not set
# CONFIG_MFD_PM8921_CORE is not set
# CONFIG_MFD_PM8XXX is not set
# CONFIG_MFD_QCOM_PM8008 is not set
+# CONFIG_MFD_RASPBERRYPI_POE_HAT is not set
# CONFIG_MFD_RC5T583 is not set
# CONFIG_MFD_RDC321X is not set
# CONFIG_MFD_RETU is not set
# CONFIG_MFD_ROHM_BD71828 is not set
# CONFIG_MFD_ROHM_BD718XX is not set
# CONFIG_MFD_ROHM_BD957XMUF is not set
+# CONFIG_MFD_RP1 is not set
+# CONFIG_MFD_RPISENSE_CORE is not set
# CONFIG_MFD_RSMU_I2C is not set
# CONFIG_MFD_RSMU_SPI is not set
# CONFIG_MFD_RT4831 is not set
# CONFIG_PHYLIB is not set
# CONFIG_PHYLIB_LEDS is not set
# CONFIG_PHYS_ADDR_T_64BIT is not set
+# CONFIG_PHY_BRCM_USB is not set
# CONFIG_PHY_CADENCE_DP is not set
# CONFIG_PHY_CADENCE_DPHY is not set
# CONFIG_PHY_CADENCE_DPHY_RX is not set
# CONFIG_PINCTRL is not set
# CONFIG_PINCTRL_AMD is not set
# CONFIG_PINCTRL_AXP209 is not set
+# CONFIG_PINCTRL_BCM2712 is not set
# CONFIG_PINCTRL_CEDARFORK is not set
# CONFIG_PINCTRL_CY8C95X0 is not set
# CONFIG_PINCTRL_EXYNOS is not set
# CONFIG_PINCTRL_MTK_V2 is not set
# CONFIG_PINCTRL_OCELOT is not set
# CONFIG_PINCTRL_PISTACHIO is not set
+# CONFIG_PINCTRL_RP1 is not set
# CONFIG_PINCTRL_SC7280 is not set
# CONFIG_PINCTRL_SC8180X is not set
# CONFIG_PINCTRL_SDX55 is not set
# CONFIG_PWM_MEDIATEK is not set
# CONFIG_PWM_PCA9685 is not set
# CONFIG_PWM_RASPBERRYPI_POE is not set
+# CONFIG_PWM_RP1 is not set
# CONFIG_PWM_XILINX is not set
CONFIG_PWRSEQ_EMMC=y
# CONFIG_PWRSEQ_SD8787 is not set
# CONFIG_RANDSTRUCT_NONE is not set
# CONFIG_RAPIDIO is not set
# CONFIG_RAS is not set
+# CONFIG_RASPBERRYPI_GPIOMEM is not set
# CONFIG_RBTREE_TEST is not set
# CONFIG_RCU_BOOST is not set
CONFIG_RCU_CPU_STALL_TIMEOUT=60
# CONFIG_RENESAS_PHY is not set
# CONFIG_RESET_ATH79 is not set
# CONFIG_RESET_BERLIN is not set
+# CONFIG_RESET_BRCMSTB is not set
# CONFIG_RESET_BRCMSTB_RESCAL is not set
# CONFIG_RESET_CONTROLLER is not set
# CONFIG_RESET_IMX7 is not set
# CONFIG_SENSORS_Q54SJ108A2 is not set
# CONFIG_SENSORS_RM3100_I2C is not set
# CONFIG_SENSORS_RM3100_SPI is not set
+# CONFIG_SENSORS_RP1_ADC is not set
# CONFIG_SENSORS_SBRMI is not set
# CONFIG_SENSORS_SBTSI is not set
# CONFIG_SENSORS_SCH5627 is not set
# CONFIG_STMMAC_PCI is not set
# CONFIG_STMMAC_PLATFORM is not set
# CONFIG_STMMAC_SELFTESTS is not set
+# CONFIG_STMPE_ADC is not set
# CONFIG_STM_DUMMY is not set
# CONFIG_STM_SOURCE_CONSOLE is not set
CONFIG_STP=y
# CONFIG_VIDEO_BT848 is not set
# CONFIG_VIDEO_BT856 is not set
# CONFIG_VIDEO_BT866 is not set
+# CONFIG_VIDEO_BU64754 is not set
# CONFIG_VIDEO_CADENCE is not set
# CONFIG_VIDEO_CADENCE_CSI2RX is not set
# CONFIG_VIDEO_CADENCE_CSI2TX is not set
# CONFIG_VIDEO_OV5675 is not set
# CONFIG_VIDEO_OV5693 is not set
# CONFIG_VIDEO_OV5695 is not set
+# CONFIG_VIDEO_OV64A40 is not set
# CONFIG_VIDEO_OV6650 is not set
# CONFIG_VIDEO_OV7251 is not set
# CONFIG_VIDEO_OV7640 is not set