From: Marty Jones Date: Thu, 18 Jan 2024 21:23:52 +0000 (-0500) Subject: bcm27xx: update 6.1 patches to latest version X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=2e715fb4fc8014eefddf5d9232105740f10e06ba;p=openwrt%2Fstaging%2Fjow.git bcm27xx: update 6.1 patches to latest version Add support for BCM2712 (Raspberry Pi 5). https://github.com/raspberrypi/linux/commit/3bb5880ab3dd31f75c07c3c33bf29c5d469b28f3 Patches were generated from the diff between linux kernel branch linux-6.1.y and rpi-6.1.y from raspberry pi kernel source: - git format-patch linux-6.1.y...rpi-6.1.y Build system: x86_64 Build-tested: bcm2708, bcm2709, bcm2710, bcm2711 Run-tested: bcm2710/RPi3B, bcm2711/RPi4B Signed-off-by: Marty Jones [Remove applied and reverted patches, squash patches and config commits] Signed-off-by: Álvaro Fernández Rojas --- diff --git a/target/linux/bcm27xx/bcm2708/config-6.1 b/target/linux/bcm27xx/bcm2708/config-6.1 index c5d604e6bc..cef17940a3 100644 --- a/target/linux/bcm27xx/bcm2708/config-6.1 +++ b/target/linux/bcm27xx/bcm2708/config-6.1 @@ -30,7 +30,6 @@ CONFIG_ARM_UNWIND=y 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 @@ -151,7 +150,6 @@ CONFIG_FB_CFB_COPYAREA=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 @@ -239,8 +237,6 @@ CONFIG_MDIO_DEVRES=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 @@ -266,6 +262,7 @@ CONFIG_NO_HZ=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 @@ -296,7 +293,6 @@ CONFIG_PM_GENERIC_DOMAINS_OF=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 @@ -306,6 +302,7 @@ CONFIG_PWM_BCM2835=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 diff --git a/target/linux/bcm27xx/bcm2709/config-6.1 b/target/linux/bcm27xx/bcm2709/config-6.1 index ec2e7f3fa6..15bbaabd74 100644 --- a/target/linux/bcm27xx/bcm2709/config-6.1 +++ b/target/linux/bcm27xx/bcm2709/config-6.1 @@ -39,7 +39,6 @@ CONFIG_ASSOCIATIVE_ARRAY=y 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 @@ -186,7 +185,6 @@ CONFIG_FB_CFB_COPYAREA=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 @@ -297,8 +295,6 @@ CONFIG_MDIO_DEVRES=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 @@ -332,6 +328,7 @@ CONFIG_NO_HZ_COMMON=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 @@ -374,7 +371,6 @@ CONFIG_PM_GENERIC_DOMAINS_SLEEP=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 @@ -387,6 +383,7 @@ CONFIG_PWM_SYSFS=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 diff --git a/target/linux/bcm27xx/bcm2710/config-6.1 b/target/linux/bcm27xx/bcm2710/config-6.1 index b0abd49c7f..50b3f87d7d 100644 --- a/target/linux/bcm27xx/bcm2710/config-6.1 +++ b/target/linux/bcm27xx/bcm2710/config-6.1 @@ -60,7 +60,6 @@ CONFIG_ASSOCIATIVE_ARRAY=y 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 @@ -152,7 +151,6 @@ CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y 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 @@ -161,8 +159,6 @@ CONFIG_CRYPTO_SHA256=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 @@ -195,7 +191,6 @@ CONFIG_FB_CFB_COPYAREA=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 @@ -299,8 +294,6 @@ CONFIG_MDIO_DEVRES=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 @@ -332,6 +325,7 @@ CONFIG_NO_HZ_COMMON=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 @@ -368,7 +362,6 @@ CONFIG_PM_GENERIC_DOMAINS_SLEEP=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 @@ -382,6 +375,7 @@ CONFIG_QUEUED_RWLOCKS=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 diff --git a/target/linux/bcm27xx/bcm2711/config-6.1 b/target/linux/bcm27xx/bcm2711/config-6.1 index 1912b653e5..0441305067 100644 --- a/target/linux/bcm27xx/bcm2711/config-6.1 +++ b/target/linux/bcm27xx/bcm2711/config-6.1 @@ -54,7 +54,6 @@ CONFIG_ASSOCIATIVE_ARRAY=y 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 @@ -150,7 +149,6 @@ CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y 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 @@ -159,8 +157,6 @@ CONFIG_CRYPTO_SHA256=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 @@ -194,7 +190,6 @@ CONFIG_FB_CFB_COPYAREA=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 @@ -300,8 +295,6 @@ CONFIG_MDIO_DEVRES=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 @@ -333,6 +326,7 @@ CONFIG_NO_HZ_COMMON=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 @@ -372,7 +366,6 @@ CONFIG_PM_GENERIC_DOMAINS_SLEEP=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 @@ -389,6 +382,7 @@ CONFIG_QUEUED_SPINLOCKS=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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch b/target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch deleted file mode 100644 index 9a36dd1bcd..0000000000 --- a/target/linux/bcm27xx/patches-6.1/950-0177-hwrng-iproc-rng200-Add-BCM2838-support.patch +++ /dev/null @@ -1,153 +0,0 @@ -From b642f64d629df5515f3a01fc5b2e17c3fa7b404c Mon Sep 17 00:00:00 2001 -From: Stefan Wahren -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 - -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 ---- - 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) { diff --git a/target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch b/target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch deleted file mode 100644 index d4c5a0183e..0000000000 --- a/target/linux/bcm27xx/patches-6.1/950-0699-Bluetooth-hci_sync-Add-fallback-bd-address-prop.patch +++ /dev/null @@ -1,41 +0,0 @@ -From dffb648dffeab7246040a30b7d1669387d1e767e Mon Sep 17 00:00:00 2001 -From: Phil Elwell -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 ---- - 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 || diff --git a/target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch b/target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch new file mode 100644 index 0000000000..276e51d2d9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0790-media-i2c-imx219-fix-binning-and-rate_factor-for-480.patch @@ -0,0 +1,306 @@ +From 3ad8e28669e0058e3cec482a47215e50e33f2574 Mon Sep 17 00:00:00 2001 +From: Vinay Varma +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch b/target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch new file mode 100644 index 0000000000..c93ebf3e7e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0791-serial-sc16is7xx-Read-modem-line-state-at-startup.patch @@ -0,0 +1,28 @@ +From 52039b6ffb6e78c2f77319b167dceab9aa51d13f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +Signed-off-by: Phil Elwell +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch b/target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch new file mode 100644 index 0000000000..6de340c2a9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0792-drivers-media-bcm2835_unicam-Improve-frame-sequence-.patch @@ -0,0 +1,79 @@ +From 6ef818eed60db70e9caf6bdf74cc1f9943994226 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch b/target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch new file mode 100644 index 0000000000..d8307f9009 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0793-dtoverlays-Fix-pitft-28-35-overlays-for-6.1-driver-c.patch @@ -0,0 +1,41 @@ +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch b/target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch new file mode 100644 index 0000000000..2255efd988 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0795-driver-media-i2c-imx477-Re-enable-temperature-sensor.patch @@ -0,0 +1,23 @@ +From 713a7ef9d73fca0f7fed122cb854d930b7a6ba5a Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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}, diff --git a/target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch b/target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch new file mode 100644 index 0000000000..43f63a1c63 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0796-overlays-allo-katana-dac-audio-Reduce-I2C-clock.patch @@ -0,0 +1,25 @@ +From d4c3133378b377ee519ea50247339cd61221fc47 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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>; diff --git a/target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch b/target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch new file mode 100644 index 0000000000..66c147837d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0797-overlays-jedec-spi-nor-Add-speed-parameter.patch @@ -0,0 +1,308 @@ +From 76c457e7e2920342637b1955fbaadf2aae282f05 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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,= +-Params: flash-spi- Enables flash device on SPI, CS#. +- flash-fastr-spi- Enables flash device with fast read capability +- on SPI, CS#. ++Params: spi- Enable flash device on SPI, CS# ++ fastr Add fast read capability to the flash device ++ speed Maximum SPI frequency (Hz) ++ flash-spi- Same as spi- (deprecated) ++ flash-fastr-spi- Same as spi->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- - Enables flash device on SPI, CS#. + // flash-fastr-spi- - Enables flash device with fast read capability on SPI, CS#. ++// 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"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch b/target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch new file mode 100644 index 0000000000..a35e7f4d1b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0798-ALSA-pcm-fix-ELD-constraints-for-E-AC3-DTS-HD-and-ML.patch @@ -0,0 +1,137 @@ +From e866f9fc7c6dd6af1e74ce6fa50db9ba21acae5e Mon Sep 17 00:00:00 2001 +From: Matthias Reichl +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 +Link: https://lore.kernel.org/r/20230624165216.5719-1-hias@horus.com +Signed-off-by: Takashi Iwai +--- + 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 + #include ++#include + #include + #include + #include + ++#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)); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch b/target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch new file mode 100644 index 0000000000..31a351a4e8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0799-ASoC-hdmi-codec-fix-channel-info-for-compressed-form.patch @@ -0,0 +1,86 @@ +From 3f388718331b5ce2acd34730448db001759868aa Mon Sep 17 00:00:00 2001 +From: Matthias Reichl +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 +Link: https://lore.kernel.org/r/20230624165232.5751-1-hias@horus.com +Signed-off-by: Takashi Iwai +--- + 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; + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch b/target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch new file mode 100644 index 0000000000..a423b8061b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0800-media-i2c-arducam_64mp-Modify-the-line-length-of-128.patch @@ -0,0 +1,44 @@ +From 9c5a7f04cab6b020389d7c5af155b1ee7f46537d Mon Sep 17 00:00:00 2001 +From: Lee Jackson +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch b/target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch new file mode 100644 index 0000000000..475ae634ad --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0801-media-i2c-arducam_64mp-Add-8000x6000-resolution.patch @@ -0,0 +1,105 @@ +From 7b3d0124c5cf462d5be0b0d4e558002b74750911 Mon Sep 17 00:00:00 2001 +From: Lee Jackson +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch b/target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch new file mode 100644 index 0000000000..8f96b33baa --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0802-media-i2c-arducam_64mp-Add-PDAF-support.patch @@ -0,0 +1,163 @@ +From b9d2d1862aa5b798cecb87a95d970ad34a4aebc0 Mon Sep 17 00:00:00 2001 +From: Lee Jackson +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 +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch b/target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch new file mode 100644 index 0000000000..bf1646322b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0803-overlays-audremap-Document-CM4-40-41-restriction.patch @@ -0,0 +1,24 @@ +From 6f4106f7a7fdcbc03290008713915b4122988c90 Mon Sep 17 00:00:00 2001 +From: James Hughes +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) + diff --git a/target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch b/target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch new file mode 100644 index 0000000000..9265e931fa --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0804-fixup-Allow-mac-address-to-be-set-in-smsc95xx.patch @@ -0,0 +1,120 @@ +From 1d15e6a34222cc8d8eb1050e7a3e276b0348be41 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +[lukas: fix netif_dbg() indentation as well, wordsmith commit message] +Signed-off-by: Lukas Wunner + +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 + +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 +[lukas: leave 2nd function parameter unchanged, wordsmith commit message] +Signed-off-by: Lukas Wunner +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch b/target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch new file mode 100644 index 0000000000..593c95e2a2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0809-cfg80211-ship-debian-certificates-as-hex-files.patch @@ -0,0 +1,1455 @@ +From a2d2745c311baa588fb0fffbe38076294f06b7c0 Mon Sep 17 00:00:00 2001 +From: Nicolai Buchwitz +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch b/target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch new file mode 100644 index 0000000000..87097d2bbf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0810-fixup-Add-support-for-all-the-downstream-rpi-sound-c.patch @@ -0,0 +1,329 @@ +From 3ece03b1575b0c3a0989e372aaaa4557ae74dc89 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* 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 + #include + +-/* 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. + diff --git a/target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch b/target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch new file mode 100644 index 0000000000..22bca6c7f4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0815-fixup-drm-tc358762-Set-the-pre_enable_upstream_first.patch @@ -0,0 +1,21 @@ +From 2addf7045f2b4866ab819f48e4d32f5734a32134 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch b/target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch new file mode 100644 index 0000000000..82a3c9ea5b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0816-rpi-sound-cards-Fix-Codec-Zero-rate-switching.patch @@ -0,0 +1,48 @@ +From b84b8a9ad2046a855a7044b6368def01ddd5de6e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 = { diff --git a/target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch b/target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch new file mode 100644 index 0000000000..0539b19cf5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0818-overlays-Add-trickle-voltage-mv-parameter-to-RTCs.patch @@ -0,0 +1,68 @@ +From 31822340129e3c4030500d7f30ce4d19bbf9dd40 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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?", diff --git a/target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch b/target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch new file mode 100644 index 0000000000..f10c0f03b5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0819-drivers-media-imx296-Add-standby-delay-during-probe.patch @@ -0,0 +1,25 @@ +From 5fb3b300557d6a6902e7321f42fdabb8c09eef54 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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", diff --git a/target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch b/target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch new file mode 100644 index 0000000000..535511eb93 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0820-overlays-Add-bmp380-to-i2c-sensor-overlay.patch @@ -0,0 +1,78 @@ +From e1016d61e3dcb058932e8ec5072f2c4bbb05fcb7 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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,= + 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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch b/target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch new file mode 100644 index 0000000000..d5cce9a433 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0821-can-isotp-add-module-parameter-for-maximum-pdu-size.patch @@ -0,0 +1,162 @@ +From 4b729a06b15fc5ee3694dcc62346dcb718ae4290 Mon Sep 17 00:00:00 2001 +From: Oliver Hartkopp +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 +Link: https://lore.kernel.org/all/20230326115911.15094-1-socketcan@hartkopp.net +Signed-off-by: Marc Kleine-Budde +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch b/target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch new file mode 100644 index 0000000000..ee4079cd64 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0822-drivers-media-imx296-Updated-imx296-driver-for-exter.patch @@ -0,0 +1,40 @@ +From e1b03ea9e84320e6bf36a1486abaebbceadd7fc7 Mon Sep 17 00:00:00 2001 +From: Ben Benson +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 +--- + 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 + #include + ++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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch b/target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch new file mode 100644 index 0000000000..f45b68f154 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0823-media-dt-bindings-imx258-Fix-alternate-compatible-st.patch @@ -0,0 +1,26 @@ +From 74bc238e86e62109c74d8f229dc105bf3818b4a7 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch b/target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch new file mode 100644 index 0000000000..43283784dd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0825-char-broadcom-vc_mem-Fix-preprocessor-conditional.patch @@ -0,0 +1,21 @@ +From 282819aead0166af415b780241dc2def4caee7f4 Mon Sep 17 00:00:00 2001 +From: Alexander Winkowski +Date: Mon, 3 Jul 2023 18:12:01 +0000 +Subject: [PATCH] char: broadcom: vc_mem: Fix preprocessor conditional + +Signed-off-by: Alexander Winkowski +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch b/target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch new file mode 100644 index 0000000000..fe74c75e07 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0826-drivers-dwc_otg-Fix-fallthrough-warnings.patch @@ -0,0 +1,32 @@ +From ec61075a786c455444a1d5df338a41bacfce0bb1 Mon Sep 17 00:00:00 2001 +From: Alexander Winkowski +Date: Mon, 3 Jul 2023 18:23:02 +0000 +Subject: [PATCH] drivers: dwc_otg: Fix fallthrough warnings + +Signed-off-by: Alexander Winkowski +--- + 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: diff --git a/target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch b/target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch new file mode 100644 index 0000000000..edd117c9b5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0827-vc04_services-vc-sm-cma-Switch-one-bit-bitfields-to-.patch @@ -0,0 +1,61 @@ +From 2dd2f36d10961e3819ff0525ae2567e601973826 Mon Sep 17 00:00:00 2001 +From: Alexander Winkowski +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 +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch b/target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch new file mode 100644 index 0000000000..d6fd3a9f92 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0828-media-i2c-ov2311-Fix-uninitialized-variable-usage.patch @@ -0,0 +1,21 @@ +From 3333d45347d313ea589b8b8da1193d342060a946 Mon Sep 17 00:00:00 2001 +From: Alexander Winkowski +Date: Mon, 3 Jul 2023 18:36:45 +0000 +Subject: [PATCH] media: i2c: ov2311: Fix uninitialized variable usage + +Signed-off-by: Alexander Winkowski +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch b/target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch new file mode 100644 index 0000000000..035b5b0049 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0830-drm-panel-Fix-default-values-for-Waveshare-7.9-inch-.patch @@ -0,0 +1,29 @@ +From e89e7655a197d28df49da2be7e2003436cf52197 Mon Sep 17 00:00:00 2001 +From: Ignacio Larrain +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", diff --git a/target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch b/target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch new file mode 100644 index 0000000000..cf703b0029 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0831-dtoverlays-Add-i2c-bus-overrides-to-edt-ft5406-overl.patch @@ -0,0 +1,102 @@ +From 3fa2fbb7f6e60b85086e454403c5eab1af63b1aa Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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,= +@@ -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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch b/target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch new file mode 100644 index 0000000000..23953c9636 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0832-dtoverlays-Fix-README-text-for-i2c-fan.patch @@ -0,0 +1,25 @@ +From 9d9586dc0c0deecb90675bd70862fe262f7376ab Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +Date: Wed, 14 Jun 2023 14:25:21 +0100 +Subject: [PATCH] dtoverlays: Fix README text for i2c-fan + +Signed-off-by: Dave Stevenson +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch b/target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch new file mode 100644 index 0000000000..48e2fcdc74 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0833-drivers-irqchip-irq-bcm2835-Concurrency-fix.patch @@ -0,0 +1,50 @@ +From e804bd1843236a63815e9acfb1a38ebf9a28ef5b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch b/target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch new file mode 100644 index 0000000000..10b1f15d2c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0835-dtoverlays-Add-drm-option-to-piscreen-overlay.patch @@ -0,0 +1,58 @@ +From 5e54398e1b61335883dff1be46a6c8b3ca973926 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 ++ + / { + 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=",; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch b/target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch new file mode 100644 index 0000000000..0bce9f9e78 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0836-drm-ili9486-Resolve-clash-in-spi_device_id-names.patch @@ -0,0 +1,36 @@ +From f59fe2d1bd056af117eb512bb0e9210a943c6d47 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch b/target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch new file mode 100644 index 0000000000..71f1063986 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0837-input-ads7846-Add-missing-spi_device_id-strings.patch @@ -0,0 +1,50 @@ +From 50c5a8558f4aaa54a3c4f5a8c2b6053f641d94eb Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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, + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch b/target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch new file mode 100644 index 0000000000..5af23821a8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0838-staging-bcm2835-codec-Downgrade-the-level-for-a-debu.patch @@ -0,0 +1,27 @@ +From 65742d7116e89b08858fcd7d67bd521ee19ee837 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + .../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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch b/target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch new file mode 100644 index 0000000000..873077f51c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0840-gpio-fsm-Sort-functions-into-a-more-logical-order.patch @@ -0,0 +1,286 @@ +From cee471c3ada3215d6dfc53fb0f1b97548444dea7 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 : ""); +- +- 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 : ""); ++ ++ 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// diff --git a/target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch b/target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch new file mode 100644 index 0000000000..ae3c019e25 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0841-gpio_fsm-Rework-the-atomic-vs-non-atomic-split.patch @@ -0,0 +1,192 @@ +From f0061ffc98c6e027c5774e2a24ceadcfee4167ea Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 : ""); + ++ 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 : ""); ++ + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch b/target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch new file mode 100644 index 0000000000..33150551b3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0842-f2fs-fix-to-avoid-NULL-pointer-dereference-in-f2fs_i.patch @@ -0,0 +1,63 @@ +From bf9fb25f3265605572f04e5c7836bb83ee345236 Mon Sep 17 00:00:00 2001 +From: Chao Yu +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 +Signed-off-by: Jaegeuk Kim +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch b/target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch new file mode 100644 index 0000000000..a179d930df --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0846-hwrng-iproc-rng200-Add-BCM2838-support.patch @@ -0,0 +1,161 @@ +From e079555a4c68356e58249cfc041b28f6eb455bd5 Mon Sep 17 00:00:00 2001 +From: Stefan Wahren +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 + +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 +--- + 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 + #include + #include ++#include + #include + #include + +@@ -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) { diff --git a/target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch b/target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch new file mode 100644 index 0000000000..b08676253e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0847-PCI-brcmstb-Wait-for-100ms-following-PERST-deassert.patch @@ -0,0 +1,39 @@ +From 6f634d7efb8876e5953c30c0a613aaa5f575fe05 Mon Sep 17 00:00:00 2001 +From: Jim Quinlan +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 +Signed-off-by: Lorenzo Pieralisi +Acked-by: Florian Fainelli +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch b/target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch new file mode 100644 index 0000000000..19550b6fd5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0850-overlays-Add-a-sample-hat_map.patch @@ -0,0 +1,47 @@ +From cc08810f89e52337a99cc6ae5f53f08588357c5f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; ++ }; ++}; diff --git a/target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch b/target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch new file mode 100644 index 0000000000..aaf49e6a7c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0851-Revert-usb-phy-generic-Get-the-vbus-supply.patch @@ -0,0 +1,26 @@ +From 406e7dc82be6ce1b81c88b418640daeef6c2be42 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch b/target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch new file mode 100644 index 0000000000..6a5c40ef4e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0853-dts-2712-Update-for-device-tree.patch @@ -0,0 +1,7672 @@ +From 1196bf1a7736ff0ab79f5012fa84082e298031a7 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++&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 should expose the registers for the interface with DT alias ++ * gpio. ++ */ ++ ++&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 ++#include ++#include ++ ++/ { ++ 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 = , ++ , ++ , ++ ; ++ 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 = ; ++ #mbox-cells = <0>; ++ }; ++ ++ pixelvalve0: pixelvalve@7c410000 { ++ compatible = "brcm,bcm2712-pixelvalve0"; ++ reg = <0x7c410000 0x100>; ++ interrupts = ; ++ status = "disabled"; ++ }; ++ ++ pixelvalve1: pixelvalve@7c411000 { ++ compatible = "brcm,bcm2712-pixelvalve1"; ++ reg = <0x7c411000 0x100>; ++ interrupts = ; ++ status = "disabled"; ++ }; ++ ++ usb: usb@7c480000 { ++ compatible = "brcm,bcm2835-usb"; ++ reg = <0x7c480000 0x10000>; ++ interrupts = ; ++ #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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ clocks = <&clk_vpu>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ spi4: spi@7d004800 { ++ compatible = "brcm,bcm2835-spi"; ++ reg = <0x7d004800 0x0200>; ++ interrupts = ; ++ clocks = <&clk_vpu>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ spi5: spi@7d004a00 { ++ compatible = "brcm,bcm2835-spi"; ++ reg = <0x7d004a00 0x0200>; ++ interrupts = ; ++ clocks = <&clk_vpu>; ++ #address-cells = <1>; ++ #size-cells = <0>; ++ status = "disabled"; ++ }; ++ ++ spi6: spi@7d004c00 { ++ compatible = "brcm,bcm2835-spi"; ++ reg = <0x7d004c00 0x0200>; ++ interrupts = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; ++ ++ main_irq: intc@7d508400 { ++ compatible = "brcm,bcm7271-l2-intc"; ++ reg = <0x7d508400 0x10>; ++ interrupts = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ skip-init; ++ status = "disabled"; ++ }; ++ ++ aon_intr: interrupt-controller@7d510600 { ++ compatible = "brcm,bcm2711-l2-intc", "brcm,l2-intc"; ++ reg = <0x7d510600 0x30>; ++ interrupts = ; ++ 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 = ; ++ 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 = ; ++ interrupt-controller; ++ #interrupt-cells = <1>; ++ }; ++ ++ bsc_aon_irq: intc@7d517b00 { ++ compatible = "brcm,bcm7271-l2-intc"; ++ reg = <0x7d517b00 0x10>; ++ interrupts = ; ++ 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 = , ++ , ++ , ++ ; ++ interrupt-affinity = <&cpu0>, <&cpu1>, <&cpu2>, <&cpu3>; ++ }; ++ ++ timer { ++ compatible = "arm,armv8-timer"; ++ interrupts = , ++ , ++ , ++ ; ++ /* 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 = , ++ , ++ , ++ , ++ , ++ ; ++ 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 = ++ , /* dma4 6 */ ++ , /* dma4 7 */ ++ , /* dma4 8 */ ++ , /* dma4 9 */ ++ , /* dma4 10 */ ++ ; /* 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 = , ++ ; ++ 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 = , ++ ; ++ 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 = , ++ ; ++ 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 = , ++ ; ++ 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 = ; ++ ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = , ++ ; ++ 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 = ; ++ }; ++ ++ pisp_be: pisp_be@880000 { ++ compatible = "raspberrypi,pispbe"; ++ reg = <0x10 0x00880000 0x0 0x4000>; ++ interrupts = ; ++ 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: + + ++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: + + + 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: + + ++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: + + ++Name: i2c0-pi5 ++Info: Enable i2c0 (Pi 5 only) ++Load: dtoverlay=i2c0-pi5,= ++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: + + ++Name: i2c1-pi5 ++Info: Enable i2c1 (Pi 5 only) ++Load: dtoverlay=i2c1-pi5,= ++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,= ++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, +@@ -2258,6 +2332,16 @@ Params: pins_2_3 Use GPIO + "100000") + + ++Name: i2c3-pi5 ++Info: Enable i2c3 (Pi 5 only) ++Load: dtoverlay=i2c3-pi5,= ++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, +@@ -2869,6 +2953,10 @@ Load: dtoverlay=midi-uart0 + Params: + + ++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: + + ++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: + + ++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: + + ++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: + + ++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: +@@ -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,= + 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,= + 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,= + 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,= + 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,= ++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,= + 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,= ++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,= + 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,= + 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,= ++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,= + 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,= ++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,= + 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,= + 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,= +-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,= +-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, ++Params: ctsrts Enable CTS/RTS on GPIOs 16-17 (default off) ++ ++ + Name: uart1 + Info: Change the pin usage of uart1 + Load: dtoverlay=uart1,= +@@ -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, ++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, + 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, ++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, + 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, ++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, + 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, ++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, +@@ -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 ++ ++/{ ++ 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 ++ ++/* ++ * 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 ++ ++/* ++ * 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 ++ ++/* ++ * 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 ++ ++/* ++ * 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 ++ ++/* ++ * 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 ++#include ++#include ++ ++&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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ status = "disabled"; ++ }; ++ ++ rp1_i2c1: i2c@74000 { ++ reg = <0xc0 0x40074000 0x0 0x1000>; ++ compatible = "snps,designware-i2c"; ++ interrupts = ; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ status = "disabled"; ++ }; ++ ++ rp1_i2c2: i2c@78000 { ++ reg = <0xc0 0x40078000 0x0 0x1000>; ++ compatible = "snps,designware-i2c"; ++ interrupts = ; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ status = "disabled"; ++ }; ++ ++ rp1_i2c3: i2c@7c000 { ++ reg = <0xc0 0x4007c000 0x0 0x1000>; ++ compatible = "snps,designware-i2c"; ++ interrupts = ; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ status = "disabled"; ++ }; ++ ++ rp1_i2c4: i2c@80000 { ++ reg = <0xc0 0x40080000 0x0 0x1000>; ++ compatible = "snps,designware-i2c"; ++ interrupts = ; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ status = "disabled"; ++ }; ++ ++ rp1_i2c5: i2c@84000 { ++ reg = <0xc0 0x40084000 0x0 0x1000>; ++ compatible = "snps,designware-i2c"; ++ interrupts = ; ++ clocks = <&rp1_clocks RP1_CLK_SYS>; ++ status = "disabled"; ++ }; ++ ++ rp1_i2c6: i2c@88000 { ++ reg = <0xc0 0x40088000 0x0 0x1000>; ++ compatible = "snps,designware-i2c"; ++ interrupts = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = , ++ , ++ ; ++ 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 = ; ++ 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 = ; ++ ++ 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 = ; ++ ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ 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 = ; ++ ++ 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 = ; ++ ++ 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 = ; ++ ++ 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 = ; ++ ++ 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" diff --git a/target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch b/target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch new file mode 100644 index 0000000000..7e39ac8abf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0855-gpio_brcmstb-Allow-to-build-for-ARCH_BCM2835.patch @@ -0,0 +1,282 @@ +From fa18902ee1e53ad391a455a01be3ab2ea1c5af5f Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 + +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 + +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch b/target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch new file mode 100644 index 0000000000..7e0886d7e6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0856-Allow-RESET_BRCMSTB-on-ARCH_BCM2835.patch @@ -0,0 +1,20 @@ +From 22ae3b2ee3293278e647877b269a5aebad3f077d Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch new file mode 100644 index 0000000000..429c3e2fd2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0857-pinctrl-bcm2712-pinctrl-pinconf-driver.patch @@ -0,0 +1,1324 @@ +From af7e60a33f0b5ce84bffb69ba084ba1edd180195 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 + +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 + +pinctrl: bcm2712: on C0 the regular GPIO pad control register moves too + +Signed-off-by: Jonathan Bell + +pinctrl: bcm2712: Implement (partially) pinconf_get + +Signed-off-by: Phil Elwell + +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 + +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 + +pinctrl: bcm2712: Guard against bad func numbers + +Signed-off-by: Phil Elwell + +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 + +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch new file mode 100644 index 0000000000..05d403a63e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0859-mmc-brcmstb-add-support-for-BCM2712.patch @@ -0,0 +1,498 @@ +From b627647c4500d39cb026924b608841fdf4d4d7e9 Mon Sep 17 00:00:00 2001 +From: Ulf Hansson +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 + +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 +--- + 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 + #include + #include ++#include ++#include + + #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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch b/target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch new file mode 100644 index 0000000000..1aea0b3bcd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0860-sdhci-Add-SD-Express-hook.patch @@ -0,0 +1,90 @@ +From 9564939f1a92e5f9807461539de28c50e5bee440 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch b/target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch new file mode 100644 index 0000000000..2ac7b7830c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0861-Add-new-pispbe-driver-though-not-yet-the-Makesfiles-.patch @@ -0,0 +1,3788 @@ +From ce14be51d71bf39893786d380cbb82e81d2a10d5 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 + +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 + +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 + +media: pisp_be: Advertise the meta output format explictily. + +Signed-off-by: Naushir Patuck + +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 + +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 + +v4l2: pisp_be: Move format definitions into v4l2 core + +Signed-off-by: Naushir Patuck + +media: raspberrypi: Move PiSP common headers to a single location + +Signed-off-by: Naushir Patuck + +media: raspberrypi: Remove old pispbe driver. + +This is now supersede by the driver in drivers/media/platform/raspberrypi/ + +Signed-off-by: Naushir Patuck + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +media: pisp: Checkpatch and coding style fixups + +Signed-off-by: Dave Stevenson + +media: pisp_be: More coding style fixups + +media: platform: bcm2712: pisp_be: Fix crash when buffer format not set + +Signed-off-by: Nick Hollinghurst + +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 + +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 + +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 + +bcm2712: Use BIT() macro + +Use the BIT() macro instead of plain bit shifting. + +Signed-off-by: Jacopo Mondi + +bcm2712: Invert condition in pispbe_schedule_internal() + +Return earlier and save one indentation level + +Signed-off-by: Jacopo Mondi + +bcm2712: Invert condition in for loop + +Save one indentation level by continuing if the node is not streaming. + +Signed-off-by: Jacopo Mondi + +bcm2712: Do not declare a local variable + +There already is a truct pispbe_node *node in the function scope. + +Signed-off-by: Jacopo Mondi + +bcm21712: Siplify pispbe_schedule_one() + +A little more verbose but easier to follow ? + +Signed-off-by: Jacopo Mondi + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +media: bcm2712: Demote info message + +Demote info message about clock enablement to dev_dbg() + +Signed-off-by: Jacopo Mondi + +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 + +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 + +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 + +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 + +media: bcm2712: Create v4l2_subdev for ISP entity + +Create a v4l2 subdevice to represent the PISPBE ISP entity. + +Signed-off-by: Jacopo Mondi + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pisp_be_config.h" ++#include "pisp_be_formats.h" ++ ++MODULE_DESCRIPTION("PiSP Back End driver"); ++MODULE_AUTHOR("David Plowman "); ++MODULE_AUTHOR("Nick Hollinghurst "); ++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 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 ++ * 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 ++ ++#include ++ ++/* 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 ++#include ++ ++#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 ++ ++#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 + diff --git a/target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch b/target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch new file mode 100644 index 0000000000..022a94af1c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0862-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch @@ -0,0 +1,386 @@ +From 89b748416358e4e04765b9a4f20e1c3d256b9d9e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 + +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch b/target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch new file mode 100644 index 0000000000..a4328e5bc7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0863-reset-reset-brcmstb-rescal-Support-shared-use.patch @@ -0,0 +1,47 @@ +From 87b1126181f79fb2558652af0d7fafd9deaab5f3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch b/target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch new file mode 100644 index 0000000000..88d92fbb20 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0864-net-macb-Also-set-DMA-coherent-mask.patch @@ -0,0 +1,418 @@ +From bd36586dd9e05bde8e23dc3d99771269b48b65f8 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 + +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 + +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 + +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 + +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 + +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 + +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 +--- + 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 + #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), diff --git a/target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch b/target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch new file mode 100644 index 0000000000..f6944e226f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0865-usb-dwc3-Set-DMA-and-coherent-masks-early.patch @@ -0,0 +1,384 @@ +From 4ffa5f2c5fc7854683964bb2f2bf23907c18213f Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 + +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 + +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 + +drivers: usb: dwc3: remove downstream quirk dis-in-autoretry + +Upstream have unilaterally disabled the feature. + +Partially reverts 6e9142a26ee0fdc3a5adc49ed6cedc0b16ec2ed1 (downstream) + +Signed-off-by: Jonathan Bell +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch b/target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch new file mode 100644 index 0000000000..a6f0c1961c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0866-drm-panel-raspberrypi-touchscreen-Insert-more-delays.patch @@ -0,0 +1,37 @@ +From 480c8e9f48f8a96c457eb3dc0079a73598fb7477 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch b/target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch new file mode 100644 index 0000000000..179d03733e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0867-PCI-brcmstb-Add-BCM2712-support.patch @@ -0,0 +1,1108 @@ +From 29702857d1ab71243ea6c247dfe9b5bc43dd422f Mon Sep 17 00:00:00 2001 +From: Jim Quinlan +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 + +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 + +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 + +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 + +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 + +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 + +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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: diff --git a/target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch b/target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch new file mode 100644 index 0000000000..3b50329ab1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0868-V4L2-Add-PiSP-opaque-formats-to-V4L2.patch @@ -0,0 +1,42 @@ +From 9a11300e46344917226b986a8740e7581d66adf3 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Mon, 7 Feb 2022 09:20:49 +0000 +Subject: [PATCH] V4L2: Add PiSP opaque formats to V4L2 + +Signed-off-by: Naushir Patuck +--- + 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 + diff --git a/target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch b/target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch new file mode 100644 index 0000000000..6836922b7c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0869-V4L2-Add-PiSP-compressed-formats-to-V4L2.patch @@ -0,0 +1,39 @@ +From 01f31f4145d49a30eb553c65ea755dde8dba1de0 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Wed, 2 Mar 2022 16:10:50 +0000 +Subject: [PATCH] V4L2: Add PiSP compressed formats to V4L2 + +Signed-off-by: Naushir Patuck +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch b/target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch new file mode 100644 index 0000000000..9e98dd0345 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0871-dt-binding-mfd-Add-binding-for-Raspberry-Pi-RP1.patch @@ -0,0 +1,249 @@ +From c93f469dabdbed822e5abeb5283d79fc9faa858c Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch new file mode 100644 index 0000000000..dd252cba36 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0872-mfd-Add-rp1-driver.patch @@ -0,0 +1,442 @@ +From 7196a12b94e90225686e6c34cdf65a583214f7a5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* 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 "); ++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 ++ ++#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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch b/target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch new file mode 100644 index 0000000000..f94782438b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0873-dt-bindings-clock-Add-bindings-for-Raspberry-Pi-RP1.patch @@ -0,0 +1,65 @@ +From 00ff2819eb852b54fe22e7181646e40d560576dc Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch new file mode 100644 index 0000000000..2a861d18c7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0874-clk-Add-rp1-clock-driver.patch @@ -0,0 +1,2208 @@ +From 66517cdfea750b89d86f78af55ef773cbd3e005f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#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 "); ++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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch b/target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch new file mode 100644 index 0000000000..d0180a65ba --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0875-dt-bindings-pinctrl-Add-bindings-for-Raspberry-Pi-RP.patch @@ -0,0 +1,60 @@ +From 19b934ce3763c9465c5c80302f7c142d30b75869 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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__ */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch new file mode 100644 index 0000000000..c824cddb51 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0876-pinctrl-Add-rp1-driver.patch @@ -0,0 +1,1666 @@ +From 4d4cc5be473a7767052122a87393a83d10f9ed41 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#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__ */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch b/target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch new file mode 100644 index 0000000000..b0b9897e89 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0877-serial-pl011-rp1-uart-support.patch @@ -0,0 +1,129 @@ +From f88da9e21d8eff58eeb9280ae96bf9593121d8eb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 12 Oct 2022 13:24:51 +0100 +Subject: [PATCH] serial: pl011: rp1 uart support + +Signed-off-by: Phil Elwell +--- + 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); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch b/target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch new file mode 100644 index 0000000000..6bc21a6a57 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0878-mmc-sdhci-of-dwcmshc-define-sdio-timeout-clocks.patch @@ -0,0 +1,83 @@ +From 4a5ac516ca0a820e7c006ae408872009e37e114b Mon Sep 17 00:00:00 2001 +From: Liam Fraser +Date: Thu, 14 Mar 2019 16:01:26 +0000 +Subject: [PATCH] mmc: sdhci-of-dwcmshc: define sdio timeout clocks + +Signed-off-by: Liam Fraser +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch b/target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch new file mode 100644 index 0000000000..12d614fd94 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0879-mmc-sdhci-of-dwcmshc-rp1-sdio-changes.patch @@ -0,0 +1,83 @@ +From 14a43b3fd43bf9b230f93d1eba276d40aac969ba Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 12 Oct 2022 14:07:32 +0100 +Subject: [PATCH] mmc: sdhci-of-dwcmshc: rp1 sdio changes + +Signed-off-by: Phil Elwell +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch new file mode 100644 index 0000000000..7db5202ae4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0880-clk-rp1-Add-sdio-clk-driver.patch @@ -0,0 +1,641 @@ +From b427cc1a83404f48b12dec2efbef076b38df6218 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Wed, 12 Oct 2022 14:20:07 +0100 +Subject: [PATCH] clk: rp1: Add sdio-clk driver + +Signed-off-by: Phil Elwell +--- + 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 ++#include ++#include ++#include ++#include ++ ++// 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 "); ++MODULE_DESCRIPTION("RP1 SDIO clock driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch b/target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch new file mode 100644 index 0000000000..ea3b315501 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0881-i2c-designware-Add-SMBUS-quick-command-support.patch @@ -0,0 +1,76 @@ +From 50adadfaf324ed5cbb59ce2b85eda59de4e3801a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch b/target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch new file mode 100644 index 0000000000..fff6cfdeb4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0882-dmaengine-dw-axi-dmac-Fixes-for-RP1.patch @@ -0,0 +1,355 @@ +From 0a1cd70189daec3baf4b4a233dd8e25ffbb9d512 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + .../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 + #include + #include ++#include + #include + #include + #include +@@ -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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch b/target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch new file mode 100644 index 0000000000..68eca7961f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0883-spi-dw-Handle-combined-tx-and-rx-messages.patch @@ -0,0 +1,64 @@ +From 8a9c0607ce0daa91c48faefd70ea73bda54ed0ae Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 29 Nov 2022 10:09:54 +0000 +Subject: [PATCH] spi: dw: Handle combined tx and rx messages + +Signed-off-by: Phil Elwell +--- + 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 + #include + #include ++#include + + #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)) diff --git a/target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch b/target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch new file mode 100644 index 0000000000..241d6c49db --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0884-pwm-Add-support-for-RP1-PWM.patch @@ -0,0 +1,292 @@ +From 824f18efc8ad59e2783570ae2df83e2cd16b9f04 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + .../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 ++ ++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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#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); ++} diff --git a/target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch new file mode 100644 index 0000000000..2023bf73e8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0886-drm-Add-RP1-DPI-driver.patch @@ -0,0 +1,1552 @@ +From 61c3065f89d4447c7e4cf61a466ebc3c4a834ad2 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; ++} diff --git a/target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch new file mode 100644 index 0000000000..5bf657ab24 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0887-drm-Add-RP1-VEC-driver.patch @@ -0,0 +1,3078 @@ +From 09c2c6aad0fed44182defecd274579770feb0ae2 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch b/target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch new file mode 100644 index 0000000000..cf0b02c64b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0888-v4l2-Add-pisp-compression-format-support-to-v4l2.patch @@ -0,0 +1,75 @@ +From 3a419974ba02d32795a5ecfaf3c020f23173b6a1 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Tue, 14 Feb 2023 20:58:59 +0000 +Subject: [PATCH] v4l2: Add pisp compression format support to v4l2 + +Signed-off-by: Naushir Patuck +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch b/target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch new file mode 100644 index 0000000000..038633b74e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0889-media-rp1-Add-CFE-Camera-Front-End-support.patch @@ -0,0 +1,4527 @@ +From 8a31623de7f034f6521b348e9a510e78a6e7e493 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +Date: Tue, 14 Feb 2023 17:30:12 +0000 +Subject: [PATCH] media: rp1: Add CFE (Camera Front End) support + +Signed-off-by: Naushir Patuck +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++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 ++#include ++#include ++ ++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 ++#include ++#include ++#include ++ ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++ ++#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 ++#include ++ ++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 ++#include ++#include ++#include ++#include ++ ++#include ++ ++#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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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 ++ ++#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_ */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch b/target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch new file mode 100644 index 0000000000..7d81e03429 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0890-dt-bindings-net-cdns-macb-AXI-tuning-properties.patch @@ -0,0 +1,38 @@ +From 2be65d1fd1f7d3cf6f59b58b53e285400f04a160 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + .../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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch b/target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch new file mode 100644 index 0000000000..06d69c7c4c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0891-ASoC-dwc-list-all-supported-sample-sizes.patch @@ -0,0 +1,29 @@ +From 9ef0615a5c5f93cb72af8df3a2dae6d23b106eb5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch b/target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch new file mode 100644 index 0000000000..b18e5f888f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0892-ASoC-dwc-Support-set_bclk_ratio.patch @@ -0,0 +1,59 @@ +From 06f794e8cb227249e03893e4b4923ff58556eb60 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 4 Mar 2021 14:49:23 +0000 +Subject: [PATCH] ASoC: dwc: Support set_bclk_ratio + +Signed-off-by: Phil Elwell +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch b/target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch new file mode 100644 index 0000000000..474903c327 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0893-ASoC-dwc-Add-DMACR-handling.patch @@ -0,0 +1,81 @@ +From b3b1177092d4d2ba6df74042d39aa42c5055f687 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch b/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch new file mode 100644 index 0000000000..73704a8be3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0894-ASOC-dwc-Improve-DMA-shutdown.patch @@ -0,0 +1,128 @@ +From e6baee4502c0228c79408b047096a1259a84353f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch b/target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch new file mode 100644 index 0000000000..fa5eb61044 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0895-ASOC-dwc-Fix-16-bit-audio-handling.patch @@ -0,0 +1,88 @@ +From 9c6694c24f26ea435165431d41c72451fadbd753 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch b/target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch new file mode 100644 index 0000000000..9f4f7a0c9a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0896-ASoC-bcm-Remove-dependency-on-BCM2835-I2S.patch @@ -0,0 +1,304 @@ +From f476db1b71e8b82e5299168f963a2fefb7a395e2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch new file mode 100644 index 0000000000..c3cf21455b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0897-hwmon-Add-RP1-ADC-and-temperature-driver.patch @@ -0,0 +1,343 @@ +From cad3c92ff0c1a5fa539d08b695b0f6b326924890 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 2 Mar 2023 18:04:42 +0000 +Subject: [PATCH] hwmon: Add RP1 ADC and temperature driver + +Signed-off-by: Phil Elwell +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch new file mode 100644 index 0000000000..37210173db --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0898-mfd-bcm2835-pm-Add-support-for-BCM2712.patch @@ -0,0 +1,69 @@ +From 0c7aeb96fd3ab68011ba6c24239c501190890308 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 = { diff --git a/target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch new file mode 100644 index 0000000000..e7e3652d91 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0899-soc-bcm-bcm2835-power-Add-support-for-BCM2712.patch @@ -0,0 +1,76 @@ +From 9cf85a95eeb239a079a3485bd1d0447431bdc7f1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch b/target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch new file mode 100644 index 0000000000..ff8471411f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0900-drivers-spi-Fix-spi-gpio-to-correctly-implement-sck-.patch @@ -0,0 +1,150 @@ +From 380c336af070edf85826abbb0057bf92a03ec466 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch b/target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch new file mode 100644 index 0000000000..1b7952ab18 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0901-spi-spi-gpio-Implement-spidelay-when-requested-bit-r.patch @@ -0,0 +1,55 @@ +From 586f87307e75552292cfc6c76b81cd38d5ec31e2 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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 + #include + #include ++#include + + #include + #include + #include + +- + /* + * 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" + diff --git a/target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch b/target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch new file mode 100644 index 0000000000..1a51a85660 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0902-drm-v3d-fix-up-register-addresses-for-V3D-7.x.patch @@ -0,0 +1,672 @@ +From 3f949caeef21269afc67dd62ae9826204f215934 Mon Sep 17 00:00:00 2001 +From: Iago Toral Quiroga +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. diff --git a/target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch b/target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch new file mode 100644 index 0000000000..ed1d5f05df --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0903-drm-v3d-update-UAPI-to-match-user-space-for-V3D-7.x.patch @@ -0,0 +1,24 @@ +From 22fb30936524ae96151789741885edbc45efb53d Mon Sep 17 00:00:00 2001 +From: Iago Toral Quiroga +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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch b/target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch new file mode 100644 index 0000000000..64189338e6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0904-drm-v3d-add-brcm-2712-v3d-as-a-compatible-V3D-device.patch @@ -0,0 +1,19 @@ +From 18bc419d38eda06ded78c7b702c0e21e5af8f24c Mon Sep 17 00:00:00 2001 +From: Iago Toral Quiroga +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" }, diff --git a/target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch b/target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch new file mode 100644 index 0000000000..3c53996ff0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0905-drm-v3d-Improve-MMU-support-for-larger-pages.patch @@ -0,0 +1,64 @@ +From 12c7ea43b930976f35ce75d11fd3f55438868e13 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"); diff --git a/target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch b/target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch new file mode 100644 index 0000000000..43c5c395c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0906-dt-bindings-gpu-v3d-Add-BCM2712-to-compatibility-lis.patch @@ -0,0 +1,19 @@ +From 5970fa51663511d7f773db7109ff6fa2504f186a Mon Sep 17 00:00:00 2001 +From: Iago Toral Quiroga +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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch new file mode 100644 index 0000000000..14e6b0a9e4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0907-drivers-char-add-generic-gpiomem-driver.patch @@ -0,0 +1,328 @@ +From fdf9cab5eaa849e90b12e17718bc47130a91433c Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); diff --git a/target/linux/bcm27xx/patches-6.1/950-0909-drivers-char-delete-bcm2835-gpiomem.patch b/target/linux/bcm27xx/patches-6.1/950-0909-drivers-char-delete-bcm2835-gpiomem.patch new file mode 100644 index 0000000000..5d6eb16fb2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0909-drivers-char-delete-bcm2835-gpiomem.patch @@ -0,0 +1,301 @@ +From 27bda80061b46e18fe83be11228df5365363b377 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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 +- * 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 +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-#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 "); diff --git a/target/linux/bcm27xx/patches-6.1/950-0910-drivers-hwmon-rp1-adc-check-conversion-validity-befo.patch b/target/linux/bcm27xx/patches-6.1/950-0910-drivers-hwmon-rp1-adc-check-conversion-validity-befo.patch new file mode 100644 index 0000000000..557b7ac406 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0910-drivers-hwmon-rp1-adc-check-conversion-validity-befo.patch @@ -0,0 +1,34 @@ +From 3cafcfbab9b5f3f1357b415b6ca09911eeb405d6 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0911-dmaengine-bcm2835-Add-BCM2712-support.patch b/target/linux/bcm27xx/patches-6.1/950-0911-dmaengine-bcm2835-Add-BCM2712-support.patch new file mode 100644 index 0000000000..58095cf8fd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0911-dmaengine-bcm2835-Add-BCM2712-support.patch @@ -0,0 +1,36 @@ +From 87c5545f9a66984894384da5f8c2eeb60983732a Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0912-dmaengine-bcm2835-HACK-Support-DMA-Lite-channels.patch b/target/linux/bcm27xx/patches-6.1/950-0912-dmaengine-bcm2835-HACK-Support-DMA-Lite-channels.patch new file mode 100644 index 0000000000..6f651a65f6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0912-dmaengine-bcm2835-HACK-Support-DMA-Lite-channels.patch @@ -0,0 +1,57 @@ +From a671a2774cb3bcfb144622149757f6821aa0604c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0913-clk-bcm-rpi-Add-disp-clock.patch b/target/linux/bcm27xx/patches-6.1/950-0913-clk-bcm-rpi-Add-disp-clock.patch new file mode 100644 index 0000000000..6f5e55d956 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0913-clk-bcm-rpi-Add-disp-clock.patch @@ -0,0 +1,46 @@ +From c8fd69c6f567bd43ba084b95a987532940465ef5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0914-net-phy-broadcom-optionally-enable-link-down-powersa.patch b/target/linux/bcm27xx/patches-6.1/950-0914-net-phy-broadcom-optionally-enable-link-down-powersa.patch new file mode 100644 index 0000000000..2df48297b5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0914-net-phy-broadcom-optionally-enable-link-down-powersa.patch @@ -0,0 +1,26 @@ +From 6370a6cd16a5aa9726bf209c0f0a3179f4011cb1 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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)) { diff --git a/target/linux/bcm27xx/patches-6.1/950-0915-dmaengine-bcm2835-Rename-to_bcm2711_cbaddr-to-to_40b.patch b/target/linux/bcm27xx/patches-6.1/950-0915-dmaengine-bcm2835-Rename-to_bcm2711_cbaddr-to-to_40b.patch new file mode 100644 index 0000000000..ce59f6072b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0915-dmaengine-bcm2835-Rename-to_bcm2711_cbaddr-to-to_40b.patch @@ -0,0 +1,75 @@ +From cfad3f71fc450639fc259d576d0903e9132fe34a Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0916-dmaengine-bcm2835-Fix-dma-driver-for-BCM2835-38.patch b/target/linux/bcm27xx/patches-6.1/950-0916-dmaengine-bcm2835-Fix-dma-driver-for-BCM2835-38.patch new file mode 100644 index 0000000000..a2ff2a6bb0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0916-dmaengine-bcm2835-Fix-dma-driver-for-BCM2835-38.patch @@ -0,0 +1,77 @@ +From 75f44d1416c5de17865247d6d012c37f7650437c Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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; + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0917-drivers-iommu-Add-BCM2712-IOMMU.patch b/target/linux/bcm27xx/patches-6.1/950-0917-drivers-iommu-Add-BCM2712-IOMMU.patch new file mode 100644 index 0000000000..92f43fa71b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0917-drivers-iommu-Add-BCM2712-IOMMU.patch @@ -0,0 +1,855 @@ +From 5fd6ee7fd084838e09d4e463ae53cd9aaa7fce70 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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 ++#include ++#include ++#include ++ ++#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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#include ++ ++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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0918-irqchip-irq-brcmstb-l2-Add-config-for-2711-controlle.patch b/target/linux/bcm27xx/patches-6.1/950-0918-irqchip-irq-brcmstb-l2-Add-config-for-2711-controlle.patch new file mode 100644 index 0000000000..5ea212d4a7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0918-irqchip-irq-brcmstb-l2-Add-config-for-2711-controlle.patch @@ -0,0 +1,73 @@ +From fa4d4ed28c92cf4470e518f1a7362dc7941632d7 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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"); diff --git a/target/linux/bcm27xx/patches-6.1/950-0919-rtc-rtc-rpi-Add-simple-RTC-driver-for-Raspberry-Pi.patch b/target/linux/bcm27xx/patches-6.1/950-0919-rtc-rtc-rpi-Add-simple-RTC-driver-for-Raspberry-Pi.patch new file mode 100644 index 0000000000..e2b8c6c6a1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0919-rtc-rtc-rpi-Add-simple-RTC-driver-for-Raspberry-Pi.patch @@ -0,0 +1,237 @@ +From 222dedcdc09247126d39364a614ff2019789f52a Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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 ++#include ++#include ++#include ++#include ++ ++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"); diff --git a/target/linux/bcm27xx/patches-6.1/950-0920-dt-bindings-rtc-new-binding-for-Raspberry-Pi-RTC-dri.patch b/target/linux/bcm27xx/patches-6.1/950-0920-dt-bindings-rtc-new-binding-for-Raspberry-Pi-RTC-dri.patch new file mode 100644 index 0000000000..f4c630cf09 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0920-dt-bindings-rtc-new-binding-for-Raspberry-Pi-RTC-dri.patch @@ -0,0 +1,35 @@ +From ff2c2f67689e10ad66c1e33ae6a7552d82ac983c Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + .../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>; ++ }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch b/target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch new file mode 100644 index 0000000000..205806cdfd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0921-hwmon-pwm-fan-Add-fan-speed-register-support.patch @@ -0,0 +1,164 @@ +From 96a8a4776cb142f5d2bb7f6379df9af40e727c0b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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) diff --git a/target/linux/bcm27xx/patches-6.1/950-0923-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch b/target/linux/bcm27xx/patches-6.1/950-0923-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch new file mode 100644 index 0000000000..2972c57fd7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0923-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch @@ -0,0 +1,63 @@ +From 07419175fdb507be2c9d3aaf4b7d18306a336348 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + .../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 ++ ++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 ++ ++ pwr_button: pwr_button { ++ compatible = "raspberrypi,firmware-button"; ++ id = ; ++ label = "pwr_button"; ++ linux,code = <116>; // KEY_POWER ++ }; ++ ++... diff --git a/target/linux/bcm27xx/patches-6.1/950-0924-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch b/target/linux/bcm27xx/patches-6.1/950-0924-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch new file mode 100644 index 0000000000..e8fea23ce8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0924-dt-bindings-input-Add-bindings-for-raspberrypi-butto.patch @@ -0,0 +1,27 @@ +From 93c8947bc7813b49fe27a5251eef97c6df1e14c6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch new file mode 100644 index 0000000000..76a9261a68 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0925-Input-Add-raspberrypi-button-firmware-driver.patch @@ -0,0 +1,197 @@ +From 7c0d40384b0648030d5202114d90239b8db7d4e0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++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 "); ++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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0926-dt-bindings-update-rpi-rtc-binding.patch b/target/linux/bcm27xx/patches-6.1/950-0926-dt-bindings-update-rpi-rtc-binding.patch new file mode 100644 index 0000000000..ffc5cc1a9d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0926-dt-bindings-update-rpi-rtc-binding.patch @@ -0,0 +1,29 @@ +From a7a3679a148e40879f1ce77580d9edf64cb5b51c Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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>; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0927-drivers-rtc-rpi-add-battery-charge-circuit-control-a.patch b/target/linux/bcm27xx/patches-6.1/950-0927-drivers-rtc-rpi-add-battery-charge-circuit-control-a.patch new file mode 100644 index 0000000000..dae0a1cbff --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0927-drivers-rtc-rpi-add-battery-charge-circuit-control-a.patch @@ -0,0 +1,153 @@ +From 33b514cb16dbf13395a0becf7442d19676ae4224 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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[] = { diff --git a/target/linux/bcm27xx/patches-6.1/950-0928-vc4_drv-Avoid-panic-when-booted-with-no-kms.patch b/target/linux/bcm27xx/patches-6.1/950-0928-vc4_drv-Avoid-panic-when-booted-with-no-kms.patch new file mode 100644 index 0000000000..8aae09c81a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0928-vc4_drv-Avoid-panic-when-booted-with-no-kms.patch @@ -0,0 +1,29 @@ +From 0f5fd4538774aa6c936bb8fc78611c3113bf19d7 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-0929-drm-vc4-Treat-zero-sized-destination-as-full-screen.patch b/target/linux/bcm27xx/patches-6.1/950-0929-drm-vc4-Treat-zero-sized-destination-as-full-screen.patch new file mode 100644 index 0000000000..70a11df293 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0929-drm-vc4-Treat-zero-sized-destination-as-full-screen.patch @@ -0,0 +1,28 @@ +From 2c987545a88507acdd8a572a3bd23a4ca0124d14 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0930-drm-vc4-Fix-FKMS-for-when-the-YUV-chroma-planes-are-.patch b/target/linux/bcm27xx/patches-6.1/950-0930-drm-vc4-Fix-FKMS-for-when-the-YUV-chroma-planes-are-.patch new file mode 100644 index 0000000000..9182194403 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0930-drm-vc4-Fix-FKMS-for-when-the-YUV-chroma-planes-are-.patch @@ -0,0 +1,50 @@ +From bb1ee75de382c7a5218750476aa2a5792309cc70 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-0931-drm-vc4-hdmi-Enable-the-audio-clock.patch b/target/linux/bcm27xx/patches-6.1/950-0931-drm-vc4-hdmi-Enable-the-audio-clock.patch new file mode 100644 index 0000000000..f228dca9d5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0931-drm-vc4-hdmi-Enable-the-audio-clock.patch @@ -0,0 +1,39 @@ +From 0da58dfbd2cc2cfa14a629787b9ba6fa10b5f666 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0932-drm-vc4-hdmi-Warn-if-writing-to-an-unknown-HDMI-regi.patch b/target/linux/bcm27xx/patches-6.1/950-0932-drm-vc4-hdmi-Warn-if-writing-to-an-unknown-HDMI-regi.patch new file mode 100644 index 0000000000..fa6a399dae --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0932-drm-vc4-hdmi-Warn-if-writing-to-an-unknown-HDMI-regi.patch @@ -0,0 +1,31 @@ +From cdbebb3a92aca7327c88c6dc6ef5d4cd470d49fc Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0933-drm-vc4-hvs-More-logging-for-dlist-generation.patch b/target/linux/bcm27xx/patches-6.1/950-0933-drm-vc4-hvs-More-logging-for-dlist-generation.patch new file mode 100644 index 0000000000..dee2fc0d66 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0933-drm-vc4-hvs-More-logging-for-dlist-generation.patch @@ -0,0 +1,41 @@ +From 4ebd8283403daf5507e5aafb42fe3e4c7612eb14 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0934-drm-vc4-hvs-Print-error-if-we-fail-an-allocation.patch b/target/linux/bcm27xx/patches-6.1/950-0934-drm-vc4-hvs-Print-error-if-we-fail-an-allocation.patch new file mode 100644 index 0000000000..7c7bbb4822 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0934-drm-vc4-hvs-Print-error-if-we-fail-an-allocation.patch @@ -0,0 +1,66 @@ +From c0af63193bd307f281211e7fb32a02a52c2869b2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0935-drm-vc4-plane-Add-more-debugging-for-LBM-allocation.patch b/target/linux/bcm27xx/patches-6.1/950-0935-drm-vc4-plane-Add-more-debugging-for-LBM-allocation.patch new file mode 100644 index 0000000000..d8d68e79aa --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0935-drm-vc4-plane-Add-more-debugging-for-LBM-allocation.patch @@ -0,0 +1,36 @@ +From 164f7e94da446984f275be1c082b93243beadfba Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0936-drm-vc4-plane-Use-return-variable-in-atomic_check.patch b/target/linux/bcm27xx/patches-6.1/950-0936-drm-vc4-plane-Use-return-variable-in-atomic_check.patch new file mode 100644 index 0000000000..220245c351 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0936-drm-vc4-plane-Use-return-variable-in-atomic_check.patch @@ -0,0 +1,31 @@ +From 950394a39f659746e5933cbc203a8bedef8246b7 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0937-drm-vc4-crtc-Move-assigned_channel-to-a-variable.patch b/target/linux/bcm27xx/patches-6.1/950-0937-drm-vc4-crtc-Move-assigned_channel-to-a-variable.patch new file mode 100644 index 0000000000..3476f548c4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0937-drm-vc4-crtc-Move-assigned_channel-to-a-variable.patch @@ -0,0 +1,47 @@ +From f3c6acc345113c57011f2b1c8421e6cf78f0bc30 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0938-drm-vc4-Introduce-generation-number-enum.patch b/target/linux/bcm27xx/patches-6.1/950-0938-drm-vc4-Introduce-generation-number-enum.patch new file mode 100644 index 0000000000..9314c797a0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0938-drm-vc4-Introduce-generation-number-enum.patch @@ -0,0 +1,1029 @@ +From fa2571d625bb53b642cd9f29a7cfc3434e1cf576 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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)); diff --git a/target/linux/bcm27xx/patches-6.1/950-0939-drm-vc4-Make-v3d-paths-unavailable-on-any-generation.patch b/target/linux/bcm27xx/patches-6.1/950-0939-drm-vc4-Make-v3d-paths-unavailable-on-any-generation.patch new file mode 100644 index 0000000000..699db05e0c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0939-drm-vc4-Make-v3d-paths-unavailable-on-any-generation.patch @@ -0,0 +1,577 @@ +From c382ea6b0457027b6ad883ee4348e03df515a785 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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)); diff --git a/target/linux/bcm27xx/patches-6.1/950-0940-drm-vc4-hvs-Use-switch-statement-to-simplify-vc4_hvs.patch b/target/linux/bcm27xx/patches-6.1/950-0940-drm-vc4-hvs-Use-switch-statement-to-simplify-vc4_hvs.patch new file mode 100644 index 0000000000..048b5d3222 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0940-drm-vc4-hvs-Use-switch-statement-to-simplify-vc4_hvs.patch @@ -0,0 +1,125 @@ +From 72e5eb3d9511af2f056911d70c4d033d4fc674b2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0941-drm-vc4-hvs-Use-switch-statement-to-simplify-enablin.patch b/target/linux/bcm27xx/patches-6.1/950-0941-drm-vc4-hvs-Use-switch-statement-to-simplify-enablin.patch new file mode 100644 index 0000000000..984b329fb4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0941-drm-vc4-hvs-Use-switch-statement-to-simplify-enablin.patch @@ -0,0 +1,74 @@ +From 72bfb10c9393688d00e4e0b00d416e23c2753318 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 * diff --git a/target/linux/bcm27xx/patches-6.1/950-0942-drm-vc4-hvs-Test-if-the-EOF-interrupts-are-enabled.patch b/target/linux/bcm27xx/patches-6.1/950-0942-drm-vc4-hvs-Test-if-the-EOF-interrupts-are-enabled.patch new file mode 100644 index 0000000000..704468b90e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0942-drm-vc4-hvs-Test-if-the-EOF-interrupts-are-enabled.patch @@ -0,0 +1,100 @@ +From bcf02f6ac0d429a425e8409f140bd875a1feed2e Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 * diff --git a/target/linux/bcm27xx/patches-6.1/950-0943-drm-vc4-hvs-Create-hw_init-function.patch b/target/linux/bcm27xx/patches-6.1/950-0943-drm-vc4-hvs-Create-hw_init-function.patch new file mode 100644 index 0000000000..5b748b5e94 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0943-drm-vc4-hvs-Create-hw_init-function.patch @@ -0,0 +1,188 @@ +From f3c84bb53107cef0009347d071c1a188ce24b8a3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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) { diff --git a/target/linux/bcm27xx/patches-6.1/950-0944-drm-vc4-hvs-Create-cob_init-function.patch b/target/linux/bcm27xx/patches-6.1/950-0944-drm-vc4-hvs-Create-cob_init-function.patch new file mode 100644 index 0000000000..5b2e9745e9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0944-drm-vc4-hvs-Create-cob_init-function.patch @@ -0,0 +1,167 @@ +From 99a13ce3a12303dfb54815637972627a7d207086 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0945-drm-vc4-hvs-Rename-hvs_regs-list.patch b/target/linux/bcm27xx/patches-6.1/950-0945-drm-vc4-hvs-Rename-hvs_regs-list.patch new file mode 100644 index 0000000000..4139d79f02 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0945-drm-vc4-hvs-Rename-hvs_regs-list.patch @@ -0,0 +1,38 @@ +From 7a1c157ac856384c47df38e1de2995f55a111b85 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0946-drm-vc4-plane-Change-ptr0_offset-to-an-array.patch b/target/linux/bcm27xx/patches-6.1/950-0946-drm-vc4-plane-Change-ptr0_offset-to-an-array.patch new file mode 100644 index 0000000000..ed55f53fad --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0946-drm-vc4-plane-Change-ptr0_offset-to-an-array.patch @@ -0,0 +1,103 @@ +From 531f66804eb95323f807d240273087fbe162aeee Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0947-drm-vc4-hvs-Rework-LBM-alignment.patch b/target/linux/bcm27xx/patches-6.1/950-0947-drm-vc4-hvs-Rework-LBM-alignment.patch new file mode 100644 index 0000000000..3a30b9ca85 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0947-drm-vc4-hvs-Rework-LBM-alignment.patch @@ -0,0 +1,45 @@ +From 2834b58a3b58198e551d8461a0786b75d3d76823 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0948-drm-vc4-hvs-Change-prototype-of-__vc4_hvs_alloc-to-p.patch b/target/linux/bcm27xx/patches-6.1/950-0948-drm-vc4-hvs-Change-prototype-of-__vc4_hvs_alloc-to-p.patch new file mode 100644 index 0000000000..2927a3e04e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0948-drm-vc4-hvs-Change-prototype-of-__vc4_hvs_alloc-to-p.patch @@ -0,0 +1,93 @@ +From 23cba2abd5ffbb7337e3f381c4724eaaf01cf5a1 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0949-drm-vc4-UV-planes-vertical-scaling-must-always-be-en.patch b/target/linux/bcm27xx/patches-6.1/950-0949-drm-vc4-UV-planes-vertical-scaling-must-always-be-en.patch new file mode 100644 index 0000000000..4a51862487 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0949-drm-vc4-UV-planes-vertical-scaling-must-always-be-en.patch @@ -0,0 +1,30 @@ +From 03e0e348df7b685439f2db61ec2d8c8da25d1217 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0950-drm-vc4-hdmi-Avoid-hang-with-debug-registers-when-su.patch b/target/linux/bcm27xx/patches-6.1/950-0950-drm-vc4-hdmi-Avoid-hang-with-debug-registers-when-su.patch new file mode 100644 index 0000000000..8e36b79794 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0950-drm-vc4-hdmi-Avoid-hang-with-debug-registers-when-su.patch @@ -0,0 +1,39 @@ +From e5e7679d634b10e88df340c85cd8368c9f9989eb Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0951-drm-vc4-Move-the-buffer-offset-out-of-the-vc4_plane_.patch b/target/linux/bcm27xx/patches-6.1/950-0951-drm-vc4-Move-the-buffer-offset-out-of-the-vc4_plane_.patch new file mode 100644 index 0000000000..eeb2a93b5c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0951-drm-vc4-Move-the-buffer-offset-out-of-the-vc4_plane_.patch @@ -0,0 +1,154 @@ +From 11cf37e741b439b26fe932750bde841a16a96828 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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. */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0952-drm-vc4-Fix-dlist-debug-not-resetting-the-next-entry.patch b/target/linux/bcm27xx/patches-6.1/950-0952-drm-vc4-Fix-dlist-debug-not-resetting-the-next-entry.patch new file mode 100644 index 0000000000..01ad932415 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0952-drm-vc4-Fix-dlist-debug-not-resetting-the-next-entry.patch @@ -0,0 +1,33 @@ +From 3660abb4a8523e988f1345985e89149804e50ebe Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0953-drm-vc4-Remove-incorrect-limit-from-hvs_dlist-debugf.patch b/target/linux/bcm27xx/patches-6.1/950-0953-drm-vc4-Remove-incorrect-limit-from-hvs_dlist-debugf.patch new file mode 100644 index 0000000000..d41d53ecbe --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0953-drm-vc4-Remove-incorrect-limit-from-hvs_dlist-debugf.patch @@ -0,0 +1,57 @@ +From ebf11a4cfd9f1236fb9eeb7e32e87b18f5f56f16 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0954-drm-vc4-hvs-Remove-ABORT_ON_EMPTY-flag.patch b/target/linux/bcm27xx/patches-6.1/950-0954-drm-vc4-hvs-Remove-ABORT_ON_EMPTY-flag.patch new file mode 100644 index 0000000000..640b0ea757 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0954-drm-vc4-hvs-Remove-ABORT_ON_EMPTY-flag.patch @@ -0,0 +1,50 @@ +From 712bccec241e84e28ccb725fae87d3255d039f42 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0955-drm-vc4-Enable-SCALER_CONTROL-early-in-HVS-init.patch b/target/linux/bcm27xx/patches-6.1/950-0955-drm-vc4-Enable-SCALER_CONTROL-early-in-HVS-init.patch new file mode 100644 index 0000000000..3060c0201f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0955-drm-vc4-Enable-SCALER_CONTROL-early-in-HVS-init.patch @@ -0,0 +1,59 @@ +From 542ba979b4fa1e07ff2ad2dabbdc12e92b80ed46 Mon Sep 17 00:00:00 2001 +From: Tim Gover +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0956-dt-bindings-display-Add-BCM2712-HDMI-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0956-dt-bindings-display-Add-BCM2712-HDMI-bindings.patch new file mode 100644 index 0000000000..dab1fff2e3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0956-dt-bindings-display-Add-BCM2712-HDMI-bindings.patch @@ -0,0 +1,26 @@ +From aed3dadaa6fb4c38275b264ecc0ff5ebe0408b82 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + .../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: diff --git a/target/linux/bcm27xx/patches-6.1/950-0957-dt-bindings-display-Add-BCM2712-HVS-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0957-dt-bindings-display-Add-BCM2712-HVS-bindings.patch new file mode 100644 index 0000000000..e28100734f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0957-dt-bindings-display-Add-BCM2712-HVS-bindings.patch @@ -0,0 +1,34 @@ +From 2f5a75f9687553d6e91fcc09b233f5f6176b3681 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + .../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: diff --git a/target/linux/bcm27xx/patches-6.1/950-0958-dt-bindings-display-Add-BCM2712-PixelValve-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0958-dt-bindings-display-Add-BCM2712-PixelValve-bindings.patch new file mode 100644 index 0000000000..b37c1274ae --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0958-dt-bindings-display-Add-BCM2712-PixelValve-bindings.patch @@ -0,0 +1,28 @@ +From 9f26d5827745df2c59e5559fd59a5045a4ad7ce0 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + .../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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0959-dt-bindings-display-Add-BCM2712-MOP-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0959-dt-bindings-display-Add-BCM2712-MOP-bindings.patch new file mode 100644 index 0000000000..ea537522cc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0959-dt-bindings-display-Add-BCM2712-MOP-bindings.patch @@ -0,0 +1,28 @@ +From 07f90ad6a81d9ed923ee0da05541718baf49fb3c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + .../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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0960-dt-bindings-display-Add-BCM2712-MOPLET-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0960-dt-bindings-display-Add-BCM2712-MOPLET-bindings.patch new file mode 100644 index 0000000000..97aaf5bb7a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0960-dt-bindings-display-Add-BCM2712-MOPLET-bindings.patch @@ -0,0 +1,25 @@ +From e4a3722d08c723f1212bfbdcb52710de8340720a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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: diff --git a/target/linux/bcm27xx/patches-6.1/950-0961-dt-bindings-display-Add-BCM2712-KMS-driver-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-0961-dt-bindings-display-Add-BCM2712-KMS-driver-bindings.patch new file mode 100644 index 0000000000..420c66ce66 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0961-dt-bindings-display-Add-BCM2712-KMS-driver-bindings.patch @@ -0,0 +1,23 @@ +From e66d4b49a027257c347fa57ce6972f08747c0917 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 + diff --git a/target/linux/bcm27xx/patches-6.1/950-0962-drm-vc4-drv-Support-BCM2712.patch b/target/linux/bcm27xx/patches-6.1/950-0962-drm-vc4-drv-Support-BCM2712.patch new file mode 100644 index 0000000000..57a797ce18 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0962-drm-vc4-drv-Support-BCM2712.patch @@ -0,0 +1,47 @@ +From 847ec495822ad512dd9f1a58a85dabea01534855 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-0963-drm-vc4-hvs-Support-BCM2712-HVS.patch b/target/linux/bcm27xx/patches-6.1/950-0963-drm-vc4-hvs-Support-BCM2712-HVS.patch new file mode 100644 index 0000000000..9659432294 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0963-drm-vc4-hvs-Support-BCM2712-HVS.patch @@ -0,0 +1,2139 @@ +From e84da235223d0209165183c430692dde5c69854c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-0964-drm-vc4-crtc-Add-support-for-BCM2712-PixelValves.patch b/target/linux/bcm27xx/patches-6.1/950-0964-drm-vc4-crtc-Add-support-for-BCM2712-PixelValves.patch new file mode 100644 index 0000000000..1870158e06 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0964-drm-vc4-crtc-Add-support-for-BCM2712-PixelValves.patch @@ -0,0 +1,144 @@ +From 000f1b7d4dc5b515c755ee25db301e26bded00e1 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-0965-drm-vc4-hdmi-Add-support-for-BCM2712-HDMI-controller.patch b/target/linux/bcm27xx/patches-6.1/950-0965-drm-vc4-hdmi-Add-support-for-BCM2712-HDMI-controller.patch new file mode 100644 index 0000000000..7cb4e5661d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0965-drm-vc4-hdmi-Add-support-for-BCM2712-HDMI-controller.patch @@ -0,0 +1,1057 @@ +From 1bb54596ae2a9a36f4aa9f8f2ba941320f463811 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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), diff --git a/target/linux/bcm27xx/patches-6.1/950-0966-drm-vc4-txp-Introduce-structure-to-deal-with-revisio.patch b/target/linux/bcm27xx/patches-6.1/950-0966-drm-vc4-txp-Introduce-structure-to-deal-with-revisio.patch new file mode 100644 index 0000000000..c54ab0d360 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0966-drm-vc4-txp-Introduce-structure-to-deal-with-revisio.patch @@ -0,0 +1,117 @@ +From a68e8ffc3314612b0d00d491c8cdd61ae1d9af4e Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 */ }, + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0967-drm-vc4-txp-Rename-TXP-data-structure.patch b/target/linux/bcm27xx/patches-6.1/950-0967-drm-vc4-txp-Rename-TXP-data-structure.patch new file mode 100644 index 0000000000..9303f86734 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0967-drm-vc4-txp-Rename-TXP-data-structure.patch @@ -0,0 +1,66 @@ +From 7d345345b70f00bf4c673a68da7d1cf0faf5cc47 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 */ }, + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0968-drm-vc4-txp-Add-byte-enable-toggle-bit.patch b/target/linux/bcm27xx/patches-6.1/950-0968-drm-vc4-txp-Add-byte-enable-toggle-bit.patch new file mode 100644 index 0000000000..cea9062b19 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0968-drm-vc4-txp-Add-byte-enable-toggle-bit.patch @@ -0,0 +1,56 @@ +From e8dbad6d506b6fac992fdf74a7e3a66a38e554c3 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-0969-drm-vc4-txp-Add-horizontal-and-vertical-size-offset-.patch b/target/linux/bcm27xx/patches-6.1/950-0969-drm-vc4-txp-Add-horizontal-and-vertical-size-offset-.patch new file mode 100644 index 0000000000..290473e834 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0969-drm-vc4-txp-Add-horizontal-and-vertical-size-offset-.patch @@ -0,0 +1,59 @@ +From a51e4acdab01540e1006e43f38e5befb40002de0 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0970-drm-vc4-txp-Handle-40-bits-DMA-Addresses.patch b/target/linux/bcm27xx/patches-6.1/950-0970-drm-vc4-txp-Handle-40-bits-DMA-Addresses.patch new file mode 100644 index 0000000000..5d1fbcb119 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0970-drm-vc4-txp-Handle-40-bits-DMA-Addresses.patch @@ -0,0 +1,59 @@ +From ddb9aa80692ed5d35e4ee4688c36789620f78c5c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0971-drm-vc4-txp-Move-the-encoder-type-in-the-variant-str.patch b/target/linux/bcm27xx/patches-6.1/950-0971-drm-vc4-txp-Move-the-encoder-type-in-the-variant-str.patch new file mode 100644 index 0000000000..0445c4f509 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0971-drm-vc4-txp-Move-the-encoder-type-in-the-variant-str.patch @@ -0,0 +1,44 @@ +From 2e8f4fa23af4bb794e9b2284a53aa40bbfdd3cbb Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0972-drm-vc4-txp-Add-a-new-TXP-encoder-type.patch b/target/linux/bcm27xx/patches-6.1/950-0972-drm-vc4-txp-Add-a-new-TXP-encoder-type.patch new file mode 100644 index 0000000000..f832fc1ed5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0972-drm-vc4-txp-Add-a-new-TXP-encoder-type.patch @@ -0,0 +1,475 @@ +From 68a00ca7b1d7809ac7be736c02238c142e629127 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-0973-drm-vc4-txp-Add-support-for-BCM2712-MOP.patch b/target/linux/bcm27xx/patches-6.1/950-0973-drm-vc4-txp-Add-support-for-BCM2712-MOP.patch new file mode 100644 index 0000000000..aec0753408 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0973-drm-vc4-txp-Add-support-for-BCM2712-MOP.patch @@ -0,0 +1,64 @@ +From 831c6e8c68a66678e8329a382823dc83c483dcc8 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 */ }, + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0974-drm-vc4-txp-Add-BCM2712-MOPLET-support.patch b/target/linux/bcm27xx/patches-6.1/950-0974-drm-vc4-txp-Add-BCM2712-MOPLET-support.patch new file mode 100644 index 0000000000..b2a28bd144 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0974-drm-vc4-txp-Add-BCM2712-MOPLET-support.patch @@ -0,0 +1,42 @@ +From b239fc6a68fea2b073c4cb48884fbb697014ac2b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 */ }, + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0975-drm-vc4-Add-additional-warn_on.patch b/target/linux/bcm27xx/patches-6.1/950-0975-drm-vc4-Add-additional-warn_on.patch new file mode 100644 index 0000000000..0fc95e5f6b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0975-drm-vc4-Add-additional-warn_on.patch @@ -0,0 +1,240 @@ +From bb05ccd66342643b1cd9a0a48cec3ebdc3eed511 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0976-drm-vc4-tests-Switch-generation-mockup-to-a-switch.patch b/target/linux/bcm27xx/patches-6.1/950-0976-drm-vc4-tests-Switch-generation-mockup-to-a-switch.patch new file mode 100644 index 0000000000..0751d46536 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0976-drm-vc4-tests-Switch-generation-mockup-to-a-switch.patch @@ -0,0 +1,47 @@ +From 336917ca87807b8a4bb08855b4dcb0477289c765 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-0977-drm-vc4-tests-Drop-drm-parameter-for-vc4_find_crtc_f.patch b/target/linux/bcm27xx/patches-6.1/950-0977-drm-vc4-tests-Drop-drm-parameter-for-vc4_find_crtc_f.patch new file mode 100644 index 0000000000..42c4be9b8b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0977-drm-vc4-tests-Drop-drm-parameter-for-vc4_find_crtc_f.patch @@ -0,0 +1,63 @@ +From 70e906d3c688491e181446afa27bea32ce241d6a Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0978-drm-vc4-tests-Return-the-allocated-output.patch b/target/linux/bcm27xx/patches-6.1/950-0978-drm-vc4-tests-Return-the-allocated-output.patch new file mode 100644 index 0000000000..6f49d52f32 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0978-drm-vc4-tests-Return-the-allocated-output.patch @@ -0,0 +1,174 @@ +From 14e97c5765579eaab3c8372701750ffa30e4c7da Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0979-drm-vc4-tests-Add-BCM2712-mock-driver.patch b/target/linux/bcm27xx/patches-6.1/950-0979-drm-vc4-tests-Add-BCM2712-mock-driver.patch new file mode 100644 index 0000000000..8c12375284 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0979-drm-vc4-tests-Add-BCM2712-mock-driver.patch @@ -0,0 +1,87 @@ +From 04bec005b049604f862765b35ebd71c2a69b9e7c Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-0980-drm-vc4-tests-Add-tests-for-BCM2712-PixelValve-Muxin.patch b/target/linux/bcm27xx/patches-6.1/950-0980-drm-vc4-tests-Add-tests-for-BCM2712-PixelValve-Muxin.patch new file mode 100644 index 0000000000..6c0db592c9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0980-drm-vc4-tests-Add-tests-for-BCM2712-PixelValve-Muxin.patch @@ -0,0 +1,138 @@ +From 77c14764ae164b7969e65437a87aca25b30d8b80 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + .../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 + ); diff --git a/target/linux/bcm27xx/patches-6.1/950-0981-drm-vc4-fkms-Rename-plane-related-functions.patch b/target/linux/bcm27xx/patches-6.1/950-0981-drm-vc4-fkms-Rename-plane-related-functions.patch new file mode 100644 index 0000000000..455c64ad2e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0981-drm-vc4-fkms-Rename-plane-related-functions.patch @@ -0,0 +1,64 @@ +From 3d849ab48cecab55862a4f2742f11937d66ba54b Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0982-drm-vc4-tests-Use-custom-plane-state-for-mock.patch b/target/linux/bcm27xx/patches-6.1/950-0982-drm-vc4-tests-Use-custom-plane-state-for-mock.patch new file mode 100644 index 0000000000..0105753a91 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0982-drm-vc4-tests-Use-custom-plane-state-for-mock.patch @@ -0,0 +1,95 @@ +From 14fe42ff341741d60ba338c401855dee7fb68754 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0983-drm-vc4-tests-Add-function-to-lookup-a-plane-for-a-C.patch b/target/linux/bcm27xx/patches-6.1/950-0983-drm-vc4-tests-Add-function-to-lookup-a-plane-for-a-C.patch new file mode 100644 index 0000000000..ebab218cf3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0983-drm-vc4-tests-Add-function-to-lookup-a-plane-for-a-C.patch @@ -0,0 +1,36 @@ +From 3320e449e40eeb49b601dcbbd4bd72b8cb8f3054 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0984-drm-vc4-tests-Add-helper-to-add-a-new-plane-to-a-sta.patch b/target/linux/bcm27xx/patches-6.1/950-0984-drm-vc4-tests-Add-helper-to-add-a-new-plane-to-a-sta.patch new file mode 100644 index 0000000000..7d230294fc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0984-drm-vc4-tests-Add-helper-to-add-a-new-plane-to-a-sta.patch @@ -0,0 +1,62 @@ +From a2f912c44b98acb6c10c977db105e199011c09b5 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 ++#include + #include + #include + #include +@@ -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; ++} diff --git a/target/linux/bcm27xx/patches-6.1/950-0985-drm-vc4-tests-Support-a-few-more-plane-formats.patch b/target/linux/bcm27xx/patches-6.1/950-0985-drm-vc4-tests-Support-a-few-more-plane-formats.patch new file mode 100644 index 0000000000..191181dbb6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0985-drm-vc4-tests-Support-a-few-more-plane-formats.patch @@ -0,0 +1,26 @@ +From 418d2e0e652c3870c29dd2e462f052a88fa027da Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0986-drm-vc4-tests-Introduce-a-test-for-LBM-buffer-size.patch b/target/linux/bcm27xx/patches-6.1/950-0986-drm-vc4-tests-Introduce-a-test-for-LBM-buffer-size.patch new file mode 100644 index 0000000000..5902d141ef --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0986-drm-vc4-tests-Introduce-a-test-for-LBM-buffer-size.patch @@ -0,0 +1,358 @@ +From 39ae36d19bf6e59e3091f8f0ec6f4eac59aaf6f2 Mon Sep 17 00:00:00 2001 +From: Maxime Ripard +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "../../drm_crtc_internal.h" ++#include "../../drm_internal.h" ++ ++#include ++ ++#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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0987-drm-vc4-kms-Avoid-setting-core-and-disp-clocks-for-h.patch b/target/linux/bcm27xx/patches-6.1/950-0987-drm-vc4-kms-Avoid-setting-core-and-disp-clocks-for-h.patch new file mode 100644 index 0000000000..f20b0cf3f8 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0987-drm-vc4-kms-Avoid-setting-core-and-disp-clocks-for-h.patch @@ -0,0 +1,40 @@ +From 352d96c9e50012f2b5e5dde9933af8d570e7dc81 Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-0988-drm-vc4-Assign-LBM-memory-during-atomic_flush.patch b/target/linux/bcm27xx/patches-6.1/950-0988-drm-vc4-Assign-LBM-memory-during-atomic_flush.patch new file mode 100644 index 0000000000..b489bbc7f7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0988-drm-vc4-Assign-LBM-memory-during-atomic_flush.patch @@ -0,0 +1,240 @@ +From bb0839405b61da6e6ae7141f7433f6a121725e6f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0989-drm-panel-simple-Alter-the-timing-for-the-Pi-7-DSI-d.patch b/target/linux/bcm27xx/patches-6.1/950-0989-drm-panel-simple-Alter-the-timing-for-the-Pi-7-DSI-d.patch new file mode 100644 index 0000000000..ef70de7ee4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0989-drm-panel-simple-Alter-the-timing-for-the-Pi-7-DSI-d.patch @@ -0,0 +1,33 @@ +From b1bd2f406eab321be642decd6aee6b6222aec62b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0990-drm-panel-waveshare-Fix-up-timings-for-10.1-panel.patch b/target/linux/bcm27xx/patches-6.1/950-0990-drm-panel-waveshare-Fix-up-timings-for-10.1-panel.patch new file mode 100644 index 0000000000..0a4203fbc5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0990-drm-panel-waveshare-Fix-up-timings-for-10.1-panel.patch @@ -0,0 +1,33 @@ +From c7cf33911d477fe55a91a9e4d84dad857b244ae3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-0991-media-i2c-imx477-Fix-locking-in-imx477_init_controls.patch b/target/linux/bcm27xx/patches-6.1/950-0991-media-i2c-imx477-Fix-locking-in-imx477_init_controls.patch new file mode 100644 index 0000000000..9d7869af9c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0991-media-i2c-imx477-Fix-locking-in-imx477_init_controls.patch @@ -0,0 +1,34 @@ +From 72c25bbb761de2b2acd9f8b652d63e2a2f1caeed Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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: diff --git a/target/linux/bcm27xx/patches-6.1/950-0994-overlays-Fix-vc4-kms-dsi-7inch.patch b/target/linux/bcm27xx/patches-6.1/950-0994-overlays-Fix-vc4-kms-dsi-7inch.patch new file mode 100644 index 0000000000..10339e2774 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0994-overlays-Fix-vc4-kms-dsi-7inch.patch @@ -0,0 +1,57 @@ +From 2ff65ffbdeb0c8764985af19df2a687a126136f4 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-0996-ASoC-hdmi-codec-Fix-broken-channel-map-reporting.patch b/target/linux/bcm27xx/patches-6.1/950-0996-ASoC-hdmi-codec-Fix-broken-channel-map-reporting.patch new file mode 100644 index 0000000000..c8fd2a2f90 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0996-ASoC-hdmi-codec-Fix-broken-channel-map-reporting.patch @@ -0,0 +1,56 @@ +From a1caea3c996f6bfa8c9568f521257f4e473285fb Mon Sep 17 00:00:00 2001 +From: Matthias Reichl +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 +Link: https://lore.kernel.org/r/20230929195027.97136-1-hias@horus.com +Signed-off-by: Mark Brown +--- + 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; + } diff --git a/target/linux/bcm27xx/patches-6.1/950-0997-media-rp1-cfe-Fix-use-of-freed-memory-on-errors.patch b/target/linux/bcm27xx/patches-6.1/950-0997-media-rp1-cfe-Fix-use-of-freed-memory-on-errors.patch new file mode 100644 index 0000000000..7f1b505336 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0997-media-rp1-cfe-Fix-use-of-freed-memory-on-errors.patch @@ -0,0 +1,48 @@ +From 3922bebc11fcc8459c798cfcb582828f9bbaa9e9 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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; + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-0998-media-rp1-cfe-Fix-width-height-in-cfe_start_channel.patch b/target/linux/bcm27xx/patches-6.1/950-0998-media-rp1-cfe-Fix-width-height-in-cfe_start_channel.patch new file mode 100644 index 0000000000..d49856c32d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0998-media-rp1-cfe-Fix-width-height-in-cfe_start_channel.patch @@ -0,0 +1,88 @@ +From 84c9958dd71b8a4dcf16cbf6fdb867c668652634 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-0999-media-rp1-csi2-Fix-missing-reg-writes.patch b/target/linux/bcm27xx/patches-6.1/950-0999-media-rp1-csi2-Fix-missing-reg-writes.patch new file mode 100644 index 0000000000..2a3915b6e3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-0999-media-rp1-csi2-Fix-missing-reg-writes.patch @@ -0,0 +1,36 @@ +From 62e8ab88d2c230dad122aabe2ad0e227d7ceba40 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1000-media-rp1-fe-Use-0-not-1-when-working-with-unsigned-.patch b/target/linux/bcm27xx/patches-6.1/950-1000-media-rp1-fe-Use-0-not-1-when-working-with-unsigned-.patch new file mode 100644 index 0000000000..f468b7ac48 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1000-media-rp1-fe-Use-0-not-1-when-working-with-unsigned-.patch @@ -0,0 +1,33 @@ +From 455c4ae2c70348a5842835d2f67f7cd8e665a2a6 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1001-media-rp1-cfe-Fix-verbose-debug-print.patch b/target/linux/bcm27xx/patches-6.1/950-1001-media-rp1-cfe-Fix-verbose-debug-print.patch new file mode 100644 index 0000000000..3344c32d29 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1001-media-rp1-cfe-Fix-verbose-debug-print.patch @@ -0,0 +1,26 @@ +From bf1709d2cf2b57c4ea98b8363156835249ae02cc Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1002-media-rp1-cfe-Rename-xxx_dbg_irq-to-xxx_dbg_verbose.patch b/target/linux/bcm27xx/patches-6.1/950-1002-media-rp1-cfe-Rename-xxx_dbg_irq-to-xxx_dbg_verbose.patch new file mode 100644 index 0000000000..14fa0447df --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1002-media-rp1-cfe-Rename-xxx_dbg_irq-to-xxx_dbg_verbose.patch @@ -0,0 +1,227 @@ +From a1ea528e187ee045aeff929ff0f4b2e53fdd970f Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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 + #include + +-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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1003-media-rp1-Add-back-reg-write-debug-prints.patch b/target/linux/bcm27xx/patches-6.1/950-1003-media-rp1-Add-back-reg-write-debug-prints.patch new file mode 100644 index 0000000000..c86f7a3e17 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1003-media-rp1-Add-back-reg-write-debug-prints.patch @@ -0,0 +1,41 @@ +From 8240f1328ead0152f116b385b3169f8f010a7869 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1004-media-rp1-cfe-Add-verbose-debug-module-parameter.patch b/target/linux/bcm27xx/patches-6.1/950-1004-media-rp1-cfe-Add-verbose-debug-module-parameter.patch new file mode 100644 index 0000000000..f44cb50d10 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1004-media-rp1-cfe-Add-verbose-debug-module-parameter.patch @@ -0,0 +1,23 @@ +From f2553458c0c1942731447aac3878f51aa1b326a7 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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 { \ diff --git a/target/linux/bcm27xx/patches-6.1/950-1005-media-rp1-csi2-Track-CSI-2-errors.patch b/target/linux/bcm27xx/patches-6.1/950-1005-media-rp1-csi2-Track-CSI-2-errors.patch new file mode 100644 index 0000000000..9d8da81887 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1005-media-rp1-csi2-Track-CSI-2-errors.patch @@ -0,0 +1,245 @@ +From b19c2b5f88f141e58044e5d1012f867d46f74bf3 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1006-media-rp1-cfe-Drop-unused-field.patch b/target/linux/bcm27xx/patches-6.1/950-1006-media-rp1-cfe-Drop-unused-field.patch new file mode 100644 index 0000000000..b53a5b6154 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1006-media-rp1-cfe-Drop-unused-field.patch @@ -0,0 +1,31 @@ +From d6bd676b49cf10046665efde820b0cc00dc43994 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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) { diff --git a/target/linux/bcm27xx/patches-6.1/950-1007-media-rp1-csi2-Set-values-for-enum-csi2_mode.patch b/target/linux/bcm27xx/patches-6.1/950-1007-media-rp1-csi2-Set-values-for-enum-csi2_mode.patch new file mode 100644 index 0000000000..126ee2c493 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1007-media-rp1-csi2-Set-values-for-enum-csi2_mode.patch @@ -0,0 +1,30 @@ +From c56b53d62116b4672962124afab02f3beada4244 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1008-media-rp1-fe-Fix-default-mbus-code.patch b/target/linux/bcm27xx/patches-6.1/950-1008-media-rp1-fe-Fix-default-mbus-code.patch new file mode 100644 index 0000000000..e4ea9e1d16 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1008-media-rp1-fe-Fix-default-mbus-code.patch @@ -0,0 +1,27 @@ +From 5edacf33de9e0349dd5a02ad684bfceae1d5565f Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1009-media-rp1-cfe-Fix-default-meta-format-s-field.patch b/target/linux/bcm27xx/patches-6.1/950-1009-media-rp1-cfe-Fix-default-meta-format-s-field.patch new file mode 100644 index 0000000000..2ac5e0e88a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1009-media-rp1-cfe-Fix-default-meta-format-s-field.patch @@ -0,0 +1,25 @@ +From 47fd9528bc1d93dd07ce9baa19c736966b536bd9 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1010-media-rp1-cfe-Fail-streaming-if-FE_CONFIG-node-is-no.patch b/target/linux/bcm27xx/patches-6.1/950-1010-media-rp1-cfe-Fail-streaming-if-FE_CONFIG-node-is-no.patch new file mode 100644 index 0000000000..de469c3565 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1010-media-rp1-cfe-Fail-streaming-if-FE_CONFIG-node-is-no.patch @@ -0,0 +1,31 @@ +From 3e2203b265ddd8630fea0fbb69b3a2ec1496f773 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1011-media-i2c-Move-Kconfig-entry-for-IMX477-to-the-camer.patch b/target/linux/bcm27xx/patches-6.1/950-1011-media-i2c-Move-Kconfig-entry-for-IMX477-to-the-camer.patch new file mode 100644 index 0000000000..37e1a86e16 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1011-media-i2c-Move-Kconfig-entry-for-IMX477-to-the-camer.patch @@ -0,0 +1,51 @@ +From 52811174f534824f5e5bcdb681f0c508aa335244 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1013-drm-Look-for-an-alias-for-the-displays-to-use-as-the.patch b/target/linux/bcm27xx/patches-6.1/950-1013-drm-Look-for-an-alias-for-the-displays-to-use-as-the.patch new file mode 100644 index 0000000000..8de6c372c0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1013-drm-Look-for-an-alias-for-the-displays-to-use-as-the.patch @@ -0,0 +1,109 @@ +From 3aa1f2477545ea6298bc6f2d7ffae68f090af9b8 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1014-dt-Add-DSI1-and-DSI2-aliases-to-2712.patch b/target/linux/bcm27xx/patches-6.1/950-1014-dt-Add-DSI1-and-DSI2-aliases-to-2712.patch new file mode 100644 index 0000000000..6d79cb6c4d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1014-dt-Add-DSI1-and-DSI2-aliases-to-2712.patch @@ -0,0 +1,24 @@ +From 7ec42740a45b21bca859cde5b7cbe2f09ef3d586 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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__ { diff --git a/target/linux/bcm27xx/patches-6.1/950-1015-vc4-drm-Remove-the-clear-of-SCALER_DISPBKGND_FILL.patch b/target/linux/bcm27xx/patches-6.1/950-1015-vc4-drm-Remove-the-clear-of-SCALER_DISPBKGND_FILL.patch new file mode 100644 index 0000000000..a4c8aeadd3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1015-vc4-drm-Remove-the-clear-of-SCALER_DISPBKGND_FILL.patch @@ -0,0 +1,64 @@ +From e6e0631fdeb0cd7d4c50e629b4b298e0b76e885b Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1017-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch b/target/linux/bcm27xx/patches-6.1/950-1017-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch new file mode 100644 index 0000000000..9bf9e6d96d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1017-gpio-brcmstb-Use-dynamic-GPIO-base-numbers.patch @@ -0,0 +1,117 @@ +From 450d617786926b27c25e930241efbd2e37d66bb8 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1018-media-rpivid-Allow-use-of-iommu-in-rpivid.patch b/target/linux/bcm27xx/patches-6.1/950-1018-media-rpivid-Allow-use-of-iommu-in-rpivid.patch new file mode 100644 index 0000000000..a6d199a8c7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1018-media-rpivid-Allow-use-of-iommu-in-rpivid.patch @@ -0,0 +1,57 @@ +From 1770efc97981dbf756a18f5eb7615c4bafab665b Mon Sep 17 00:00:00 2001 +From: John Cox +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-1019-dts-bcm2712-Add-iommu-to-rpivid.patch b/target/linux/bcm27xx/patches-6.1/950-1019-dts-bcm2712-Add-iommu-to-rpivid.patch new file mode 100644 index 0000000000..9f7c8674fb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1019-dts-bcm2712-Add-iommu-to-rpivid.patch @@ -0,0 +1,22 @@ +From 10ff8ba24c68f704ecf5a58878617dfe149a14c6 Mon Sep 17 00:00:00 2001 +From: John Cox +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1020-drivers-media-rp1_cfe-Remove-PISP-specific-MBUS-form.patch b/target/linux/bcm27xx/patches-6.1/950-1020-drivers-media-rp1_cfe-Remove-PISP-specific-MBUS-form.patch new file mode 100644 index 0000000000..fabc96e422 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1020-drivers-media-rp1_cfe-Remove-PISP-specific-MBUS-form.patch @@ -0,0 +1,117 @@ +From b5c3cc7fd9fca73352310e61092fb445b56a362a Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-1022-vc04_services-bcm2835-codec-Correct-alignment-requir.patch b/target/linux/bcm27xx/patches-6.1/950-1022-vc04_services-bcm2835-codec-Correct-alignment-requir.patch new file mode 100644 index 0000000000..313ca37738 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1022-vc04_services-bcm2835-codec-Correct-alignment-requir.patch @@ -0,0 +1,53 @@ +From 9f4002165439d02a63760e68948246e3af764318 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + .../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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1025-input-touchscreen-edt-ft5x06-Suppress-bogus-data-on-.patch b/target/linux/bcm27xx/patches-6.1/950-1025-input-touchscreen-edt-ft5x06-Suppress-bogus-data-on-.patch new file mode 100644 index 0000000000..fe956c2ed4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1025-input-touchscreen-edt-ft5x06-Suppress-bogus-data-on-.patch @@ -0,0 +1,96 @@ +From dd802fc03b1c71b4297e87068077bfcf9810300a Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-1029-overlays-mcp23017-allow-specification-of-the-i2c-bus.patch b/target/linux/bcm27xx/patches-6.1/950-1029-overlays-mcp23017-allow-specification-of-the-i2c-bus.patch new file mode 100644 index 0000000000..8cb35e1a79 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1029-overlays-mcp23017-allow-specification-of-the-i2c-bus.patch @@ -0,0 +1,97 @@ +From 27f0e0e195568c06f21ce380f0736bdd219baf3c Mon Sep 17 00:00:00 2001 +From: Janis Streib +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"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1030-dts-bcm2712-Set-default-I2C-baudrates-to-100kHz.patch b/target/linux/bcm27xx/patches-6.1/950-1030-dts-bcm2712-Set-default-I2C-baudrates-to-100kHz.patch new file mode 100644 index 0000000000..e9422f1daa --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1030-dts-bcm2712-Set-default-I2C-baudrates-to-100kHz.patch @@ -0,0 +1,46 @@ +From 0339c8c61ca6b54c529f699e7bafd65cda9f3733 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1031-vc_mem-Add-the-DMA-memcpy-support-from-bcm2708_fb.patch b/target/linux/bcm27xx/patches-6.1/950-1031-vc_mem-Add-the-DMA-memcpy-support-from-bcm2708_fb.patch new file mode 100644 index 0000000000..23d428789f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1031-vc_mem-Add-the-DMA-memcpy-support-from-bcm2708_fb.patch @@ -0,0 +1,345 @@ +From 2a47ccf97c6a91bc56f8cfb387d47f59cc347dd5 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 + #include + #include ++#include ++#include ++#include + + #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; + } + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-1032-drm-vc4-Correct-address-offset-for-planes-with-src_-.patch b/target/linux/bcm27xx/patches-6.1/950-1032-drm-vc4-Correct-address-offset-for-planes-with-src_-.patch new file mode 100644 index 0000000000..32112f949b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1032-drm-vc4-Correct-address-offset-for-planes-with-src_-.patch @@ -0,0 +1,48 @@ +From 6ab30a5dd3fb2ccbd918f147e91eb9bfe9849970 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1033-drivers-media-rp1_cfe-Fix-link-validate-test-for-pix.patch b/target/linux/bcm27xx/patches-6.1/950-1033-drivers-media-rp1_cfe-Fix-link-validate-test-for-pix.patch new file mode 100644 index 0000000000..3e1d504da0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1033-drivers-media-rp1_cfe-Fix-link-validate-test-for-pix.patch @@ -0,0 +1,69 @@ +From c14550658832026e82111f2a89b3f7bf567afc1c Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1034-dts-bcm2712-Use-the-new-model-name.patch b/target/linux/bcm27xx/patches-6.1/950-1034-dts-bcm2712-Use-the-new-model-name.patch new file mode 100644 index 0000000000..f30456835a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1034-dts-bcm2712-Use-the-new-model-name.patch @@ -0,0 +1,23 @@ +From b6bfece0d9ddf21e1526fead81340ef02f98f6ad Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1035-fbdev-Allow-client-to-request-a-particular-dev-fbN-n.patch b/target/linux/bcm27xx/patches-6.1/950-1035-fbdev-Allow-client-to-request-a-particular-dev-fbN-n.patch new file mode 100644 index 0000000000..df87a1b757 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1035-fbdev-Allow-client-to-request-a-particular-dev-fbN-n.patch @@ -0,0 +1,93 @@ +From 0c7fb448e0e0e47c2b3be64e438208682c6a5e61 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1036-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch b/target/linux/bcm27xx/patches-6.1/950-1036-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch new file mode 100644 index 0000000000..3e360be77b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1036-drm-fb-helper-Look-up-preferred-fbdev-node-number-fr.patch @@ -0,0 +1,41 @@ +From 1216ea56c2e30aee4975b4dcce79ebd199afaf8f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1037-dt-Add-overrides-for-drm-framebuffer-allocations-on-.patch b/target/linux/bcm27xx/patches-6.1/950-1037-dt-Add-overrides-for-drm-framebuffer-allocations-on-.patch new file mode 100644 index 0000000000..924c1b9f19 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1037-dt-Add-overrides-for-drm-framebuffer-allocations-on-.patch @@ -0,0 +1,72 @@ +From 61b138adaeaddefe749f421a3b69c67d4a49a8e3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1039-drm-connector-Change-DRM-card-alias-from-underscore-.patch b/target/linux/bcm27xx/patches-6.1/950-1039-drm-connector-Change-DRM-card-alias-from-underscore-.patch new file mode 100644 index 0000000000..57782d51c3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1039-drm-connector-Change-DRM-card-alias-from-underscore-.patch @@ -0,0 +1,26 @@ +From f429fc1a072d4bb35e622a1012a5a52914eba4e3 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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) + { diff --git a/target/linux/bcm27xx/patches-6.1/950-1040-dt-Alter-alias-names-from-_-to-for-drm_dsiN.patch b/target/linux/bcm27xx/patches-6.1/950-1040-dt-Alter-alias-names-from-_-to-for-drm_dsiN.patch new file mode 100644 index 0000000000..0536924d05 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1040-dt-Alter-alias-names-from-_-to-for-drm_dsiN.patch @@ -0,0 +1,24 @@ +From e33170e21494279801e9f17eeb910ec45ebd740c Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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__ { diff --git a/target/linux/bcm27xx/patches-6.1/950-1041-drm-fb_helper-Change-query-for-FB-designation-from-d.patch b/target/linux/bcm27xx/patches-6.1/950-1041-drm-fb_helper-Change-query-for-FB-designation-from-d.patch new file mode 100644 index 0000000000..78e5b89be4 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1041-drm-fb_helper-Change-query-for-FB-designation-from-d.patch @@ -0,0 +1,28 @@ +From 0e9e925112fabdbd448e17947796317a6dbca96e Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1042-dt-Alter-alias-names-from-_-to-for-drm_fbN_-override.patch b/target/linux/bcm27xx/patches-6.1/950-1042-dt-Alter-alias-names-from-_-to-for-drm_fbN_-override.patch new file mode 100644 index 0000000000..42865dc7ff --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1042-dt-Alter-alias-names-from-_-to-for-drm_fbN_-override.patch @@ -0,0 +1,43 @@ +From 87be94059193d0bc64d52a9535df4fb1891c72fe Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1044-Typo-in-overlays-README.patch b/target/linux/bcm27xx/patches-6.1/950-1044-Typo-in-overlays-README.patch new file mode 100644 index 0000000000..19720e8645 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1044-Typo-in-overlays-README.patch @@ -0,0 +1,21 @@ +From cb8a4adb586c5e926e415ac0dae3ffb4af30b0a9 Mon Sep 17 00:00:00 2001 +From: Andrew Scheller +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,= + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1045-dts-bcm2712-Add-the-krnbt-parameter.patch b/target/linux/bcm27xx/patches-6.1/950-1045-dts-bcm2712-Add-the-krnbt-parameter.patch new file mode 100644 index 0000000000..95940dd830 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1045-dts-bcm2712-Add-the-krnbt-parameter.patch @@ -0,0 +1,23 @@ +From 4cb97982f88d8fb623ace5a9511198e442e993ba Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-1047-drm-vc4_fkms-Fix-up-interrupt-handler-for-both-2835-.patch b/target/linux/bcm27xx/patches-6.1/950-1047-drm-vc4_fkms-Fix-up-interrupt-handler-for-both-2835-.patch new file mode 100644 index 0000000000..99b8426e1c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1047-drm-vc4_fkms-Fix-up-interrupt-handler-for-both-2835-.patch @@ -0,0 +1,109 @@ +From 4137b49989ce710305e476d0bd1086d7d906ff50 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1048-dt-Switch-bcm2712-firmware-kms-node-to-using-the-271.patch b/target/linux/bcm27xx/patches-6.1/950-1048-dt-Switch-bcm2712-firmware-kms-node-to-using-the-271.patch new file mode 100644 index 0000000000..9dea2d0fff --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1048-dt-Switch-bcm2712-firmware-kms-node-to-using-the-271.patch @@ -0,0 +1,25 @@ +From 5a52cae54a05499a8487f392cf5dfc3d8a837e6f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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>; diff --git a/target/linux/bcm27xx/patches-6.1/950-1049-drivers-media-imx477-Disable-the-scaler.patch b/target/linux/bcm27xx/patches-6.1/950-1049-drivers-media-imx477-Disable-the-scaler.patch new file mode 100644 index 0000000000..841fa42440 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1049-drivers-media-imx477-Disable-the-scaler.patch @@ -0,0 +1,35 @@ +From f075893e9b0e241879998c0b12cf8af0ba7737da Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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}, diff --git a/target/linux/bcm27xx/patches-6.1/950-1050-dt-Add-drm_fbN_vc4-overrides-for-Pi0-4.patch b/target/linux/bcm27xx/patches-6.1/950-1050-dt-Add-drm_fbN_vc4-overrides-for-Pi0-4.patch new file mode 100644 index 0000000000..77f30a345d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1050-dt-Add-drm_fbN_vc4-overrides-for-Pi0-4.patch @@ -0,0 +1,37 @@ +From eccaa8588fca9c9ec950664f1d5894bd826b57b0 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1051-fixup-overlays-mcp23017-allow-specification-of-the-i.patch b/target/linux/bcm27xx/patches-6.1/950-1051-fixup-overlays-mcp23017-allow-specification-of-the-i.patch new file mode 100644 index 0000000000..5016e78778 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1051-fixup-overlays-mcp23017-allow-specification-of-the-i.patch @@ -0,0 +1,95 @@ +From 3ed6d34d53e94ecbebc64c8fa3d1b6d3c41db8fb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 + +(*) 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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-1052-drivers-media-pisp_be-Add-back-V4L2_PIX_FMT_RPI_BE-f.patch b/target/linux/bcm27xx/patches-6.1/950-1052-drivers-media-pisp_be-Add-back-V4L2_PIX_FMT_RPI_BE-f.patch new file mode 100644 index 0000000000..7fe0c50740 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1052-drivers-media-pisp_be-Add-back-V4L2_PIX_FMT_RPI_BE-f.patch @@ -0,0 +1,54 @@ +From 8d53cc5b4b2a6f9baed7a0aa801a39ad9dce9bf8 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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[] = { diff --git a/target/linux/bcm27xx/patches-6.1/950-1053-dt-bindings-PCI-brcmstb-add-optional-property-brcm-t.patch b/target/linux/bcm27xx/patches-6.1/950-1053-dt-bindings-PCI-brcmstb-add-optional-property-brcm-t.patch new file mode 100644 index 0000000000..3ef42e7967 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1053-dt-bindings-PCI-brcmstb-add-optional-property-brcm-t.patch @@ -0,0 +1,32 @@ +From f1154884295a4bd00d6ebcaf01fa30141145903d Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1054-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch b/target/linux/bcm27xx/patches-6.1/950-1054-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch new file mode 100644 index 0000000000..021e210591 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1054-drivers-pci-brcmstb-optionally-extend-Tperst_clk-tim.patch @@ -0,0 +1,71 @@ +From 4b0c6453808a662869a43c504913f3b7ed64486a Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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) { diff --git a/target/linux/bcm27xx/patches-6.1/950-1055-arm-dt-add-dtparams-for-PCIe-reset-timing-override.patch b/target/linux/bcm27xx/patches-6.1/950-1055-arm-dt-add-dtparams-for-PCIe-reset-timing-override.patch new file mode 100644 index 0000000000..a81d0383ac --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1055-arm-dt-add-dtparams-for-PCIe-reset-timing-override.patch @@ -0,0 +1,59 @@ +From db90a5e5fc2fbd843b29eb8110ed5e03604a2887 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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") + diff --git a/target/linux/bcm27xx/patches-6.1/950-1056-arm-dt-bcm2712-don-t-unconditionally-enable-MPS-read.patch b/target/linux/bcm27xx/patches-6.1/950-1056-arm-dt-bcm2712-don-t-unconditionally-enable-MPS-read.patch new file mode 100644 index 0000000000..b08a5720d5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1056-arm-dt-bcm2712-don-t-unconditionally-enable-MPS-read.patch @@ -0,0 +1,35 @@ +From cb013b6602de32c647ed08faf899596664a18635 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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"; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1057-drivers-media-imx477-Set-horizontal-binning-when-dis.patch b/target/linux/bcm27xx/patches-6.1/950-1057-drivers-media-imx477-Set-horizontal-binning-when-dis.patch new file mode 100644 index 0000000000..e6122e1a6b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1057-drivers-media-imx477-Set-horizontal-binning-when-dis.patch @@ -0,0 +1,39 @@ +From 8dcc16f0adc50f5cb8a11a6dde238131d0ca45a0 Mon Sep 17 00:00:00 2001 +From: David Plowman +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 +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}, diff --git a/target/linux/bcm27xx/patches-6.1/950-1058-fixup-arch-arm64-Add-Revision-Serial-Model-to-cpuinf.patch b/target/linux/bcm27xx/patches-6.1/950-1058-fixup-arch-arm64-Add-Revision-Serial-Model-to-cpuinf.patch new file mode 100644 index 0000000000..adedaf6d4b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1058-fixup-arch-arm64-Add-Revision-Serial-Model-to-cpuinf.patch @@ -0,0 +1,25 @@ +From e641fd7a50987ad6b7ce1ab36189bc8817295e42 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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)) diff --git a/target/linux/bcm27xx/patches-6.1/950-1060-dts-bcm2710-rpi-zero-2-w-Remove-WLAN-firmwares.patch b/target/linux/bcm27xx/patches-6.1/950-1060-dts-bcm2710-rpi-zero-2-w-Remove-WLAN-firmwares.patch new file mode 100644 index 0000000000..18c12cd10f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1060-dts-bcm2710-rpi-zero-2-w-Remove-WLAN-firmwares.patch @@ -0,0 +1,35 @@ +From e86c43b86179fba90a1d9dd5acb554767af6740f Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; +- }; +- }; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1061-drivers-media-cfe-Set-the-CSI-2-link-frequency-corre.patch b/target/linux/bcm27xx/patches-6.1/950-1061-drivers-media-cfe-Set-the-CSI-2-link-frequency-corre.patch new file mode 100644 index 0000000000..d5aeae5333 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1061-drivers-media-cfe-Set-the-CSI-2-link-frequency-corre.patch @@ -0,0 +1,111 @@ +From a11312709f46b71bf320a9dcc8cf4e09056552cd Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1062-dts-bcm2712-rpi-5-b-Create-some-dummy-nodes.patch b/target/linux/bcm27xx/patches-6.1/950-1062-dts-bcm2712-rpi-5-b-Create-some-dummy-nodes.patch new file mode 100644 index 0000000000..722714d56a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1062-dts-bcm2712-rpi-5-b-Create-some-dummy-nodes.patch @@ -0,0 +1,42 @@ +From fb78b2617e70e58e03e0d50e674758fdd2a80d45 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-1063-dts-rp1-Add-spi6-fix-spi1-address-cells.patch b/target/linux/bcm27xx/patches-6.1/950-1063-dts-rp1-Add-spi6-fix-spi1-address-cells.patch new file mode 100644 index 0000000000..41ab025555 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1063-dts-rp1-Add-spi6-fix-spi1-address-cells.patch @@ -0,0 +1,49 @@ +From cd66a0832351762b496bdce6f2f94a871d11484e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 = ; + 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 = ; ++ 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 { diff --git a/target/linux/bcm27xx/patches-6.1/950-1064-overlays-uart-n-pi5-Add-the-pinctrl-0-property.patch b/target/linux/bcm27xx/patches-6.1/950-1064-overlays-uart-n-pi5-Add-the-pinctrl-0-property.patch new file mode 100644 index 0000000000..b6e0c323db --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1064-overlays-uart-n-pi5-Add-the-pinctrl-0-property.patch @@ -0,0 +1,67 @@ +From 17f135b742c4edb340afb365873c3a574f7e16cb Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Thu, 2 Nov 2023 17:05:46 +0000 +Subject: [PATCH] overlays: uart-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 +--- + 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>; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1065-drivers-media-imx477-Add-V4L2_CID_LINK_FREQ-control.patch b/target/linux/bcm27xx/patches-6.1/950-1065-drivers-media-imx477-Add-V4L2_CID_LINK_FREQ-control.patch new file mode 100644 index 0000000000..f0a652b520 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1065-drivers-media-imx477-Add-V4L2_CID_LINK_FREQ-control.patch @@ -0,0 +1,51 @@ +From c9a785d57c302d5f1d4de4e67fa57522e66c7882 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1066-drivers-media-imx477-Correctly-set-IMX477_PIXEL_RATE.patch b/target/linux/bcm27xx/patches-6.1/950-1066-drivers-media-imx477-Correctly-set-IMX477_PIXEL_RATE.patch new file mode 100644 index 0000000000..f62fbbe13d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1066-drivers-media-imx477-Correctly-set-IMX477_PIXEL_RATE.patch @@ -0,0 +1,24 @@ +From 46913ee0590ee0e3f607ab189be19a4d0ce785f2 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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 = diff --git a/target/linux/bcm27xx/patches-6.1/950-1067-drm-vc4-Correct-logic-on-stopping-an-HVS-channel.patch b/target/linux/bcm27xx/patches-6.1/950-1067-drm-vc4-Correct-logic-on-stopping-an-HVS-channel.patch new file mode 100644 index 0000000000..948d79bfdb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1067-drm-vc4-Correct-logic-on-stopping-an-HVS-channel.patch @@ -0,0 +1,52 @@ +From 6e9f68bba01b9c36a77b68c4b3167c317da986da Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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), diff --git a/target/linux/bcm27xx/patches-6.1/950-1068-drm-vc4-Drop-WARN-for-HVS-FIFOs-not-being-empty.patch b/target/linux/bcm27xx/patches-6.1/950-1068-drm-vc4-Drop-WARN-for-HVS-FIFOs-not-being-empty.patch new file mode 100644 index 0000000000..627f22c845 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1068-drm-vc4-Drop-WARN-for-HVS-FIFOs-not-being-empty.patch @@ -0,0 +1,30 @@ +From 31c4c359aa2dbb1a7c095f0a6ef4e13cd46cfd14 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-1069-drm-vc4-Free-all-stale-dlists-if-channel-is-disabled.patch b/target/linux/bcm27xx/patches-6.1/950-1069-drm-vc4-Free-all-stale-dlists-if-channel-is-disabled.patch new file mode 100644 index 0000000000..ce03224c39 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1069-drm-vc4-Free-all-stale-dlists-if-channel-is-disabled.patch @@ -0,0 +1,85 @@ +From 8b7078d1bbd8bb548cc97d5214adb828e9f0037c Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1070-drm-vc4-Add-hvs_dlist_allocs-debugfs-function.patch b/target/linux/bcm27xx/patches-6.1/950-1070-drm-vc4-Add-hvs_dlist_allocs-debugfs-function.patch new file mode 100644 index 0000000000..b16489a12d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1070-drm-vc4-Add-hvs_dlist_allocs-debugfs-function.patch @@ -0,0 +1,64 @@ +From 665e9810340abc37769b317445907bac1843dd64 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1071-drm-vc4-Log-the-size-of-the-dlist-allocation-that-wa.patch b/target/linux/bcm27xx/patches-6.1/950-1071-drm-vc4-Log-the-size-of-the-dlist-allocation-that-wa.patch new file mode 100644 index 0000000000..aa38b182ba --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1071-drm-vc4-Log-the-size-of-the-dlist-allocation-that-wa.patch @@ -0,0 +1,23 @@ +From 23ea21ef5f6efb3082c184843b35a2f8f2e4374c Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-1072-drm-vc4-crtc-Support-odd-horizontal-timings-on-BCM27.patch b/target/linux/bcm27xx/patches-6.1/950-1072-drm-vc4-crtc-Support-odd-horizontal-timings-on-BCM27.patch new file mode 100644 index 0000000000..a72e3b8e5b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1072-drm-vc4-crtc-Support-odd-horizontal-timings-on-BCM27.patch @@ -0,0 +1,104 @@ +From f9f480b04f1dc280bd4411477f5ee7336361367b Mon Sep 17 00:00:00 2001 +From: Dom Cobley +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1073-spi-dw-dma-Get-the-last-DMA-scoop-out-of-the-FIFO.patch b/target/linux/bcm27xx/patches-6.1/950-1073-spi-dw-dma-Get-the-last-DMA-scoop-out-of-the-FIFO.patch new file mode 100644 index 0000000000..caf8b1a462 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1073-spi-dw-dma-Get-the-last-DMA-scoop-out-of-the-FIFO.patch @@ -0,0 +1,41 @@ +From 686fe776309fba5cad642c40177d39bf1fb320b2 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1075-drivers-mmc-sdhci-add-SPURIOUS_INT_RESP-quirk.patch b/target/linux/bcm27xx/patches-6.1/950-1075-drivers-mmc-sdhci-add-SPURIOUS_INT_RESP-quirk.patch new file mode 100644 index 0000000000..2ab224df05 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1075-drivers-mmc-sdhci-add-SPURIOUS_INT_RESP-quirk.patch @@ -0,0 +1,62 @@ +From 4d2261fe86ce08bbee3c000718000e9f86593d88 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-1076-dt-bindings-mmc-sdhci-of-dwcmhsc-Add-Raspberry-Pi-RP.patch b/target/linux/bcm27xx/patches-6.1/950-1076-dt-bindings-mmc-sdhci-of-dwcmhsc-Add-Raspberry-Pi-RP.patch new file mode 100644 index 0000000000..ffdc1b1c5d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1076-dt-bindings-mmc-sdhci-of-dwcmhsc-Add-Raspberry-Pi-RP.patch @@ -0,0 +1,42 @@ +From ebe13d0d4314255d226ba740e37a14172a8b9091 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + .../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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1077-drivers-mmc-sdhci-of-dwcmshc-add-RP1-dt-ID-and-quirk.patch b/target/linux/bcm27xx/patches-6.1/950-1077-drivers-mmc-sdhci-of-dwcmshc-add-RP1-dt-ID-and-quirk.patch new file mode 100644 index 0000000000..54b719c6ec --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1077-drivers-mmc-sdhci-of-dwcmshc-add-RP1-dt-ID-and-quirk.patch @@ -0,0 +1,41 @@ +From 80dd8795ca631ac692fd3079487aea6d934a829c Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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, + }, diff --git a/target/linux/bcm27xx/patches-6.1/950-1078-arm-dts-change-RP1-SDHCI-controller-compatible-strin.patch b/target/linux/bcm27xx/patches-6.1/950-1078-arm-dts-change-RP1-SDHCI-controller-compatible-strin.patch new file mode 100644 index 0000000000..c708bf1f71 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1078-arm-dts-change-RP1-SDHCI-controller-compatible-strin.patch @@ -0,0 +1,104 @@ +From 51cdff455e3c3df29764f71bc0c9dd0e099945d6 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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: + + ++Name: sdio-pi5 ++Info: Selects the rp1_mmc0 interface and enables it on GPIOs 22-27. ++ Pi 5 only. ++Load: dtoverlay=sdio-pi5 ++Params: ++ ++ + 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 = ; + 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 = ; + clocks = <&rp1_clocks RP1_CLK_SYS &sdhci_core + &rp1_clocks RP1_CLK_SDIO_TIMER diff --git a/target/linux/bcm27xx/patches-6.1/950-1079-ASoC-bcm-audioinjector_octo-Add-soundcard-owner.patch b/target/linux/bcm27xx/patches-6.1/950-1079-ASoC-bcm-audioinjector_octo-Add-soundcard-owner.patch new file mode 100644 index 0000000000..aa025125e7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1079-ASoC-bcm-audioinjector_octo-Add-soundcard-owner.patch @@ -0,0 +1,22 @@ +From 020ee5029ab0b11e47696f538418105ccfdb44de Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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), + diff --git a/target/linux/bcm27xx/patches-6.1/950-1080-drivers-media-imx708-Adjust-broken-line-correction-p.patch b/target/linux/bcm27xx/patches-6.1/950-1080-drivers-media-imx708-Adjust-broken-line-correction-p.patch new file mode 100644 index 0000000000..60f489c6c0 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1080-drivers-media-imx708-Adjust-broken-line-correction-p.patch @@ -0,0 +1,139 @@ +From f364e0eb8f973e1aa24a3c451d18e84247a8efcd Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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 + #include + ++/* ++ * 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1082-drivers-media-cfe-Don-t-confuse-MHz-and-Mbps.patch b/target/linux/bcm27xx/patches-6.1/950-1082-drivers-media-cfe-Don-t-confuse-MHz-and-Mbps.patch new file mode 100644 index 0000000000..ea0ea396ba --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1082-drivers-media-cfe-Don-t-confuse-MHz-and-Mbps.patch @@ -0,0 +1,96 @@ +From 65407c54fb4119e528b70b329448269657e0941e Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1083-overlays-imx296-Fix-cam-port-override-for-regulators.patch b/target/linux/bcm27xx/patches-6.1/950-1083-overlays-imx296-Fix-cam-port-override-for-regulators.patch new file mode 100644 index 0000000000..1b6d2041f1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1083-overlays-imx296-Fix-cam-port-override-for-regulators.patch @@ -0,0 +1,25 @@ +From 6137fb168c08bd8c41c8421bf26f09ed29479f08 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1148-overlays-ov5647-Regularise-vcm-node-label-name.patch b/target/linux/bcm27xx/patches-6.1/950-1148-overlays-ov5647-Regularise-vcm-node-label-name.patch new file mode 100644 index 0000000000..28017989a9 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1148-overlays-ov5647-Regularise-vcm-node-label-name.patch @@ -0,0 +1,32 @@ +From 7443b602cb503b42dd0ae8e957e26decb420d632 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Mon, 20 Nov 2023 10:15:15 +0000 +Subject: [PATCH] overlays: ov5647: Regularise vcm node label name + +Signed-off-by: Phil Elwell +--- + 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>; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1149-overlays-ov5647-cam0-mode-should-use-cam0_reg.patch b/target/linux/bcm27xx/patches-6.1/950-1149-overlays-ov5647-cam0-mode-should-use-cam0_reg.patch new file mode 100644 index 0000000000..f079756133 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1149-overlays-ov5647-cam0-mode-should-use-cam0_reg.patch @@ -0,0 +1,27 @@ +From d484bef133af9c87d64899fc1e1d0be2a7c7785b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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>; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1150-w1-Disable-kernel-log-spam.patch b/target/linux/bcm27xx/patches-6.1/950-1150-w1-Disable-kernel-log-spam.patch new file mode 100644 index 0000000000..457a0e9ec5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1150-w1-Disable-kernel-log-spam.patch @@ -0,0 +1,25 @@ +From 0924b74687bd195b98f223814ff88b4227654e85 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1151-include-uapi-mbus-Add-a-media-bus-format-enum-for-16.patch b/target/linux/bcm27xx/patches-6.1/950-1151-include-uapi-mbus-Add-a-media-bus-format-enum-for-16.patch new file mode 100644 index 0000000000..b8933bcbb6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1151-include-uapi-mbus-Add-a-media-bus-format-enum-for-16.patch @@ -0,0 +1,21 @@ +From be8ca764e0530ec8ac18ca03c49e3cda13562d3a Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1152-include-uapi-v4l2-Add-additional-pixel-formats-for-u.patch b/target/linux/bcm27xx/patches-6.1/950-1152-include-uapi-v4l2-Add-additional-pixel-formats-for-u.patch new file mode 100644 index 0000000000..56855184be --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1152-include-uapi-v4l2-Add-additional-pixel-formats-for-u.patch @@ -0,0 +1,73 @@ +From 9abab2e8e5cfbeae6e1b33cc3a5ed773e4e31774 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-1153-drivers-media-cfe-Add-16-bit-and-compressed-mono-for.patch b/target/linux/bcm27xx/patches-6.1/950-1153-drivers-media-cfe-Add-16-bit-and-compressed-mono-for.patch new file mode 100644 index 0000000000..607f23eeb3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1153-drivers-media-cfe-Add-16-bit-and-compressed-mono-for.patch @@ -0,0 +1,52 @@ +From 88d06a674009ad5b77234537527a800e6e0e88a3 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1154-drivers-media-pisp_be-Add-mono-and-48-bit-RGB-pixel-.patch b/target/linux/bcm27xx/patches-6.1/950-1154-drivers-media-pisp_be-Add-mono-and-48-bit-RGB-pixel-.patch new file mode 100644 index 0000000000..2ea4abd112 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1154-drivers-media-pisp_be-Add-mono-and-48-bit-RGB-pixel-.patch @@ -0,0 +1,72 @@ +From 2affda8d2b172aa0fd22778983d983fc9522e621 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1155-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch b/target/linux/bcm27xx/patches-6.1/950-1155-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch new file mode 100644 index 0000000000..88bb61a4c3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1155-ASoC-dwc-Remove-check-in-set_bclk_ratio-handling.patch @@ -0,0 +1,32 @@ +From 52545628c07be2fd1c9df598a17130d92f12da23 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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: diff --git a/target/linux/bcm27xx/patches-6.1/950-1159-overlays-README-Fix-cut-and-paste-errors.patch b/target/linux/bcm27xx/patches-6.1/950-1159-overlays-README-Fix-cut-and-paste-errors.patch new file mode 100644 index 0000000000..30cc82b0cd --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1159-overlays-README-Fix-cut-and-paste-errors.patch @@ -0,0 +1,39 @@ +From 5a0aa24b8ff58ceaf98c62670156bef7f48ed32b Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 21 Nov 2023 15:08:38 +0000 +Subject: [PATCH] overlays: README: Fix cut-and-paste errors + +Signed-off-by: Phil Elwell +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1160-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch b/target/linux/bcm27xx/patches-6.1/950-1160-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch new file mode 100644 index 0000000000..36565bf2d6 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1160-media-i2c-ov7251-Switch-from-V4L2_CID_GAIN-to-V4L2_C.patch @@ -0,0 +1,38 @@ +From e60fbc34aa98b3ba2c9338ad628fc8d8137e9065 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1161-drm-vc4-Drop-planes-that-are-completely-off-screen.patch b/target/linux/bcm27xx/patches-6.1/950-1161-drm-vc4-Drop-planes-that-are-completely-off-screen.patch new file mode 100644 index 0000000000..8cdbb51d55 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1161-drm-vc4-Drop-planes-that-are-completely-off-screen.patch @@ -0,0 +1,63 @@ +From 444884f7b62bfe5ef313dd1d47f81a40e695ab0b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1162-drm-bridge-display-connector-Select-DRM_KMS_HELPER.patch b/target/linux/bcm27xx/patches-6.1/950-1162-drm-bridge-display-connector-Select-DRM_KMS_HELPER.patch new file mode 100644 index 0000000000..270975e587 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1162-drm-bridge-display-connector-Select-DRM_KMS_HELPER.patch @@ -0,0 +1,29 @@ +From 91ad217f93232fb3a0b52487fec67860fb29e93a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1163-drm-vc4-Free-the-dlist-alloc-immediately-if-it-never.patch b/target/linux/bcm27xx/patches-6.1/950-1163-drm-vc4-Free-the-dlist-alloc-immediately-if-it-never.patch new file mode 100644 index 0000000000..49359362fe --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1163-drm-vc4-Free-the-dlist-alloc-immediately-if-it-never.patch @@ -0,0 +1,56 @@ +From 51712a6493bf8824419f51ca7950e7d88f48b699 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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), diff --git a/target/linux/bcm27xx/patches-6.1/950-1164-input-edt-ft5x06-Include-I2C-details-in-names-for-th.patch b/target/linux/bcm27xx/patches-6.1/950-1164-input-edt-ft5x06-Include-I2C-details-in-names-for-th.patch new file mode 100644 index 0000000000..131e502406 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1164-input-edt-ft5x06-Include-I2C-details-in-names-for-th.patch @@ -0,0 +1,48 @@ +From 2a6c3115f4142e23ca10c984d7f65ac8fb901cc2 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1165-input-goodix-Include-I2C-details-in-names-for-the-de.patch b/target/linux/bcm27xx/patches-6.1/950-1165-input-goodix-Include-I2C-details-in-names-for-the-de.patch new file mode 100644 index 0000000000..4b529e66f5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1165-input-goodix-Include-I2C-details-in-names-for-the-de.patch @@ -0,0 +1,52 @@ +From a420bbde05f8a6691b0c3e0830092e443365aaa7 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1166-drm-vc4-Block-swiotlb-bounce-buffers-being-imported-.patch b/target/linux/bcm27xx/patches-6.1/950-1166-drm-vc4-Block-swiotlb-bounce-buffers-being-imported-.patch new file mode 100644 index 0000000000..c40b77c957 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1166-drm-vc4-Block-swiotlb-bounce-buffers-being-imported-.patch @@ -0,0 +1,84 @@ +From a984fda6b2c24dbf1ca21924f99c8f9418f5765e Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 + #include + #include ++#include + + #include + #include +@@ -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, + diff --git a/target/linux/bcm27xx/patches-6.1/950-1167-overlays-i2c-sensor-Add-adt7410-support.patch b/target/linux/bcm27xx/patches-6.1/950-1167-overlays-i2c-sensor-Add-adt7410-support.patch new file mode 100644 index 0000000000..c7c9daba3f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1167-overlays-i2c-sensor-Add-adt7410-support.patch @@ -0,0 +1,75 @@ +From 77a01f7da77446277139d8e9ce63f078cbe1ecfe Mon Sep 17 00:00:00 2001 +From: Kenny +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 +--- + 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,= +-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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-1168-overlays-hat_map-Add-pisound-mapping.patch b/target/linux/bcm27xx/patches-6.1/950-1168-overlays-hat_map-Add-pisound-mapping.patch new file mode 100644 index 0000000000..2620a598f7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1168-overlays-hat_map-Add-pisound-mapping.patch @@ -0,0 +1,26 @@ +From 7c185b18a1c6a6cd6ab8805fef03a4a8c9931656 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-1169-drm-vc4-Set-TV-margins-on-the-composite-connector-st.patch b/target/linux/bcm27xx/patches-6.1/950-1169-drm-vc4-Set-TV-margins-on-the-composite-connector-st.patch new file mode 100644 index 0000000000..05da5d38c5 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1169-drm-vc4-Set-TV-margins-on-the-composite-connector-st.patch @@ -0,0 +1,47 @@ +From bc76ab2e772242d0d92d38b2a5648485ee1a1b44 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20B=C3=BCchler?= +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 +--- + 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)); diff --git a/target/linux/bcm27xx/patches-6.1/950-1170-drm-panel-jdi-lt070me05000-Add-prepare_upstream_firs.patch b/target/linux/bcm27xx/patches-6.1/950-1170-drm-panel-jdi-lt070me05000-Add-prepare_upstream_firs.patch new file mode 100644 index 0000000000..62e408b514 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1170-drm-panel-jdi-lt070me05000-Add-prepare_upstream_firs.patch @@ -0,0 +1,26 @@ +From 63d8c0f5185169058384142547655fc038aae0bf Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-1172-drivers-media-cfe-Find-the-source-pads-on-the-sensor.patch b/target/linux/bcm27xx/patches-6.1/950-1172-drivers-media-cfe-Find-the-source-pads-on-the-sensor.patch new file mode 100644 index 0000000000..e1f16d0171 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1172-drivers-media-cfe-Find-the-source-pads-on-the-sensor.patch @@ -0,0 +1,55 @@ +From 76b1bbf3ec3be0afdc768863ab7e9bbd2734b97b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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# */ diff --git a/target/linux/bcm27xx/patches-6.1/950-1173-dtoverlays-Add-option-for-cam0-to-camera-mux-Nport-o.patch b/target/linux/bcm27xx/patches-6.1/950-1173-dtoverlays-Add-option-for-cam0-to-camera-mux-Nport-o.patch new file mode 100644 index 0000000000..09cdcc7928 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1173-dtoverlays-Add-option-for-cam0-to-camera-mux-Nport-o.patch @@ -0,0 +1,101 @@ +From 08c5904ad00ffc54d37058ead19814f8a85f6f39 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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>; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1174-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch b/target/linux/bcm27xx/patches-6.1/950-1174-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch new file mode 100644 index 0000000000..258b9de01c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1174-ASoC-dwc-Permit-sample-rates-up-to-384kHz.patch @@ -0,0 +1,26 @@ +From 6fac5d5ed6d023733ef7304afc88c8d89467a204 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1176-ASoC-dwc-Fix-full-duplex-mode.patch b/target/linux/bcm27xx/patches-6.1/950-1176-ASoC-dwc-Fix-full-duplex-mode.patch new file mode 100644 index 0000000000..f4b7098fae --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1176-ASoC-dwc-Fix-full-duplex-mode.patch @@ -0,0 +1,63 @@ +From cbc65d9c95088d1437a5a84c7d6628b8d27a86f1 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1179-ASoC-pcm512x-Adds-bindings-for-TAS575x-devices.patch b/target/linux/bcm27xx/patches-6.1/950-1179-ASoC-pcm512x-Adds-bindings-for-TAS575x-devices.patch new file mode 100644 index 0000000000..8472c3e9e1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1179-ASoC-pcm512x-Adds-bindings-for-TAS575x-devices.patch @@ -0,0 +1,45 @@ +From a7ac27fbac1aa3fb8316cf1ff6dd2f81109d46d2 Mon Sep 17 00:00:00 2001 +From: Joerg Schambacher +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 +Reviewed-by: Rob Herring +Link: https://lore.kernel.org/r/20230929150555.405388-1-joerg.hifiberry@gmail.com +Signed-off-by: Mark Brown +--- + 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: + diff --git a/target/linux/bcm27xx/patches-6.1/950-1180-ASoC-Adds-support-for-TAS575x-to-the-pcm512x-driver.patch b/target/linux/bcm27xx/patches-6.1/950-1180-ASoC-Adds-support-for-TAS575x-to-the-pcm512x-driver.patch new file mode 100644 index 0000000000..5009b225a3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1180-ASoC-Adds-support-for-TAS575x-to-the-pcm512x-driver.patch @@ -0,0 +1,102 @@ +From a535dd07aff73f3a6eb40174ab5dc413d05f36a1 Mon Sep 17 00:00:00 2001 +From: Joerg Schambacher +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 + +Signed-off-by: Joerg Schambacher +Link: https://lore.kernel.org/r/20230929150722.405415-1-joerg.hifiberry@gmail.com +Signed-off-by: Mark Brown +--- + 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 + diff --git a/target/linux/bcm27xx/patches-6.1/950-1182-drm-panel-add-panel-dsi.patch b/target/linux/bcm27xx/patches-6.1/950-1182-drm-panel-add-panel-dsi.patch new file mode 100644 index 0000000000..d914ab6d04 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1182-drm-panel-add-panel-dsi.patch @@ -0,0 +1,176 @@ +From 0a0084b8621682ad96d001e798aa239b11a3cf00 Mon Sep 17 00:00:00 2001 +From: Timon Skerutsch +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 +--- + 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 + #include + #include ++#include + + /** + * 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1183-dt-bindings-display-panel-dsi-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-1183-dt-bindings-display-panel-dsi-bindings.patch new file mode 100644 index 0000000000..c75ca8e8ac --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1183-dt-bindings-display-panel-dsi-bindings.patch @@ -0,0 +1,136 @@ +From e14ceeadfa5b9d8a41e73edfa416bbe92dd5b20d Mon Sep 17 00:00:00 2001 +From: Timon Skerutsch +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 +--- + .../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 ++ ++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>; ++ }; ++ }; ++ ++... diff --git a/target/linux/bcm27xx/patches-6.1/950-1184-overlays-example-overlay-for-using-panel-dsi-on-RPi.patch b/target/linux/bcm27xx/patches-6.1/950-1184-overlays-example-overlay-for-using-panel-dsi-on-RPi.patch new file mode 100644 index 0000000000..28a82d596d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1184-overlays-example-overlay-for-using-panel-dsi-on-RPi.patch @@ -0,0 +1,176 @@ +From ccf75f2a6f4045484c4539f7d47264f8f6b8453c Mon Sep 17 00:00:00 2001 +From: Timon Skerutsch +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 +--- + 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,= ++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"; ++ }; ++ ++}; diff --git a/target/linux/bcm27xx/patches-6.1/950-1185-overlays-ADS1115-allow-specification-of-the-i2c-bus.patch b/target/linux/bcm27xx/patches-6.1/950-1185-overlays-ADS1115-allow-specification-of-the-i2c-bus.patch new file mode 100644 index 0000000000..ef9328bd6f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1185-overlays-ADS1115-allow-specification-of-the-i2c-bus.patch @@ -0,0 +1,159 @@ +From a477a6351575aa173f9f82857f5797e384fbc704 Mon Sep 17 00:00:00 2001 +From: JinShil +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"; + }; + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1186-dts-bcm2712-put-usb-under-axi-not-soc.patch b/target/linux/bcm27xx/patches-6.1/950-1186-dts-bcm2712-put-usb-under-axi-not-soc.patch new file mode 100644 index 0000000000..b66265f5a1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1186-dts-bcm2712-put-usb-under-axi-not-soc.patch @@ -0,0 +1,59 @@ +From 82069a7a02632aa60fa5c69415bf891ede7d6fd4 Mon Sep 17 00:00:00 2001 +From: Jonathan Bell +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 +--- + 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 = ; +- #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 = ; ++ #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 */ diff --git a/target/linux/bcm27xx/patches-6.1/950-1187-drm-vc4-Correct-HVS-muxing-setup-for-the-moplet.patch b/target/linux/bcm27xx/patches-6.1/950-1187-drm-vc4-Correct-HVS-muxing-setup-for-the-moplet.patch new file mode 100644 index 0000000000..be02fd27fa --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1187-drm-vc4-Correct-HVS-muxing-setup-for-the-moplet.patch @@ -0,0 +1,26 @@ +From 01139e4e9141d031c6f4f00371e5eb52fa78839e Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1188-drm-vc4-Mop-and-moplet-have-different-register-offse.patch b/target/linux/bcm27xx/patches-6.1/950-1188-drm-vc4-Mop-and-moplet-have-different-register-offse.patch new file mode 100644 index 0000000000..f1bb26a26b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1188-drm-vc4-Mop-and-moplet-have-different-register-offse.patch @@ -0,0 +1,68 @@ +From cc948130d3e1c70ef21ae9963b56e0d500cef70b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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, + }; diff --git a/target/linux/bcm27xx/patches-6.1/950-1189-arm-dt-bcm2712-Correct-the-size-of-the-register-rang.patch b/target/linux/bcm27xx/patches-6.1/950-1189-arm-dt-bcm2712-Correct-the-size-of-the-register-rang.patch new file mode 100644 index 0000000000..ece5c716e7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1189-arm-dt-bcm2712-Correct-the-size-of-the-register-rang.patch @@ -0,0 +1,25 @@ +From 36593e2e27769d635ef18301f25b5e219a23949a Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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"; diff --git a/target/linux/bcm27xx/patches-6.1/950-1192-media-Add-MIPI-CCI-register-access-helper-functions.patch b/target/linux/bcm27xx/patches-6.1/950-1192-media-Add-MIPI-CCI-register-access-helper-functions.patch new file mode 100644 index 0000000000..3305e66986 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1192-media-Add-MIPI-CCI-register-access-helper-functions.patch @@ -0,0 +1,388 @@ +From ae0e1b70f675f6ac7966e427f0d8f57812dbc312 Mon Sep 17 00:00:00 2001 +From: Hans de Goede +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 +Tested-by: Tommaso Merciai +Reviewed-by: Tommaso Merciai +Signed-off-by: Hans de Goede +Signed-off-by: Sakari Ailus +Signed-off-by: Mauro Carvalho Chehab +(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 ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++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 "); ++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 ++ */ ++#ifndef _V4L2_CCI_H ++#define _V4L2_CCI_H ++ ++#include ++ ++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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1193-media-dt-bindings-Add-OmniVision-OV64A40.patch b/target/linux/bcm27xx/patches-6.1/950-1193-media-dt-bindings-Add-OmniVision-OV64A40.patch new file mode 100644 index 0000000000..649e8bc393 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1193-media-dt-bindings-Add-OmniVision-OV64A40.patch @@ -0,0 +1,114 @@ +From 3d108604ca669b83bb4918c4f5f0a02ddef84972 Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi +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 +--- + .../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 ++ ++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 ++ ++ 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>; ++ }; ++ }; ++ }; ++ }; ++ ++... diff --git a/target/linux/bcm27xx/patches-6.1/950-1194-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch b/target/linux/bcm27xx/patches-6.1/950-1194-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch new file mode 100644 index 0000000000..82eda14173 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1194-media-dt-bindings-i2c-Add-Rohm-BU64754-bindings.patch @@ -0,0 +1,83 @@ +From 31c2999e543c245f7b96af3e73cd18e1036bfe7b Mon Sep 17 00:00:00 2001 +From: Kieran Bingham +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 +Signed-off-by: Jacopo Mondi +--- + .../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 ++ ++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 ++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 + L: linux-kernel@vger.kernel.org diff --git a/target/linux/bcm27xx/patches-6.1/950-1195-media-i2c-Add-driver-for-OmniVision-OV64A40.patch b/target/linux/bcm27xx/patches-6.1/950-1195-media-i2c-Add-driver-for-OmniVision-OV64A40.patch new file mode 100644 index 0000000000..2f3ea4b072 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1195-media-i2c-Add-driver-for-OmniVision-OV64A40.patch @@ -0,0 +1,3763 @@ +From 87e3fcaad3017bdc91a7a79d2d1c874422ef87b0 Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi +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 +--- + 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 ++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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "); ++MODULE_DESCRIPTION("OmniVision OV64A40 sensor driver"); ++MODULE_LICENSE("GPL"); diff --git a/target/linux/bcm27xx/patches-6.1/950-1196-media-i2c-Add-ROHM-BU64754-Camera-Autofocus-Actuator.patch b/target/linux/bcm27xx/patches-6.1/950-1196-media-i2c-Add-ROHM-BU64754-Camera-Autofocus-Actuator.patch new file mode 100644 index 0000000000..a50617ee56 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1196-media-i2c-Add-ROHM-BU64754-Camera-Autofocus-Actuator.patch @@ -0,0 +1,368 @@ +From 97ec6aeb265df0bfe7193f00c249b38873fb0fb7 Mon Sep 17 00:00:00 2001 +From: Kieran Bingham +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 +Signed-off-by: Jacopo Mondi +--- + 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 ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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"); ++ diff --git a/target/linux/bcm27xx/patches-6.1/950-1197-overlays-Add-overlay-for-the-OV64A40-Arducam-Camera-.patch b/target/linux/bcm27xx/patches-6.1/950-1197-overlays-Add-overlay-for-the-OV64A40-Arducam-Camera-.patch new file mode 100644 index 0000000000..d3ee0dc68e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1197-overlays-Add-overlay-for-the-OV64A40-Arducam-Camera-.patch @@ -0,0 +1,426 @@ +From 7f67a45ee7c008c3d8e45fde6fa9c4287fb3bc9e Mon Sep 17 00:00:00 2001 +From: Jacopo Mondi +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 +Signed-off-by: Kieran Bingham +--- + 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,= ++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>; ++}; diff --git a/target/linux/bcm27xx/patches-6.1/950-1199-media-rp1-cfe-Fix-verbose-debug-print.patch b/target/linux/bcm27xx/patches-6.1/950-1199-media-rp1-cfe-Fix-verbose-debug-print.patch new file mode 100644 index 0000000000..8ff96cc568 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1199-media-rp1-cfe-Fix-verbose-debug-print.patch @@ -0,0 +1,26 @@ +From 8ef68aadaa3aa29bc2661ab44db4ddc50e77cef5 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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); + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-1200-media-rp1-cfe-Expose-find_format_by_pix.patch b/target/linux/bcm27xx/patches-6.1/950-1200-media-rp1-cfe-Expose-find_format_by_pix.patch new file mode 100644 index 0000000000..f263648af2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1200-media-rp1-cfe-Expose-find_format_by_pix.patch @@ -0,0 +1,33 @@ +From d978e784f433346d3676b5de805b3cea36b835c4 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1201-media-rp1-cfe-Add-missing-remaps.patch b/target/linux/bcm27xx/patches-6.1/950-1201-media-rp1-cfe-Add-missing-remaps.patch new file mode 100644 index 0000000000..d6dadb6f27 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1201-media-rp1-cfe-Add-missing-remaps.patch @@ -0,0 +1,43 @@ +From cbed711f05a228d0f8f54b1b01f43d4d6489eccc Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1202-media-rp1-cfe-Add-missing-compressed-remaps.patch b/target/linux/bcm27xx/patches-6.1/950-1202-media-rp1-cfe-Add-missing-compressed-remaps.patch new file mode 100644 index 0000000000..92d7db369c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1202-media-rp1-cfe-Add-missing-compressed-remaps.patch @@ -0,0 +1,43 @@ +From 93c40564b94367c6ce072d66479af58afa3f08e0 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + 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 */ + { diff --git a/target/linux/bcm27xx/patches-6.1/950-1203-media-rp1-cfe-Add-cfe_find_16bit_code-and-cfe_find_c.patch b/target/linux/bcm27xx/patches-6.1/950-1203-media-rp1-cfe-Add-cfe_find_16bit_code-and-cfe_find_c.patch new file mode 100644 index 0000000000..42c3b6f42b --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1203-media-rp1-cfe-Add-cfe_find_16bit_code-and-cfe_find_c.patch @@ -0,0 +1,74 @@ +From 2e0e1d7b493dffe7baa763d499e51ba42f0bad19 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1204-media-rp1-csi2-Fix-csi2_pad_set_fmt.patch b/target/linux/bcm27xx/patches-6.1/950-1204-media-rp1-csi2-Fix-csi2_pad_set_fmt.patch new file mode 100644 index 0000000000..d9b8d264d3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1204-media-rp1-csi2-Fix-csi2_pad_set_fmt.patch @@ -0,0 +1,95 @@ +From eaa8a0ae14a1ca797c1896e9dafbefa1fa51a617 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1205-media-rp1-fe-Fix-pisp_fe_pad_set_fmt.patch b/target/linux/bcm27xx/patches-6.1/950-1205-media-rp1-fe-Fix-pisp_fe_pad_set_fmt.patch new file mode 100644 index 0000000000..202fe2946d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1205-media-rp1-fe-Fix-pisp_fe_pad_set_fmt.patch @@ -0,0 +1,104 @@ +From 214e8134842a338215831f2efa6d730f413c5ec4 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1206-media-rp1-csi2-Use-get_frame_desc-to-get-CSI-2-VC-an.patch b/target/linux/bcm27xx/patches-6.1/950-1206-media-rp1-csi2-Use-get_frame_desc-to-get-CSI-2-VC-an.patch new file mode 100644 index 0000000000..bd5789a1b3 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1206-media-rp1-csi2-Use-get_frame_desc-to-get-CSI-2-VC-an.patch @@ -0,0 +1,146 @@ +From 425a6b752c38b50c97220db37a67b18b281f56e5 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1207-media-rp1-cfe-Add-is_image_node.patch b/target/linux/bcm27xx/patches-6.1/950-1207-media-rp1-cfe-Add-is_image_node.patch new file mode 100644 index 0000000000..ad589d79cf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1207-media-rp1-cfe-Add-is_image_node.patch @@ -0,0 +1,91 @@ +From 2b6570e66f2769110311593f52f88dba3271a278 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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"); diff --git a/target/linux/bcm27xx/patches-6.1/950-1208-media-rp1-cfe-Dual-purpose-video-nodes.patch b/target/linux/bcm27xx/patches-6.1/950-1208-media-rp1-cfe-Dual-purpose-video-nodes.patch new file mode 100644 index 0000000000..f68aa3975d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1208-media-rp1-cfe-Dual-purpose-video-nodes.patch @@ -0,0 +1,621 @@ +From c54b8d2fc79c684deacc81a94f6baa1cb56c62be Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1209-media-rp1-Drop-LE-handling.patch b/target/linux/bcm27xx/patches-6.1/950-1209-media-rp1-Drop-LE-handling.patch new file mode 100644 index 0000000000..1d6e396f1c --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1209-media-rp1-Drop-LE-handling.patch @@ -0,0 +1,127 @@ +From dad296088dffbaf55c1e61cbdc3f7cb1eb504ca6 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1210-media-rp1-csi2-Use-standard-link_validate.patch b/target/linux/bcm27xx/patches-6.1/950-1210-media-rp1-csi2-Use-standard-link_validate.patch new file mode 100644 index 0000000000..a3836150dc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1210-media-rp1-csi2-Use-standard-link_validate.patch @@ -0,0 +1,75 @@ +From b6316a8450d3cb99b7599175d59b1b7f710770f5 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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 = { diff --git a/target/linux/bcm27xx/patches-6.1/950-1211-media-rp1-fe-Use-standard-link_validate.patch b/target/linux/bcm27xx/patches-6.1/950-1211-media-rp1-fe-Use-standard-link_validate.patch new file mode 100644 index 0000000000..d36ea5bd52 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1211-media-rp1-fe-Use-standard-link_validate.patch @@ -0,0 +1,70 @@ +From 0eeb351222adbc5b534c86f7815ee787babc3485 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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 = { diff --git a/target/linux/bcm27xx/patches-6.1/950-1212-media-rp1-cfe-Improve-link-validation-for-metadata.patch b/target/linux/bcm27xx/patches-6.1/950-1212-media-rp1-cfe-Improve-link-validation-for-metadata.patch new file mode 100644 index 0000000000..d85a90aac1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1212-media-rp1-cfe-Improve-link-validation-for-metadata.patch @@ -0,0 +1,61 @@ +From e0f52ccfe1e383622fb30708acd38921e84fbff4 Mon Sep 17 00:00:00 2001 +From: Tomi Valkeinen +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 +--- + .../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; + } + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-1214-drivers-pinctrl-bcm-Kconfig-Fix-BCM2712-help.patch b/target/linux/bcm27xx/patches-6.1/950-1214-drivers-pinctrl-bcm-Kconfig-Fix-BCM2712-help.patch new file mode 100644 index 0000000000..c3e4140fa2 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1214-drivers-pinctrl-bcm-Kconfig-Fix-BCM2712-help.patch @@ -0,0 +1,24 @@ +From 046d03c87ecce5db074eae6ffa2d5298c5f7d5a7 Mon Sep 17 00:00:00 2001 +From: Leon Anavi +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 +--- + 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" diff --git a/target/linux/bcm27xx/patches-6.1/950-1216-drivers-gpu-drm-panel-fix-waveshare-panel-software-r.patch b/target/linux/bcm27xx/patches-6.1/950-1216-drivers-gpu-drm-panel-fix-waveshare-panel-software-r.patch new file mode 100644 index 0000000000..aeb63589fc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1216-drivers-gpu-drm-panel-fix-waveshare-panel-software-r.patch @@ -0,0 +1,41 @@ +From a6872b25f18fe46ef9979f9d4a3635a9c3966afd Mon Sep 17 00:00:00 2001 +From: eng33 +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 +--- + 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); + diff --git a/target/linux/bcm27xx/patches-6.1/950-1217-firmware-psci-Pass-given-partition-number-through.patch b/target/linux/bcm27xx/patches-6.1/950-1217-firmware-psci-Pass-given-partition-number-through.patch new file mode 100644 index 0000000000..7feeb224d1 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1217-firmware-psci-Pass-given-partition-number-through.patch @@ -0,0 +1,33 @@ +From 6988ae7c909dc342322a82daaa3a95b78d038305 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-1218-dts-bcm2712-rpi-5-b-Enable-warm-reboot-mode.patch b/target/linux/bcm27xx/patches-6.1/950-1218-dts-bcm2712-rpi-5-b-Enable-warm-reboot-mode.patch new file mode 100644 index 0000000000..8bcf587e6d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1218-dts-bcm2712-rpi-5-b-Enable-warm-reboot-mode.patch @@ -0,0 +1,23 @@ +From 5d87d7f91cb4f1d0f391f6fe9dd0524b363b78e3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1222-drivers-media-i2c-imx296-imx477-Configure-tigger_mod.patch b/target/linux/bcm27xx/patches-6.1/950-1222-drivers-media-i2c-imx296-imx477-Configure-tigger_mod.patch new file mode 100644 index 0000000000..be1eb5611e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1222-drivers-media-i2c-imx296-imx477-Configure-tigger_mod.patch @@ -0,0 +1,85 @@ +From 083f39e40d980b47ab12b451d40b9f935bb22a5b Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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) diff --git a/target/linux/bcm27xx/patches-6.1/950-1223-overlays-Add-always-on-parameter-to-imx477-and-imx29.patch b/target/linux/bcm27xx/patches-6.1/950-1223-overlays-Add-always-on-parameter-to-imx477-and-imx29.patch new file mode 100644 index 0000000000..d705df6f40 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1223-overlays-Add-always-on-parameter-to-imx477-and-imx29.patch @@ -0,0 +1,99 @@ +From 3ed57f25f3074f6abfe570fc11aa7d370fdc499a Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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"; + }; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1224-input-edt-ft5x06-Correct-prefix-length-in-snprintf.patch b/target/linux/bcm27xx/patches-6.1/950-1224-input-edt-ft5x06-Correct-prefix-length-in-snprintf.patch new file mode 100644 index 0000000000..6fb8fc0b95 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1224-input-edt-ft5x06-Correct-prefix-length-in-snprintf.patch @@ -0,0 +1,29 @@ +From e0ecfaf1abfd5ef63ceec7606352020a80a2742b Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 * diff --git a/target/linux/bcm27xx/patches-6.1/950-1225-ARM-dts-bcm2712-rpi-5-b-Allow-RTC-to-be-disabled.patch b/target/linux/bcm27xx/patches-6.1/950-1225-ARM-dts-bcm2712-rpi-5-b-Allow-RTC-to-be-disabled.patch new file mode 100644 index 0000000000..5a3e6df555 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1225-ARM-dts-bcm2712-rpi-5-b-Allow-RTC-to-be-disabled.patch @@ -0,0 +1,38 @@ +From da5fd98469edd797ed77d9a8690a608c6b54f9e6 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1226-i2c-designware-Look-for-CNT-values-in-DT.patch b/target/linux/bcm27xx/patches-6.1/950-1226-i2c-designware-Look-for-CNT-values-in-DT.patch new file mode 100644 index 0000000000..ccba16ad8e --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1226-i2c-designware-Look-for-CNT-values-in-DT.patch @@ -0,0 +1,57 @@ +From 0a09088e24c013ef608b1bb79501ef890cefc767 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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; + } + diff --git a/target/linux/bcm27xx/patches-6.1/950-1227-dts-rp1-Add-I2C-timings.patch b/target/linux/bcm27xx/patches-6.1/950-1227-dts-rp1-Add-I2C-timings.patch new file mode 100644 index 0000000000..892f31ec9f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1227-dts-rp1-Add-I2C-timings.patch @@ -0,0 +1,103 @@ +From 660d569b1a623e4b64350e608bbf8bc2cc6332e9 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +Date: Tue, 19 Dec 2023 11:27:20 +0000 +Subject: [PATCH] dts: rp1: Add I2C timings + +Signed-off-by: Phil Elwell +--- + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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 = ; + 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"; + }; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1228-drivers-media-pisp_be-pisp_fe-Update-UAPI-header-lic.patch b/target/linux/bcm27xx/patches-6.1/950-1228-drivers-media-pisp_be-pisp_fe-Update-UAPI-header-lic.patch new file mode 100644 index 0000000000..fc77e4d1cf --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1228-drivers-media-pisp_be-pisp_fe-Update-UAPI-header-lic.patch @@ -0,0 +1,58 @@ +From 74c6c159c2b499bdf3c39961bd40eb9243624226 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + 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. + * diff --git a/target/linux/bcm27xx/patches-6.1/950-1229-drivers-media-cfe-Add-more-robust-ISR-handlers.patch b/target/linux/bcm27xx/patches-6.1/950-1229-drivers-media-cfe-Add-more-robust-ISR-handlers.patch new file mode 100644 index 0000000000..b79cad417f --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1229-drivers-media-cfe-Add-more-robust-ISR-handlers.patch @@ -0,0 +1,207 @@ +From 6fb7a0b4c1dd6cf5b12ec2b2c197dcf8e58cd2b9 Mon Sep 17 00:00:00 2001 +From: Naushir Patuck +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 +--- + .../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"); diff --git a/target/linux/bcm27xx/patches-6.1/950-1231-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch b/target/linux/bcm27xx/patches-6.1/950-1231-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch new file mode 100644 index 0000000000..c187507545 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1231-ASoC-dwc-Defer-bclk_ratio-handling-to-hw_params.patch @@ -0,0 +1,89 @@ +From dfc04900c40eb14f9364d56e96db2cc3340a1f21 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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; diff --git a/target/linux/bcm27xx/patches-6.1/950-1232-drm-vc4-Fix-reading-of-frame-count-on-GEN5-Pi4.patch b/target/linux/bcm27xx/patches-6.1/950-1232-drm-vc4-Fix-reading-of-frame-count-on-GEN5-Pi4.patch new file mode 100644 index 0000000000..4374e24244 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1232-drm-vc4-Fix-reading-of-frame-count-on-GEN5-Pi4.patch @@ -0,0 +1,79 @@ +From d5066442e39dd9bf4ba6431ffb3f99e3d5085d3f Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1233-ARM-dts-bcm2712-rpi-5-b-Add-eth_ledx-parameters.patch b/target/linux/bcm27xx/patches-6.1/950-1233-ARM-dts-bcm2712-rpi-5-b-Add-eth_ledx-parameters.patch new file mode 100644 index 0000000000..f0c6efa362 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1233-ARM-dts-bcm2712-rpi-5-b-Add-eth_ledx-parameters.patch @@ -0,0 +1,56 @@ +From eeb5969ae34df16024f39a77496a44ae94bfab13 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 diff --git a/target/linux/bcm27xx/patches-6.1/950-1234-ARM-dts-bcm2712-rpi-5-b-Add-fan-speed-dtparams.patch b/target/linux/bcm27xx/patches-6.1/950-1234-ARM-dts-bcm2712-rpi-5-b-Add-fan-speed-dtparams.patch new file mode 100644 index 0000000000..da2345fdf7 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1234-ARM-dts-bcm2712-rpi-5-b-Add-fan-speed-dtparams.patch @@ -0,0 +1,71 @@ +From 2c085a1ff40521ab89d8f1894757aa83b59b4607 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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") + diff --git a/target/linux/bcm27xx/patches-6.1/950-1235-drm-vc4-don-t-check-if-plane-state-fb-state-fb.patch b/target/linux/bcm27xx/patches-6.1/950-1235-drm-vc4-don-t-check-if-plane-state-fb-state-fb.patch new file mode 100644 index 0000000000..7c7c05c5ef --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1235-drm-vc4-don-t-check-if-plane-state-fb-state-fb.patch @@ -0,0 +1,93 @@ +From 146bbf9627f6c37816939de29538ec8ee9a7be1a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ma=C3=ADra=20Canal?= +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/950-1236-spi-bcm2835-Support-spi0-0cs-and-SPI_NO_CS-mode.patch b/target/linux/bcm27xx/patches-6.1/950-1236-spi-bcm2835-Support-spi0-0cs-and-SPI_NO_CS-mode.patch new file mode 100644 index 0000000000..6039363a40 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1236-spi-bcm2835-Support-spi0-0cs-and-SPI_NO_CS-mode.patch @@ -0,0 +1,42 @@ +From 5d9075ed7e73dc6ccebf78710c78f39ddc2dd78e Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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 + * diff --git a/target/linux/bcm27xx/patches-6.1/950-1237-drivers-media-imx519-Add-V4L2_CID_LINK_FREQ-control.patch b/target/linux/bcm27xx/patches-6.1/950-1237-drivers-media-imx519-Add-V4L2_CID_LINK_FREQ-control.patch new file mode 100644 index 0000000000..889ddd77dc --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1237-drivers-media-imx519-Add-V4L2_CID_LINK_FREQ-control.patch @@ -0,0 +1,51 @@ +From 55ed8cded4af6530276b26f567601bed868ae8f5 Mon Sep 17 00:00:00 2001 +From: Lee Jackson +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 +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1238-drivers-media-arducam_64mp-Add-V4L2_CID_LINK_FREQ-co.patch b/target/linux/bcm27xx/patches-6.1/950-1238-drivers-media-arducam_64mp-Add-V4L2_CID_LINK_FREQ-co.patch new file mode 100644 index 0000000000..df353ffc31 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1238-drivers-media-arducam_64mp-Add-V4L2_CID_LINK_FREQ-co.patch @@ -0,0 +1,56 @@ +From 5e339e1502c9be0f624398cf774e5880a6d1a677 Mon Sep 17 00:00:00 2001 +From: Lee Jackson +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 +--- + 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. diff --git a/target/linux/bcm27xx/patches-6.1/950-1239-drivers-gpu-drm-panel-Modify-the-DSI-mode-to-fix-the.patch b/target/linux/bcm27xx/patches-6.1/950-1239-drivers-gpu-drm-panel-Modify-the-DSI-mode-to-fix-the.patch new file mode 100644 index 0000000000..e106b0609a --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1239-drivers-gpu-drm-panel-Modify-the-DSI-mode-to-fix-the.patch @@ -0,0 +1,25 @@ +From 7af1e18d2e90ec64f7cb78cdb5163b63b9fbb056 Mon Sep 17 00:00:00 2001 +From: eng33 +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1240-drivers-gpu-drm-panel-Modified-the-timing-of-11.9inc.patch b/target/linux/bcm27xx/patches-6.1/950-1240-drivers-gpu-drm-panel-Modified-the-timing-of-11.9inc.patch new file mode 100644 index 0000000000..0f04b21137 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1240-drivers-gpu-drm-panel-Modified-the-timing-of-11.9inc.patch @@ -0,0 +1,22 @@ +From aec3f9b6058448c54b74349f16b21185148a8c6e Mon Sep 17 00:00:00 2001 +From: eng33 +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 +--- + 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, diff --git a/target/linux/bcm27xx/patches-6.1/950-1241-drm-vc4-Add-2712-support-to-vc4_plane_async_set_fb.patch b/target/linux/bcm27xx/patches-6.1/950-1241-drm-vc4-Add-2712-support-to-vc4_plane_async_set_fb.patch new file mode 100644 index 0000000000..c13eb354fb --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1241-drm-vc4-Add-2712-support-to-vc4_plane_async_set_fb.patch @@ -0,0 +1,88 @@ +From a14da0fdb0e052a672c269df7f18c9de698bd827 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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); + } diff --git a/target/linux/bcm27xx/patches-6.1/950-1242-drm-vc4-Fix-atomic_async_check-to-call-the-right-mod.patch b/target/linux/bcm27xx/patches-6.1/950-1242-drm-vc4-Fix-atomic_async_check-to-call-the-right-mod.patch new file mode 100644 index 0000000000..683c4a46ff --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1242-drm-vc4-Fix-atomic_async_check-to-call-the-right-mod.patch @@ -0,0 +1,38 @@ +From bfe927647253ab3a86be16320baa1579518c6786 Mon Sep 17 00:00:00 2001 +From: Dave Stevenson +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 +--- + 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; + diff --git a/target/linux/bcm27xx/patches-6.1/950-1244-drm-rp1-rp1-vec-Allow-non-standard-modes-with-variou.patch b/target/linux/bcm27xx/patches-6.1/950-1244-drm-rp1-rp1-vec-Allow-non-standard-modes-with-variou.patch new file mode 100644 index 0000000000..fb20dfe94d --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1244-drm-rp1-rp1-vec-Allow-non-standard-modes-with-variou.patch @@ -0,0 +1,343 @@ +From 04b88e1a8b867b045bc90d997e8e0260b00dbb15 Mon Sep 17 00:00:00 2001 +From: Nick Hollinghurst +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 +--- + 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 + #include + #include +-#include + #include + #include + #include +@@ -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 + #include + #include +-#include + #include + #include + #include +@@ -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) { diff --git a/target/linux/bcm27xx/patches-6.1/950-1245-ARM-pl011-Add-rs485-to-the-RP1-support.patch b/target/linux/bcm27xx/patches-6.1/950-1245-ARM-pl011-Add-rs485-to-the-RP1-support.patch new file mode 100644 index 0000000000..c94619c914 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1245-ARM-pl011-Add-rs485-to-the-RP1-support.patch @@ -0,0 +1,24 @@ +From ab8ba584f91576c41d921076221ceb48e2930ae0 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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"); + diff --git a/target/linux/bcm27xx/patches-6.1/950-1246-fixup-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch b/target/linux/bcm27xx/patches-6.1/950-1246-fixup-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch new file mode 100644 index 0000000000..8fc8d0c768 --- /dev/null +++ b/target/linux/bcm27xx/patches-6.1/950-1246-fixup-irqchip-irq-bcm2712-mip-Support-for-2712-s-MIP.patch @@ -0,0 +1,38 @@ +From 3bb5880ab3dd31f75c07c3c33bf29c5d469b28f3 Mon Sep 17 00:00:00 2001 +From: Phil Elwell +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 +--- + 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); diff --git a/target/linux/bcm27xx/patches-6.1/960-hwrng-iproc-set-quality-to-1000.patch b/target/linux/bcm27xx/patches-6.1/960-hwrng-iproc-set-quality-to-1000.patch index c1b4879a7c..cabe36ba71 100644 --- a/target/linux/bcm27xx/patches-6.1/960-hwrng-iproc-set-quality-to-1000.patch +++ b/target/linux/bcm27xx/patches-6.1/960-hwrng-iproc-set-quality-to-1000.patch @@ -15,7 +15,7 @@ Signed-off-by: Álvaro Fernández Rojas --- 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; diff --git a/target/linux/generic/config-6.1 b/target/linux/generic/config-6.1 index d11c946dbc..c6301662f7 100644 --- a/target/linux/generic/config-6.1 +++ b/target/linux/generic/config-6.1 @@ -605,6 +605,7 @@ CONFIG_BASE_SMALL=0 # 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 @@ -1036,6 +1037,8 @@ CONFIG_CMDLINE="" # 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 @@ -1725,6 +1728,9 @@ CONFIG_DQL=y # 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 @@ -2083,6 +2089,7 @@ CONFIG_FB_NOTIFY=y # 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 @@ -2283,6 +2290,7 @@ CONFIG_GPIOLIB_FASTPATH_LIMIT=512 # 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 @@ -2853,6 +2861,7 @@ CONFIG_INPUT_MISC=y # 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 @@ -3609,6 +3618,7 @@ CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 # 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 @@ -3618,6 +3628,8 @@ CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4 # 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 @@ -4839,6 +4851,7 @@ CONFIG_PCI_SYSCALL=y # 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 @@ -4875,6 +4888,7 @@ CONFIG_PINCONF=y # 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 @@ -4895,6 +4909,7 @@ CONFIG_PINCONF=y # 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 @@ -5053,6 +5068,7 @@ CONFIG_PSTORE_DEFAULT_KMSG_BYTES=10240 # 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 @@ -5127,6 +5143,7 @@ CONFIG_RANDOM_TRUST_CPU=y # 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 @@ -5251,6 +5268,7 @@ CONFIG_REISERFS_FS_XATTR=y # 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 @@ -5779,6 +5797,7 @@ CONFIG_SELECT_MEMORY_MODEL=y # 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 @@ -6593,6 +6612,7 @@ CONFIG_STDBINUTILS=y # 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 @@ -7510,6 +7530,7 @@ CONFIG_VHOST_MENU=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 @@ -7603,6 +7624,7 @@ CONFIG_VHOST_MENU=y # 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