* Re: [PATCH v3 2/8] dt-bindings: gpio: cdns: add Axiado AX3005 GPIO variant
From: Swark Yang @ 2026-07-21 3:35 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
Linus Walleij, Bartosz Golaszewski, Jan Kotas, Michal Simek,
Andi Shyti, Przemysław Gaj, Alexandre Belloni, Frank Li,
Boris Brezillon, Greg Kroah-Hartman, Jiri Slaby, Mark Brown,
Mathias Nyman, devicetree, linux-arm-kernel, linux-kernel,
linux-gpio, linux-i2c, linux-i3c, linux-serial, linux-spi,
linux-usb
In-Reply-To: <20260717-prudent-indefinable-labradoodle-fcaa9e@quoll>
On 7/17/2026 4:33 PM, Krzysztof Kozlowski wrote:
>
> This should stay enum which would also make your diff easier to read.
>
> Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
>
> Best regards,
> Krzysztof
>
Hi Krzysztof,
Agreed on the enum, and thanks for the tags.
Since Bartosz already applied this to the GPIO tree, should I send an
incremental patch to fix the formatting, or is it fine as-is?
Thanks,
Swark
^ permalink raw reply
* [PATCH v2] iommu/io-pgtable-arm: Add support for contiguous hint bit
From: Vijayanand Jitta @ 2026-07-21 3:28 UTC (permalink / raw)
To: Joerg Roedel (AMD), Will Deacon, Robin Murphy
Cc: linux-arm-msm, iommu, linux-kernel, linux-arm-kernel,
Prakash Gupta, Vijayanand Jitta
From: Prakash Gupta <prakash.gupta@oss.qualcomm.com>
Add support for the contiguous hint (CONT) bit in ARM LPAE page tables.
When a set of consecutive PTEs map a naturally-aligned contiguous block
of memory, the CONT bit can be set on all entries in the group to allow
the hardware to combine them into a single TLB entry, improving TLB
utilization.
The contiguous hint sizes per granule are:
Page Size | CONT PTE | Block | CONT Block | L1 Block | CONT L1
----------+----------+---------+------------+----------+---------
4K | 64K | 2M | 32M | 1G | 16G
16K | 2M | 32M | 1G | |
64K | 2M | 512M | 16G | |
Contiguous hint sizes are advertised in pgsize_bitmap so that IOMMU API
users can align allocations to these sizes and benefit from the TLB
optimization automatically.
Partial unmaps of a contiguous group are rejected, ensuring the full
group is always invalidated as a unit. The
IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT quirk allows SMMU drivers to disable
contiguous hint support at runtime for hardware with
implementation-specific errata.
Co-developed-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
Signed-off-by: Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
Signed-off-by: Prakash Gupta <prakash.gupta@oss.qualcomm.com>
---
Changes in v2:
- Extend contiguous hint support to level-1 (1G) blocks for the 4K granule,
adding a CONT L1 (16G) grouping alongside the existing CONT PTE/CONT Block
sizes.
- Replace the compile-time CONFIG_IOMMU_IO_PGTABLE_CONTIG_HINT Kconfig option
with a runtime quirk, IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT, so SMMU drivers
can opt out per page table instance instead of at build time.
- Simplify __arm_lpae_map() to program the CONT-sized block directly via
arm_lpae_init_pte() instead of recursing into the next level with an
adjusted pgcount.
- Reject unmaps that are not aligned to the contiguous group size with
WARN_ON_ONCE(), instead of clearing the CONT bit on a partial group before
invalidation.
- Link to v1: https://patch.msgid.link/20260618-iommu_contig_hint-v1-1-4502a59e6388@oss.qualcomm.com
To: Will Deacon <will@kernel.org>
To: Robin Murphy <robin.murphy@arm.com>
To: "Joerg Roedel (AMD)" <joro@8bytes.org>
Cc: linux-arm-msm@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: iommu@lists.linux.dev
Cc: linux-kernel@vger.kernel.org
---
drivers/iommu/io-pgtable-arm.c | 167 +++++++++++++++++++++++++++++++++++++++--
include/linux/io-pgtable.h | 3 +
2 files changed, 162 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 476c0e25631af..241efa37f8631 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -86,6 +86,21 @@
/* Software bit for solving coherency races */
#define ARM_LPAE_PTE_SW_SYNC (((arm_lpae_iopte)1) << 55)
+/* PTE Contiguous Bit */
+#define ARM_LPAE_PTE_CONT (((arm_lpae_iopte)1) << 52)
+
+/*
+ * CONTIG HINT SUPPORT TABLE
+ *
+ *------------------------------------------------------------------
+ *| Page Size | CONT PTE | Block | CONT Block | L1 Block | CONT L1 |
+ *------------------------------------------------------------------
+ *| 4K | 64K | 2M | 32M | 1G | 16G |
+ *| 16K | 2M | 32M | 1G | | |
+ *| 64K | 2M | 512M | 16G | | |
+ *------------------------------------------------------------------
+ */
+
/* Stage-1 PTE */
#define ARM_LPAE_PTE_AP_UNPRIV (((arm_lpae_iopte)1) << 6)
#define ARM_LPAE_PTE_AP_RDONLY_BIT 7
@@ -453,6 +468,112 @@ static arm_lpae_iopte arm_lpae_install_table(arm_lpae_iopte *table,
return old;
}
+static inline bool arm_lpae_cont_hint_enabled(struct io_pgtable_cfg *cfg)
+{
+ return !(cfg->quirks & IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT);
+}
+
+static inline int arm_lpae_cont_ptes(unsigned long size)
+{
+ if (size == SZ_4K)
+ return 16;
+ if (size == SZ_16K)
+ return 128;
+ if (size == SZ_64K)
+ return 32;
+ return 1;
+}
+
+static inline unsigned long arm_lpae_cont_pte_size(unsigned long size)
+{
+ return arm_lpae_cont_ptes(size) * size;
+}
+
+static inline int arm_lpae_cont_blks(unsigned long size)
+{
+ if (size == SZ_2M)
+ return 16;
+ if (size == SZ_32M)
+ return 32;
+ if (size == SZ_512M)
+ return 32;
+ if (size == SZ_1G)
+ return 16;
+ return 1;
+}
+
+static inline unsigned long arm_lpae_cont_blk_size(unsigned long size)
+{
+ return arm_lpae_cont_blks(size) * size;
+}
+
+static unsigned long arm_lpae_get_cont_sizes(struct io_pgtable_cfg *cfg)
+{
+ unsigned long pg_size, blk_size, cont_sizes;
+ int pg_shift, bits_per_level;
+
+ if (!cfg->pgsize_bitmap || !arm_lpae_cont_hint_enabled(cfg))
+ return 0;
+
+ pg_shift = __ffs(cfg->pgsize_bitmap);
+ bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
+ pg_size = (1UL << pg_shift);
+ blk_size = (pg_size << bits_per_level);
+
+ cont_sizes = arm_lpae_cont_pte_size(pg_size) |
+ arm_lpae_cont_blk_size(blk_size);
+
+ /*
+ * Only add the level-1 contiguous block size if level-1 block mappings
+ * are supported for this granule. For 16K and 64K granules, level-1
+ * block mappings do not exist, so blk_size << bits_per_level would not
+ * be in pgsize_bitmap after arm_lpae_restrict_pgsizes() has filtered it.
+ * For 4K granule, 1G blocks are supported, giving a 16G contiguous group.
+ */
+ if (cfg->pgsize_bitmap & (blk_size << bits_per_level))
+ cont_sizes |= arm_lpae_cont_blk_size(blk_size << bits_per_level);
+
+ return cont_sizes;
+}
+
+static u32 arm_lpae_find_num_cont(struct arm_lpae_io_pgtable *data, int lvl)
+{
+ if (lvl == ARM_LPAE_MAX_LEVELS - 1)
+ return arm_lpae_cont_ptes(ARM_LPAE_BLOCK_SIZE(lvl, data));
+ else if (lvl >= ARM_LPAE_MAX_LEVELS - 3)
+ return arm_lpae_cont_blks(ARM_LPAE_BLOCK_SIZE(lvl, data));
+ else
+ return 1;
+}
+
+static u32 arm_lpae_check_num_cont(struct arm_lpae_io_pgtable *data, size_t size, int lvl)
+{
+ int num_cont;
+
+ num_cont = arm_lpae_find_num_cont(data, lvl);
+ if (size == num_cont * ARM_LPAE_BLOCK_SIZE(lvl, data))
+ return num_cont;
+ else
+ return 1;
+}
+
+static bool arm_lpae_pte_is_contiguous_range(struct arm_lpae_io_pgtable *data,
+ unsigned long size,
+ int lvl, u32 *num_cont)
+{
+ unsigned long block_size;
+
+ if (!arm_lpae_cont_hint_enabled(&data->iop.cfg)) {
+ *num_cont = 1;
+ return false;
+ }
+
+ *num_cont = arm_lpae_find_num_cont(data, lvl);
+ block_size = ARM_LPAE_BLOCK_SIZE(lvl, data);
+
+ return size == (*num_cont * block_size);
+}
+
static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
phys_addr_t paddr, size_t size, size_t pgcount,
arm_lpae_iopte prot, int lvl, arm_lpae_iopte *ptep,
@@ -463,6 +584,7 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
size_t tblsz = ARM_LPAE_GRANULE(data);
struct io_pgtable_cfg *cfg = &data->iop.cfg;
int ret = 0, num_entries, max_entries, map_idx_start;
+ u32 num_cont = 1;
/* Find our entry at the current level */
map_idx_start = ARM_LPAE_LVL_IDX(iova, lvl, data);
@@ -479,6 +601,22 @@ static int __arm_lpae_map(struct arm_lpae_io_pgtable *data, unsigned long iova,
return ret;
}
+ if (arm_lpae_pte_is_contiguous_range(data, size, lvl, &num_cont)) {
+ size_t ct_size = ARM_LPAE_BLOCK_SIZE(lvl, data);
+
+ max_entries = arm_lpae_max_entries(map_idx_start, data);
+ num_entries = min_t(int, num_cont * pgcount, max_entries);
+
+ /* Set cont bit */
+ prot |= ARM_LPAE_PTE_CONT;
+
+ ret = arm_lpae_init_pte(data, iova, paddr, prot, lvl,
+ num_entries, ptep);
+ if (!ret)
+ *mapped += num_entries * ct_size;
+ return ret;
+ }
+
/* We can't allocate tables at the final level */
if (WARN_ON(lvl >= ARM_LPAE_MAX_LEVELS - 1))
return -EINVAL;
@@ -660,7 +798,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
{
arm_lpae_iopte pte;
struct io_pgtable *iop = &data->iop;
- int i = 0, num_entries, max_entries, unmap_idx_start;
+ int i = 0, num_cont = 1, num_entries, max_entries, unmap_idx_start;
/* Something went horribly wrong and we ran out of page table */
if (WARN_ON(lvl == ARM_LPAE_MAX_LEVELS))
@@ -675,9 +813,19 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
}
/* If the size matches this level, we're in the right place */
- if (size == ARM_LPAE_BLOCK_SIZE(lvl, data)) {
+ if (size == ARM_LPAE_BLOCK_SIZE(lvl, data) ||
+ (size == arm_lpae_find_num_cont(data, lvl) *
+ ARM_LPAE_BLOCK_SIZE(lvl, data))) {
+ size_t pte_size;
+
max_entries = arm_lpae_max_entries(unmap_idx_start, data);
- num_entries = min_t(int, pgcount, max_entries);
+ num_cont = arm_lpae_check_num_cont(data, size, lvl);
+ num_entries = min_t(int, num_cont * pgcount, max_entries);
+ pte_size = size / num_cont;
+
+ if (num_cont > 1 &&
+ WARN_ON_ONCE(!IS_ALIGNED(iova, size)))
+ return 0;
/* Find and handle non-leaf entries */
for (i = 0; i < num_entries; i++) {
@@ -691,7 +839,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
__arm_lpae_clear_pte(&ptep[i], &iop->cfg, 1);
/* Also flush any partial walks */
- io_pgtable_tlb_flush_walk(iop, iova + i * size, size,
+ io_pgtable_tlb_flush_walk(iop, iova + i * pte_size, pte_size,
ARM_LPAE_GRANULE(data));
__arm_lpae_free_pgtable(data, lvl + 1, iopte_deref(pte, data));
}
@@ -702,9 +850,9 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
if (gather && !iommu_iotlb_gather_queued(gather))
for (int j = 0; j < i; j++)
- io_pgtable_tlb_add_page(iop, gather, iova + j * size, size);
+ io_pgtable_tlb_add_page(iop, gather, iova + j * pte_size, pte_size);
- return i * size;
+ return i * pte_size;
} else if (iopte_leaf(pte, lvl, iop->fmt)) {
WARN_ONCE(true, "Unmap of a partial large IOPTE is not allowed");
return 0;
@@ -943,6 +1091,7 @@ static void arm_lpae_restrict_pgsizes(struct io_pgtable_cfg *cfg)
}
cfg->pgsize_bitmap &= page_sizes;
+ cfg->pgsize_bitmap |= arm_lpae_get_cont_sizes(cfg);
cfg->ias = min(cfg->ias, max_addr_bits);
cfg->oas = min(cfg->oas, max_addr_bits);
}
@@ -1001,7 +1150,8 @@ arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
IO_PGTABLE_QUIRK_ARM_TTBR1 |
IO_PGTABLE_QUIRK_ARM_OUTER_WBWA |
IO_PGTABLE_QUIRK_ARM_HD |
- IO_PGTABLE_QUIRK_NO_WARN))
+ IO_PGTABLE_QUIRK_NO_WARN |
+ IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT))
return NULL;
data = arm_lpae_alloc_pgtable(cfg);
@@ -1103,7 +1253,8 @@ arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
typeof(&cfg->arm_lpae_s2_cfg.vtcr) vtcr = &cfg->arm_lpae_s2_cfg.vtcr;
if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_S2FWB |
- IO_PGTABLE_QUIRK_NO_WARN))
+ IO_PGTABLE_QUIRK_NO_WARN |
+ IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT))
return NULL;
data = arm_lpae_alloc_pgtable(cfg);
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index e19872e37e067..7b2097aaffb09 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -86,6 +86,8 @@ struct io_pgtable_cfg {
*
* IO_PGTABLE_QUIRK_ARM_HD: Enables dirty tracking in stage 1 pagetable.
* IO_PGTABLE_QUIRK_ARM_S2FWB: Use the FWB format for the MemAttrs bits
+ * IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT: Disable use of the contiguous
+ * hint for hardware affected by implementation-specific errata.
*
* IO_PGTABLE_QUIRK_NO_WARN: Do not WARN_ON() on conflicting
* mappings, but silently return -EEXISTS. Normally an attempt
@@ -103,6 +105,7 @@ struct io_pgtable_cfg {
#define IO_PGTABLE_QUIRK_ARM_HD BIT(7)
#define IO_PGTABLE_QUIRK_ARM_S2FWB BIT(8)
#define IO_PGTABLE_QUIRK_NO_WARN BIT(9)
+ #define IO_PGTABLE_QUIRK_ARM_NO_CONT_HINT BIT(10)
unsigned long quirks;
unsigned long pgsize_bitmap;
unsigned int ias;
---
base-commit: 4fa3f5fabb30bf00d7475d5a33459ea83d639bf9
change-id: 20260618-iommu_contig_hint-71ae491fbb52
Best regards,
--
Vijayanand Jitta <vijayanand.jitta@oss.qualcomm.com>
^ permalink raw reply related
* Re: [PATCH v3 2/4] drm/bridge: it6505: guard against zero channel count in audio infoframe
From: Chen-Yu Tsai @ 2026-07-21 3:08 UTC (permalink / raw)
To: Daniel Golle
Cc: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Allen Chen,
Hermes Wu, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <e7c7944f7ab37332c2afcc808aa8e105e4752a43.1784600387.git.daniel@makrotopia.org>
On Tue, Jul 21, 2026 at 10:26 AM Daniel Golle <daniel@makrotopia.org> wrote:
>
> it6505->audio.channel_count is zero from allocation until either DP
> link training has run once (it6505_variable_config()) or a valid
> hw_params call has cached a channel count. it6505_enable_audio() can
> be reached before either of those has happened, e.g. from the
> audio-FIFO-error IRQ, which is unmasked unconditionally at poweron.
> it6505_enable_audio_infoframe() then indexes the 8-entry
> audio_info_ca[] table with channel_count - 1, an out-of-bounds stack
> read when channel_count is still 0.
>
> Bail out of it6505_enable_audio_infoframe() when channel_count is not
> yet known instead of indexing with it.
>
> While here, fix the debug print in it6505_audio_setup_hw_params(): on
> the invalid-channel-count path it logged the previously cached
> channel_count instead of the actually-rejected params->cea.channels.
>
> Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Reviewed-by: Chen-Yu Tsai <wenst@chromium.org>
^ permalink raw reply
* Re: [PATCH v3 7/8] dt-bindings: usb: generic-xhci: add Axiado AX3005 xHCI variant
From: Swark Yang @ 2026-07-21 3:02 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
Linus Walleij, Bartosz Golaszewski, Jan Kotas, Michal Simek,
Andi Shyti, Przemysław Gaj, Alexandre Belloni, Frank Li,
Boris Brezillon, Greg Kroah-Hartman, Jiri Slaby, Mark Brown,
Mathias Nyman, devicetree, linux-arm-kernel, linux-kernel,
linux-gpio, linux-i2c, linux-i3c, linux-serial, linux-spi,
linux-usb
In-Reply-To: <20260717-sarcastic-hyena-of-music-27cc5d@quoll>
On 7/17/2026 4:37 PM, Krzysztof Kozlowski wrote:
>
> Just add it to the enum in "Armada 37xx/8k SoCs" group while dropping
> that description. Description anyway repeats the compatibles so is
> completely redundant.
>
> Best regards,
> Krzysztof
>
Hi Krzysztof,
Got it. I'll drop the redundant description and move it to the
"Armada 37xx/8k SoCs" enum.
(This will also be split and sent separately to the USB subsystem in
the next version).
Thanks,
Swark
^ permalink raw reply
* [PATCH] arm64: dts: rockchip: Enable USB device mode on rk3399-roc-pc-plus
From: Fabio Estevam @ 2026-07-21 3:00 UTC (permalink / raw)
To: heiko
Cc: robh, krzk+dt, conor+dt, devicetree, linux-arm-kernel,
linux-rockchip, Fabio Estevam
From: Fabio Estevam <festevam@nabladev.com>
The USB3 OTG controller on the rk3399-roc-pc-plus is forced to host
mode. As a result, it does not register a UDC and USB gadget drivers
fail with:
UDC core: g_mass_storage: couldn't find an available UDC
Describe the controller as a USB role switch and select peripheral as
the default role. This makes DWC3 register a UDC when no external role
provider has selected a host role, while retaining OTG operation and
both the USB2 and USB3 PHYs inherited from the SoC dtsi.
Tested by binding g_mass_storage and exposing the onboard eMMC to a
Linux host.
Fixes: 6d9a7bd6a13c ("arm64: dts: rockchip: add support for Firefly ROC-RK3399-PC-PLUS")
Signed-off-by: Fabio Estevam <festevam@nabladev.com>
---
arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
index 7e8039b19be7..c733429fd5d9 100644
--- a/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3399-roc-pc-plus.dts
@@ -270,7 +270,9 @@ bluetooth {
};
&usbdrd_dwc3_0 {
- dr_mode = "host";
+ dr_mode = "otg";
+ role-switch-default-mode = "peripheral";
+ usb-role-switch;
status = "okay";
};
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v3 6/8] dt-bindings: spi: dw-apb-ssi: add Axiado AX3005 SPI variant
From: Swark Yang @ 2026-07-21 2:47 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
Linus Walleij, Bartosz Golaszewski, Jan Kotas, Michal Simek,
Andi Shyti, Przemysław Gaj, Alexandre Belloni, Frank Li,
Boris Brezillon, Greg Kroah-Hartman, Jiri Slaby, Mark Brown,
Mathias Nyman, devicetree, linux-arm-kernel, linux-kernel,
linux-gpio, linux-i2c, linux-i3c, linux-serial, linux-spi,
linux-usb
In-Reply-To: <20260717-admirable-robust-adder-7aad70@quoll>
On 7/17/2026 4:34 PM, Krzysztof Kozlowski wrote:
> Rather: Vendor controllers compatible with v1.01a
>
> and then place it after the group with "description: Vendor controllers which
> use snps,dw-apb-ssi as fallback".
>
> Best regards,
> Krzysztof
>
Hi Krzysztof,
Will do. I'll change the first entry to an enum and move it after the
fallback group.
Note: Following Mark's advice, I will be splitting this out and sending
it separately to the SPI subsystem in the next iteration.
Best Regards,
Swark
^ permalink raw reply
* Re: [PATCH v3 6/8] dt-bindings: spi: dw-apb-ssi: add Axiado AX3005 SPI variant
From: Swark Yang @ 2026-07-21 2:37 UTC (permalink / raw)
To: Mark Brown
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Harshit Shah,
Linus Walleij, Bartosz Golaszewski, Jan Kotas, Michal Simek,
Andi Shyti, Przemysław Gaj, Alexandre Belloni, Frank Li,
Boris Brezillon, Greg Kroah-Hartman, Jiri Slaby, Mathias Nyman,
devicetree, linux-arm-kernel, linux-kernel, linux-gpio, linux-i2c,
linux-i3c, linux-serial, linux-spi, linux-usb
In-Reply-To: <6d69cfd0-5f3b-4324-8d64-b4695cbb3e92@sirena.org.uk>
On 7/17/2026 8:02 PM, Mark Brown wrote:
> If there is no need for things to be part of a multi-subsystem series
> (eg, dependencies) it's normally better to send each subsystem
> separately.
Hi Mark,
Got it. I will split the series and send the SPI binding separately
in the next iteration.
Quick question on the versioning: should I send the newly split SPI
patch as a fresh v1, or continue with v4 to align with the rest of
the SoC series?
Best Regards,
Swark
^ permalink raw reply
* Re: [RFC PATCH 31/36] arm64: nmi: Add handling of superpriority interrupts as NMIs
From: Jinjie Ruan @ 2026-07-21 2:33 UTC (permalink / raw)
To: Vladimir Murzin, linux-arm-kernel
Cc: mark.rutland, maz, Mark Brown, catalin.marinas, will
In-Reply-To: <63cb9447-a219-4115-a6ed-c69aeaa45535@arm.com>
On 7/15/2026 12:24 AM, Vladimir Murzin wrote:
> On 7/10/26 11:13, Jinjie Ruan wrote:
>>> diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
>>> index a13653b228b7..de71d5a3a6a1 100644
>>> --- a/arch/arm64/kernel/entry-common.c
>>> +++ b/arch/arm64/kernel/entry-common.c
>>> @@ -525,8 +525,8 @@ asmlinkage void noinstr el1h_64_sync_handler(struct pt_regs *regs)
>>> arm64_debug_exc_context(CRITICAL_CONTEXT);
>>> }
>>>
>>> -static __always_inline void __el1_pnmi(struct pt_regs *regs,
>>> - void (*handler)(struct pt_regs *))
>>> +static __always_inline void __el1_nmi(struct pt_regs *regs,
>>> + void (*handler)(struct pt_regs *))
>>> {
>>> arm64_exc_hwstate_t hwstate;
>>> irqentry_state_t state;
>>> @@ -545,7 +545,10 @@ static __always_inline void __el1_irq(struct pt_regs *regs,
>>>
>>> state = arm64_enter_from_kernel_mode(regs);
>>>
>>> - arm64_unmask_exc_context(NONMI_CONTEXT);
>>> + if (system_uses_nmi())
>>> + arm64_unmask_exc_context(NOIRQ_CONTEXT);
>>> + else
>>> + arm64_unmask_exc_context(NONMI_CONTEXT);
>> This looks strange; whether it's a pseudo NMI or FEAT_NMI, the behavior
>> should be consistent.
>
> Here is my understanding of how things behave.
>
> No NMI support
>
> NONMI_CONTEXT and NOIRQ_CONTEXT are the same, so we cannot take any
> IRQ while handling an IRQ. On return from the handler, we remain in
> NOIRQ_CONTEXT. Easy.
>
> pNMI
>
> We cannot distinguish between an NMI and an IRQ on exception entry,
> so we have to postpone unmasking the IF bits until the interrupt
> handler. In other words, we enter the interrupt handler in
> NONMI_CONTEXT.
>
> In the handler:
>
> 1. For an NMI, we handle the NMI and then drop to NOIRQ_CONTEXT.
>
> 2. For an IRQ, we drop to NOIRQ_CONTEXT (allowing NMIs to preempt
> the IRQ handler) and then handle the IRQ.
>
> In both cases, we return from the handler in NOIRQ_CONTEXT.
>
> FEAT_NMI
>
> We can distinguish between an NMI and an IRQ on exception
> entry. Thus, for an IRQ, we can immediately drop to NOIRQ_CONTEXT
> before passing control to the IRQ handler, allowing NMIs to preempt
> the IRQ handler. This corresponds to case 2 of the pNMI flow above.
>
>
> Perhaps above could be better expressed with:
>
> if (gic_supports_pseudo_nmis())
> arm64_unmask_exc_context(NONMI_CONTEXT);
> else
> arm64_unmask_exc_context(NOIRQ_CONTEXT);
Making it this way is reasonable.I understand that only when handling an
IRQ can the masking of NMI be canceled, which is a special case for
pseudo-NMI.
Here, not clearing the DAIF.I bit might be to prevent pseudo-NMI
nesting? Because at the interrupt entry point, it is still unclear
whether this is a pseudo-NMI or an IRQ being handled.
Best regards,
Jinjie
>
>
> Have I missed anything?
>
> Cheers
> Vladimir
>
^ permalink raw reply
* [PATCH v3 4/4] drm/bridge: it6505: Don't reject audio hw_params without an encoder
From: Daniel Golle @ 2026-07-21 2:28 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Allen Chen,
Hermes Wu, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <cover.1784600387.git.daniel@makrotopia.org>
it6505_audio_setup_hw_params() returns -ENODEV when the bridge is not
attached to a DRM encoder. Now that it6505 registers an hdmi-audio-codec,
this callback runs whenever a stream is configured on the I2S DAI it is
wired to, including when that DAI is shared with another codec. On
mt8186-corsola the speaker amplifier (rt1019) and it6505 share I2S3, so
when the it6505 DP output has no display attached (bridge.encoder is
NULL) the -ENODEV propagates up through dpcm_be_dai_hw_params() and tears
down the whole backend, breaking speaker playback.
The check is harmful in its own right: the rest of the function only
caches the stream parameters (channel count, rate, word length) in
software, none of which needs an encoder. Returning early leaves
it6505->audio.channel_count at 0, which would then index
audio_info_ca[-1] in it6505_enable_audio_infoframe() should a display be
hotplugged while a stream is running, and makes such a hotplug play with
stale parameters.
Drop the encoder check so the parameters are always cached; the actual
audio output is already gated by it6505->powered.
Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v3: no changes
v2:
* drop the encoder check entirely instead of returning 0 early, so
the stream parameters are always cached even while no display is
attached; rewrite the commit message to explain why the check is
harmful
drivers/gpu/drm/bridge/ite-it6505.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 37036ef5403f..435601b6929e 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -2999,9 +2999,6 @@ static int it6505_audio_setup_hw_params(struct it6505 *it6505,
params->sample_rate, params->sample_width,
params->cea.channels);
- if (!it6505->bridge.encoder)
- return -ENODEV;
-
if (params->cea.channels <= 1 || params->cea.channels > 8) {
DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support",
params->cea.channels);
--
2.55.0
^ permalink raw reply related
* [PATCH v3 3/4] drm/bridge: it6505: Add audio support
From: Daniel Golle @ 2026-07-21 2:27 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Allen Chen,
Hermes Wu, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <cover.1784600387.git.daniel@makrotopia.org>
From: Jiaxin Yu <jiaxin.yu@mediatek.com>
Add audio support for it6505 by bridging to the hdmi-codec, registering
an "hdmi-audio-codec" platform device from probe. The it6505's audio
setup/shutdown helpers were merged earlier as unused code; wire them up
via hdmi_codec_ops so the DAI actually appears, which unblocks the
mt8186-mt6366 sound card that references it6505 as the I2S3 codec.
Some DP-to-HDMI dongles get into a bad state if InfoFrame is sent
without audio data, so it6505's audio is only enabled once the stream
is unmuted.
it6505_enable_audio()/it6505_disable_audio() can be called from
several contexts: DP link training (link_works), the HPD-low path,
the audio-FIFO-error IRQ, and now also the delayed audio-enable work
and the .mute_stream/.audio_shutdown hdmi_codec_ops added here. None
of these are mutually exclusive, so serialize the actual register
sequences with a new audio_lock mutex.
Also track the mute state requested through .mute_stream/
.audio_shutdown in it6505->audio.mute, and have the audio-FIFO-error
IRQ handler skip re-enabling audio while muted: without this, an
audio-FIFO error following an ALSA-requested mute would silently
re-enable audio and could send the very InfoFrame-without-data
sequence the delayed-enable logic above is meant to avoid.
Link: https://lore.kernel.org/all/20230730180803.22570-4-jiaxin.yu@mediatek.com/
Signed-off-by: Jiaxin Yu <jiaxin.yu@mediatek.com>
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v3:
* serialise it6505_enable_audio()/it6505_disable_audio() with a new
audio_lock mutex, resolving the "input welcome on whether a lock
is warranted" note below
* track requested mute state in it6505->audio.mute and have the
audio-FIFO-error IRQ handler skip re-enabling audio while muted,
both addressing automated review of v2
v2:
* store the hdmi-codec platform_device and unregister it on i2c
remove, fixing a resource leak and use-after-free on unbind
* initialise the delayed audio work before registering the codec
device instead of after
* synchronously cancel the delayed audio work on audio shutdown and
on driver remove (cancel_delayed_work_sync)
* rework the mute path to cancel pending work synchronously and
disable audio immediately when muting, removing a race with the
delayed enable work
djg: respin of Jiaxin Yu's v3, rebased onto current mainline. Changes
from v3: hdmi_codec_ops lost .trigger, so drive enable/disable from
.mute_stream; drop the now-redundant __maybe_unused; use it6505->dev
instead of &client->dev, addressing AngeloGioacchino Del Regno's v3
review. Also fix issues in v3: initialise delayed_audio before
registering the codec device; keep and unregister the platform_device
and cancel delayed_audio on remove to avoid a leak and a
use-after-free; cancel the delayed work on audio shutdown; and
actually disable audio (not just cancel the pending enable) when the
stream is muted.
---
drivers/gpu/drm/bridge/ite-it6505.c | 126 ++++++++++++++++++++++++----
1 file changed, 108 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index f2ed76a05f6b..37036ef5403f 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -407,6 +407,7 @@ struct it6505_audio_data {
u8 i2s_data_delay;
u8 i2s_ws_channel;
u8 i2s_data_sequence;
+ bool mute;
};
struct it6505_audio_sample_rate_map {
@@ -439,6 +440,7 @@ struct it6505 {
struct mutex extcon_lock;
struct mutex mode_lock; /* used to bridge_detect */
struct mutex aux_lock; /* used to aux data transfers */
+ struct mutex audio_lock; /* serializes audio enable/disable */
struct regmap *regmap;
struct drm_display_mode source_output_mode;
struct drm_display_mode video_info;
@@ -476,6 +478,7 @@ struct it6505 {
bool enable_enhanced_frame;
hdmi_codec_plugged_cb plugged_cb;
struct device *codec_dev;
+ struct platform_device *audio_pdev;
struct delayed_work delayed_audio;
struct it6505_audio_data audio;
struct dentry *debugfs;
@@ -1594,7 +1597,7 @@ static void it6505_enable_audio_infoframe(struct it6505 *it6505)
EN_AUD_CTRL_PKT);
}
-static void it6505_disable_audio(struct it6505 *it6505)
+static void __it6505_disable_audio(struct it6505 *it6505)
{
it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, EN_AUD_MUTE);
it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, M_AUDIO_I2S_EN, 0x00);
@@ -1602,13 +1605,23 @@ static void it6505_disable_audio(struct it6505 *it6505)
it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, AUDIO_RESET);
}
+static void it6505_disable_audio(struct it6505 *it6505)
+{
+ mutex_lock(&it6505->audio_lock);
+ __it6505_disable_audio(it6505);
+ mutex_unlock(&it6505->audio_lock);
+}
+
static void it6505_enable_audio(struct it6505 *it6505)
{
struct device *dev = it6505->dev;
int regbe;
DRM_DEV_DEBUG_DRIVER(dev, "start");
- it6505_disable_audio(it6505);
+
+ mutex_lock(&it6505->audio_lock);
+
+ __it6505_disable_audio(it6505);
it6505_setup_audio_channel_status(it6505);
it6505_setup_audio_format(it6505);
@@ -1627,6 +1640,8 @@ static void it6505_enable_audio(struct it6505 *it6505)
DRM_DEV_DEBUG_DRIVER(dev, "regbe:0x%02x audio input fs: %d.%d kHz",
regbe, 6750 / regbe, (6750 % regbe) * 10 / regbe);
it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, 0x00);
+
+ mutex_unlock(&it6505->audio_lock);
}
static bool it6505_use_step_train_check(struct it6505 *it6505)
@@ -2323,19 +2338,12 @@ static void it6505_stop_link_train(struct it6505 *it6505)
static void it6505_link_train_ok(struct it6505 *it6505)
{
- struct device *dev = it6505->dev;
-
it6505->link_state = LINK_OK;
/* disalbe mute enable avi info frame */
it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, 0x00);
it6505_set_bits(it6505, REG_INFOFRAME_CTRL,
EN_VID_CTRL_PKT, EN_VID_CTRL_PKT);
- if (it6505_audio_input(it6505)) {
- DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!");
- it6505_enable_audio(it6505);
- }
-
if (it6505->hdcp_desired)
it6505_start_hdcp(it6505);
}
@@ -2627,6 +2635,9 @@ static void it6505_irq_audio_fifo_error(struct it6505 *it6505)
DRM_DEV_DEBUG_DRIVER(dev, "audio fifo error Interrupt");
+ if (it6505->audio.mute)
+ return;
+
if (it6505_audio_input(it6505))
it6505_enable_audio(it6505);
}
@@ -2963,7 +2974,7 @@ static void it6505_remove_notifier_module(struct it6505 *it6505)
}
}
-static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
+static void it6505_delayed_audio(struct work_struct *work)
{
struct it6505 *it6505 = container_of(work, struct it6505,
delayed_audio.work);
@@ -2977,9 +2988,9 @@ static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
it6505_enable_audio(it6505);
}
-static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
- struct hdmi_codec_params
- *params)
+static int it6505_audio_setup_hw_params(struct it6505 *it6505,
+ struct hdmi_codec_params
+ *params)
{
struct device *dev = it6505->dev;
int i = 0;
@@ -3034,18 +3045,55 @@ static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
return 0;
}
-static void __maybe_unused it6505_audio_shutdown(struct device *dev, void *data)
+static void it6505_audio_shutdown(struct device *dev, void *data)
{
struct it6505 *it6505 = dev_get_drvdata(dev);
+ it6505->audio.mute = true;
+ cancel_delayed_work_sync(&it6505->delayed_audio);
if (it6505->powered)
it6505_disable_audio(it6505);
}
-static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev,
- void *data,
- hdmi_codec_plugged_cb fn,
- struct device *codec_dev)
+static int it6505_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ return it6505_audio_setup_hw_params(it6505, params);
+}
+
+static int it6505_audio_mute(struct device *dev, void *data,
+ bool enable, int direction)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+
+ DRM_DEV_DEBUG_DRIVER(dev, "mute: %d", enable);
+
+ it6505->audio.mute = enable;
+
+ /*
+ * Some DP-to-HDMI dongles get into a bad state if the InfoFrame is
+ * sent without audio data, so only enable it6505's audio once the
+ * stream is unmuted (i.e. actually playing).
+ */
+ if (enable) {
+ cancel_delayed_work_sync(&it6505->delayed_audio);
+ if (it6505->powered)
+ it6505_disable_audio(it6505);
+ } else {
+ queue_delayed_work(system_wq, &it6505->delayed_audio,
+ msecs_to_jiffies(180));
+ }
+
+ return 0;
+}
+
+static int it6505_audio_hook_plugged_cb(struct device *dev,
+ void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
{
struct it6505 *it6505 = data;
@@ -3056,6 +3104,39 @@ static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev,
return 0;
}
+static const struct hdmi_codec_ops it6505_audio_codec_ops = {
+ .hw_params = it6505_audio_hw_params,
+ .mute_stream = it6505_audio_mute,
+ .audio_shutdown = it6505_audio_shutdown,
+ .hook_plugged_cb = it6505_audio_hook_plugged_cb,
+};
+
+static int it6505_register_audio_driver(struct device *dev)
+{
+ struct it6505 *it6505 = dev_get_drvdata(dev);
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &it6505_audio_codec_ops,
+ .max_i2s_channels = 8,
+ .i2s = 1,
+ .no_capture_mute = 1,
+ .data = it6505,
+ };
+ struct platform_device *pdev;
+
+ INIT_DELAYED_WORK(&it6505->delayed_audio, it6505_delayed_audio);
+
+ pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO, &codec_data,
+ sizeof(codec_data));
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ it6505->audio_pdev = pdev;
+ DRM_DEV_DEBUG_DRIVER(dev, "bound to %s", HDMI_CODEC_DRV_NAME);
+
+ return 0;
+}
+
static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge)
{
return container_of(bridge, struct it6505, bridge);
@@ -3565,6 +3646,7 @@ static int it6505_i2c_probe(struct i2c_client *client)
mutex_init(&it6505->extcon_lock);
mutex_init(&it6505->mode_lock);
mutex_init(&it6505->aux_lock);
+ mutex_init(&it6505->audio_lock);
it6505->bridge.of_node = client->dev.of_node;
it6505->connector_status = connector_status_disconnected;
@@ -3615,6 +3697,12 @@ static int it6505_i2c_probe(struct i2c_client *client)
return err;
}
+ err = it6505_register_audio_driver(dev);
+ if (err < 0) {
+ dev_err(dev, "Failed to register audio driver: %d", err);
+ return err;
+ }
+
INIT_WORK(&it6505->link_works, it6505_link_training_work);
INIT_WORK(&it6505->hdcp_wait_ksv_list, it6505_hdcp_wait_ksv_list);
INIT_DELAYED_WORK(&it6505->hdcp_work, it6505_hdcp_work);
@@ -3647,6 +3735,8 @@ static void it6505_i2c_remove(struct i2c_client *client)
{
struct it6505 *it6505 = i2c_get_clientdata(client);
+ platform_device_unregister(it6505->audio_pdev);
+ cancel_delayed_work_sync(&it6505->delayed_audio);
cancel_work_sync(&it6505->link_works);
cancel_work_sync(&it6505->hdcp_wait_ksv_list);
cancel_delayed_work_sync(&it6505->hdcp_work);
--
2.55.0
^ permalink raw reply related
* [PATCH v3 2/4] drm/bridge: it6505: guard against zero channel count in audio infoframe
From: Daniel Golle @ 2026-07-21 2:26 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Allen Chen,
Hermes Wu, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <cover.1784600387.git.daniel@makrotopia.org>
it6505->audio.channel_count is zero from allocation until either DP
link training has run once (it6505_variable_config()) or a valid
hw_params call has cached a channel count. it6505_enable_audio() can
be reached before either of those has happened, e.g. from the
audio-FIFO-error IRQ, which is unmasked unconditionally at poweron.
it6505_enable_audio_infoframe() then indexes the 8-entry
audio_info_ca[] table with channel_count - 1, an out-of-bounds stack
read when channel_count is still 0.
Bail out of it6505_enable_audio_infoframe() when channel_count is not
yet known instead of indexing with it.
While here, fix the debug print in it6505_audio_setup_hw_params(): on
the invalid-channel-count path it logged the previously cached
channel_count instead of the actually-rejected params->cea.channels.
Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v3: new patch
drivers/gpu/drm/bridge/ite-it6505.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 2f349aefb705..f2ed76a05f6b 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -1575,6 +1575,9 @@ static void it6505_enable_audio_infoframe(struct it6505 *it6505)
struct device *dev = it6505->dev;
u8 audio_info_ca[] = { 0x00, 0x00, 0x01, 0x03, 0x07, 0x0B, 0x0F, 0x1F };
+ if (!it6505->audio.channel_count)
+ return;
+
DRM_DEV_DEBUG_DRIVER(dev, "infoframe channel_allocation:0x%02x",
audio_info_ca[it6505->audio.channel_count - 1]);
@@ -2990,7 +2993,7 @@ static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
if (params->cea.channels <= 1 || params->cea.channels > 8) {
DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support",
- it6505->audio.channel_count);
+ params->cea.channels);
return -EINVAL;
}
--
2.55.0
^ permalink raw reply related
* [PATCH v3 1/4] drm/bridge: it6505: cancel outstanding work before teardown in remove()
From: Daniel Golle @ 2026-07-21 2:26 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Allen Chen,
Hermes Wu, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
In-Reply-To: <cover.1784600387.git.daniel@makrotopia.org>
it6505_i2c_remove() tears down the bridge and frees its state without
cancelling any of the driver's background work. struct it6505 is
allocated with devm_drm_bridge_alloc(), so devres frees it as soon as
remove() returns; link_works, hdcp_wait_ksv_list, hdcp_work and
extcon_wq can all still be pending or running at that point and will
then dereference freed memory.
link_works and extcon_wq are flushed in it6505_bridge_detach(), but
that only runs if DRM core calls the bridge's .detach() before the
i2c client is unbound -- not guaranteed if the i2c driver is unbound
independently of the DRM device that consumes it. hdcp_wait_ksv_list
and hdcp_work are never cancelled anywhere.
Cancel all four synchronously in remove() itself so teardown is safe
regardless of ordering. cancel_work_sync() on extcon_wq is safe even
when it was never INIT_WORK()'d, since it6505 is zero-allocated and a
zeroed work_struct is reported as not pending.
Fixes: b5c84a9edcd4 ("drm/bridge: add it6505 driver")
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
---
v3: new patch, split out to fix a pre-existing use-after-free flagged
by automated review of v2's audio series; needed before that
series adds another cancel_delayed_work_sync() call next to the
ones this commit is adding.
drivers/gpu/drm/bridge/ite-it6505.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index 8ecb43611dba..2f349aefb705 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -3644,6 +3644,10 @@ static void it6505_i2c_remove(struct i2c_client *client)
{
struct it6505 *it6505 = i2c_get_clientdata(client);
+ cancel_work_sync(&it6505->link_works);
+ cancel_work_sync(&it6505->hdcp_wait_ksv_list);
+ cancel_delayed_work_sync(&it6505->hdcp_work);
+ cancel_work_sync(&it6505->extcon_wq);
drm_bridge_remove(&it6505->bridge);
drm_dp_aux_unregister(&it6505->aux);
it6505_debugfs_remove(it6505);
--
2.55.0
^ permalink raw reply related
* Re: [PATCH v5 4/4] irqchip/gic-v3-its: Fix grammar and replace a bit number with its symbol
From: Kemeng Shi @ 2026-07-21 2:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <46025d0b3255bd62412598eedae4eda03a61cd39.camel@rendec.net>
在 2026/7/21 8:58:52, Radu Rendec 写道:
> On Mon, 2026-07-20 at 15:12 +0800, Kemeng Shi wrote:
>> Fix grammatical errors in comments and simplify the comment about reading
>> GITS_BASER_INDIRECT to check two-level support.
>>
>> Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
>> ---
>> drivers/irqchip/irq-gic-v3-its.c | 5 ++---
>> 1 file changed, 2 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
>> index 5dc91862fc15..8935a6e7a20f 100644
>> --- a/drivers/irqchip/irq-gic-v3-its.c
>> +++ b/drivers/irqchip/irq-gic-v3-its.c
>> @@ -163,7 +163,7 @@ struct event_lpi_map {
>>
>> /*
>> * The ITS view of a device - belongs to an ITS, owns an interrupt
>> - * translation table, and a list of interrupts. If it some of its
>> + * translation table, and a list of interrupts. If some of its
>> * LPIs are injected into a guest (GICv4), the event_map.vm field
>> * indicates which one.
>> */
>> @@ -2502,8 +2502,7 @@ static bool its_parse_indirect_baser(struct its_node *its,
>> /* No need to enable Indirection if memory requirement < (psz*2)bytes */
>> if ((esz << ids) > (psz * 2)) {
>> /*
>> - * Find out whether hw supports a single or two-level table by
>> - * table by reading bit at offset '62' after writing '1' to it.
>> + * Find out whether hw supports a single or two-level table
>> */
>
> nit: since it's just one line of text now, you can put the /* */
> comment markers on the same line, which is the preferred style for
> single-line comments.
Thanks for letting me know. I will improve this in the next version.>
>> its_write_baser(its, baser, val | GITS_BASER_INDIRECT);
>> indirect = !!(baser->val & GITS_BASER_INDIRECT);
>
> With that,
>
> Reviewed-by: Radu Rendec <radu@rendec.net>
^ permalink raw reply
* [PATCH v3 0/4] drm/bridge: it6505: DP audio support + shared-DAI hw_params fix
From: Daniel Golle @ 2026-07-21 2:26 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Matthias Brugger, AngeloGioacchino Del Regno, Allen Chen,
Hermes Wu, dri-devel, linux-kernel, linux-arm-kernel,
linux-mediatek
This series revives HDMI/DP audio support for the ITE IT6505 DP
bridge, originally posted by Jiaxin Yu [1], rebased onto current
-next and with the v3 review feedback addressed: the stale
&client->dev usages reported by AngeloGioacchino Del Regno [2] are
gone (the driver meanwhile gained it6505->dev), and the
"#sound-dai-cells" binding property requested by Chen-Yu Tsai [3] has
since been added by commit 325af1bef5b9 ("dt-bindings: display:
bridge: it6505: Add #sound-dai-cells").
Patch 1 fixes a pre-existing use-after-free in the driver's remove()
path: none of link_works, hdcp_wait_ksv_list or hdcp_work were ever
cancelled before devres frees the it6505 struct, unless DRM core
happened to call the bridge's .detach() first. It is needed before
patch 3 so that wiring up the new hdmi-audio-codec platform_device
does not add another caller into an already-broken teardown path.
Patch 2 fixes a second pre-existing bug: it6505->audio.channel_count
is zero until either DP link training or a valid ALSA hw_params call
has run, but it6505_enable_audio_infoframe() indexes an 8-entry table
with channel_count - 1 unconditionally, which is an out-of-bounds
stack read if the audio-FIFO-error IRQ fires first. It also fixes a
debug print that logged the previous channel count instead of the
rejected one. Needed before patch 3 for the same reason as patch 1:
that patch starts exercising the audio-FIFO-error IRQ path in a
configuration (shared I2S bus) where it is actually likely to fire
before any display has ever been hotplugged.
Patch 3 wires up the previously-unused it6505 audio helpers via
hdmi_codec_ops, which unblocks the mt8186-mt6366 sound card that
references it6505 as the I2S3 codec.
Patch 4 is a follow-up fix so that audio hw_params are accepted even
when the it6505 DP output has no display (and thus no encoder)
attached. This is needed when the it6505 shares its I2S bus with
another codec, as on the MediaTek MT8186 "steelix" Chromebooks, where
the shared I2S3 must keep working for the speaker path regardless of
the it6505 link state.
Tested on a MediaTek MT8186 (google,steelix) Chromebook.
Actual HDMI/DP audio output could not be tested lacking the USB-C
adapter cable. However, making the it6505 at least probe and fixing
the obviously missing things makes the sound card on the Chromebook
come up and internal speakers and microphone as well as the headset
mini-jack work as expected (both tested).
[1] https://lore.kernel.org/all/20230730180803.22570-4-jiaxin.yu@mediatek.com/
[2] https://lore.kernel.org/all/c35ef2d8-ab40-484b-9a4c-38f2f3e7d99c@collabora.com/
[3] https://lore.kernel.org/all/CAGXv+5G2tP9i8VrUc6-xs2d72_nL9XH9iSCeixzA2AM7X5fXOQ@mail.gmail.com/
---
v3:
* new patch 1: cancel link_works, hdcp_wait_ksv_list and hdcp_work in
it6505_i2c_remove(), fixing a pre-existing use-after-free flagged
by automated review of v2
* new patch 2: guard it6505_enable_audio_infoframe() against
channel_count == 0 and fix a debug log printing the wrong channel
count, both pre-existing bugs flagged by automated review of v2
* patch 3 (formerly patch 1): serialise it6505_enable_audio() /
it6505_disable_audio() with a new audio_lock mutex, resolving the
"input welcome on whether a lock is warranted" note in the commit
message; track explicit mute state so the audio-FIFO-error IRQ
no longer re-enables audio out from under an ALSA-requested mute,
both issues flagged by automated review of v2
* patch 4 (formerly patch 2): no changes
v2: https://lore.kernel.org/all/cover.1784561622.git.daniel@makrotopia.org/
* it6505: store the hdmi-codec platform_device and unregister it on
i2c remove, fixing a resource leak / use-after-free
* it6505: initialise the delayed audio work before registering the
codec device
* it6505: synchronously cancel the delayed audio work on audio
shutdown and on driver remove (cancel_delayed_work_sync)
* it6505: rework the mute path to cancel pending work synchronously
and disable audio immediately when muting, removing a race
* patch 2: drop the encoder check entirely instead of returning 0
early, so the stream parameters are always cached; rewrite the
commit message accordingly
v1: https://lore.kernel.org/all/cover.1784393979.git.daniel@makrotopia.org/
Daniel Golle (3):
drm/bridge: it6505: cancel outstanding work before teardown in
remove()
drm/bridge: it6505: guard against zero channel count in audio
infoframe
drm/bridge: it6505: Don't reject audio hw_params without an encoder
Jiaxin Yu (1):
drm/bridge: it6505: Add audio support
drivers/gpu/drm/bridge/ite-it6505.c | 138 +++++++++++++++++++++++-----
1 file changed, 116 insertions(+), 22 deletions(-)
base-commit: 0718283ab28bc3907e10b61a6b4be6fefa1cbb2f
--
2.55.0
^ permalink raw reply
* Re: [RFC PATCH 36/36] irqchip/gic-v3: Implement FEAT_GICv3_NMI support
From: Jinjie Ruan @ 2026-07-21 2:25 UTC (permalink / raw)
To: Vladimir Murzin, linux-arm-kernel
Cc: mark.rutland, maz, Lorenzo Pieralisi, Mark Brown, catalin.marinas,
will
In-Reply-To: <20260709121333.23507-37-vladimir.murzin@arm.com>
On 7/9/2026 8:13 PM, Vladimir Murzin wrote:
> From: Lorenzo Pieralisi <lpieralisi@kernel.org>
>
> The FEAT_GICv3_NMI GIC feature coupled with the CPU FEAT_NMI enables
> handling NMI interrupts in HW on aarch64, by adding a superpriority
> interrupt to the existing GIC priority scheme.
>
> Implement GIC driver support for the FEAT_GICv3_NMI feature.
>
> Rename gic_supports_nmi() helper function to gic_supports_pseudo_nmis()
> to make the pseudo NMIs code path clearer and more explicit.
>
> Check, through the ARM64 capabilitity infrastructure, if support
> for FEAT_NMI was detected on the core and the system has not overridden
> the detection and forced pseudo-NMIs enablement.
>
> If FEAT_NMI is detected, it was not overridden (check embedded in the
> system_uses_nmi() call) and the GIC supports the FEAT_GICv3_NMI
> feature, initialize NMIs related HW GIC registers and route irq to NMI
> handling logic.
>
> Signed-off-by: Lorenzo Pieralisi <lpieralisi@kernel.org>
> Signed-off-by: Mark Brown <broonie@kernel.org>
> Signed-off-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
> drivers/irqchip/irq-gic-v3.c | 122 +++++++++++++++++++++++++----
> include/linux/irqchip/arm-gic-v3.h | 4 +
> 2 files changed, 111 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
> index 94c6a3f2b009..625be4fc06b4 100644
> --- a/drivers/irqchip/irq-gic-v3.c
> +++ b/drivers/irqchip/irq-gic-v3.c
> @@ -64,6 +64,7 @@ struct gic_chip_data {
> u32 nr_redist_regions;
> u64 flags;
> bool has_rss;
> + bool has_nmi;
> unsigned int ppi_nr;
> struct partition_affinity *parts;
> unsigned int nr_parts;
> @@ -87,7 +88,7 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
> #define GIC_LINE_NR min(GICD_TYPER_SPIS(gic_data.rdists.gicd_typer), 1020U)
> #define GIC_ESPI_NR GICD_TYPER_ESPIS(gic_data.rdists.gicd_typer)
>
> -static bool nmi_support_forbidden;
> +static bool pnmi_support_forbidden;
>
> /*
> * There are 16 SGIs, though we only actually use 8 in Linux. The other 8 SGIs
> @@ -185,7 +186,7 @@ static void __init gic_prio_init(void)
> pr_warn("Broken GIC integration, security disabled\n");
> } else {
> pr_warn("Broken GIC integration, pNMI forbidden\n");
> - nmi_support_forbidden = true;
> + pnmi_support_forbidden = true;
> }
> }
>
> @@ -252,6 +253,37 @@ enum gic_intid_range {
> __INVALID_RANGE__
> };
>
> +#ifdef CONFIG_ARM64
> +#include <asm/cpufeature.h>
> +
> +static inline bool gic_supports_v3_3_nmi(void)
> +{
> + return gic_data.has_nmi && system_uses_nmi();
> +}
> +
> +static inline u64 gic_read_nmiar(void)
> +{
> + u64 irqstat;
> +
> + irqstat = read_sysreg_s(SYS_ICC_NMIAR1_EL1);
> +
> + dsb(sy);
> +
> + return irqstat;
> +}
> +
> +#else
> +static inline bool gic_supports_v3_3_nmi(void)
> +{
> + return false;
> +}
> +
> +static inline u64 gic_read_nmiar(void)
> +{
> + BUG();
> +}
I think the BUG() here is unnecessary because arm32 will not detect the
feat_nmi feature; a warning or an empty statement would be sufficient.
> +#endif
> +
> static enum gic_intid_range __get_intid_range(irq_hw_number_t hwirq)
> {
> switch (hwirq) {
> @@ -438,6 +470,8 @@ static u32 convert_offset_index(struct irq_data *d, u32 offset, u32 *index)
> return GICD_ICFGRnE;
> case GICD_IROUTER:
> return GICD_IROUTERnE;
> + case GICD_INMIR:
> + return GICD_INMIRnE;
> default:
> break;
> }
> @@ -467,6 +501,42 @@ static int gic_peek_irq(struct irq_data *d, u32 offset)
> return !!(readl_relaxed(base + offset + (index / 32) * 4) & mask);
> }
>
> +static DEFINE_RAW_SPINLOCK(irq_controller_lock);
> +
> +static void gic_irq_configure_nmi(struct irq_data *d, bool enable)
> +{
> + void __iomem *base, *addr;
> + u32 offset, index, mask, val;
> +
> + offset = convert_offset_index(d, GICD_INMIR, &index);
> + mask = 1 << (index % 32);
> +
> + if (gic_irq_in_rdist(d))
> + base = gic_data_rdist_sgi_base();
> + else
> + base = gic_dist_base_alias(d);
> +
> + addr = base + offset + (index / 32) * 4;
> +
> + raw_spin_lock(&irq_controller_lock);
> +
> + val = readl_relaxed(addr);
> + val = enable ? (val | mask) : (val & ~mask);
> + writel_relaxed(val, addr);
> +
> + raw_spin_unlock(&irq_controller_lock);
> +}
> +
> +static void gic_irq_enable_nmi(struct irq_data *d)
> +{
> + gic_irq_configure_nmi(d, true);
> +}
> +
> +static void gic_irq_disable_nmi(struct irq_data *d)
> +{
> + gic_irq_configure_nmi(d, false);
> +}
> +
> static void gic_poke_irq(struct irq_data *d, u32 offset)
> {
> void __iomem *base;
> @@ -512,7 +582,7 @@ static void gic_unmask_irq(struct irq_data *d)
> gic_poke_irq(d, GICD_ISENABLER);
> }
>
> -static inline bool gic_supports_nmi(void)
> +static inline bool gic_supports_pseudo_nmis(void)
> {
> return IS_ENABLED(CONFIG_ARM64_PSEUDO_NMI) &&
> static_branch_likely(&supports_pseudo_nmis);
> @@ -598,7 +668,7 @@ static int gic_irq_nmi_setup(struct irq_data *d)
> {
> struct irq_desc *desc = irq_to_desc(d->irq);
>
> - if (!gic_supports_nmi())
> + if (!gic_supports_pseudo_nmis() && !gic_supports_v3_3_nmi())
> return -EINVAL;
>
> if (gic_peek_irq(d, GICD_ISENABLER)) {
> @@ -617,7 +687,10 @@ static int gic_irq_nmi_setup(struct irq_data *d)
> if (!gic_irq_in_rdist(d))
> desc->handle_irq = handle_fasteoi_nmi;
>
> - gic_irq_set_prio(d, dist_prio_nmi);
> + if (gic_supports_v3_3_nmi())
> + gic_irq_enable_nmi(d);
> + else
> + gic_irq_set_prio(d, dist_prio_nmi);
>
> return 0;
> }
> @@ -626,7 +699,7 @@ static void gic_irq_nmi_teardown(struct irq_data *d)
> {
> struct irq_desc *desc = irq_to_desc(d->irq);
>
> - if (WARN_ON(!gic_supports_nmi()))
> + if (WARN_ON(!gic_supports_pseudo_nmis() && !gic_supports_v3_3_nmi()))
> return;
>
> if (gic_peek_irq(d, GICD_ISENABLER)) {
> @@ -645,7 +718,10 @@ static void gic_irq_nmi_teardown(struct irq_data *d)
> if (!gic_irq_in_rdist(d))
> desc->handle_irq = handle_fasteoi_irq;
>
> - gic_irq_set_prio(d, dist_prio_irq);
> + if (gic_supports_v3_3_nmi())
> + gic_irq_disable_nmi(d);
> + else
> + gic_irq_set_prio(d, dist_prio_irq);
> }
>
> static bool gic_arm64_erratum_2941627_needed(struct irq_data *d)
> @@ -804,7 +880,7 @@ static inline void gic_complete_ack(u32 irqnr)
>
> static bool gic_rpr_is_nmi_prio(void)
> {
> - if (!gic_supports_nmi())
> + if (!gic_supports_pseudo_nmis())
> return false;
>
> return unlikely(gic_read_rpr() == GICV3_PRIO_NMI);
> @@ -836,7 +912,8 @@ static void __gic_handle_nmi(u32 irqnr, struct pt_regs *regs)
> gic_complete_ack(irqnr);
>
> if (generic_handle_domain_nmi(gic_data.domain, irqnr)) {
> - WARN_ONCE(true, "Unexpected pseudo-NMI (irqnr %u)\n", irqnr);
> + WARN_ONCE(true, "Unexpected %sNMI (irqnr %u)\n",
> + gic_supports_pseudo_nmis() ? "pseudo-" : "", irqnr);
> gic_deactivate_unhandled(irqnr);
> }
> }
> @@ -911,7 +988,11 @@ static void __gic_handle_irq_from_irqsoff(struct pt_regs *regs)
>
> static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
> {
> - if (unlikely(gic_supports_nmi() && !interrupts_enabled(regs)))
> + if (gic_supports_v3_3_nmi() && in_nmi()) {
> + u32 irqnr = gic_read_nmiar();
> +
> + __gic_handle_nmi(irqnr, regs);
> + } else if (unlikely(gic_supports_pseudo_nmis() && !interrupts_enabled(regs)))
> __gic_handle_irq_from_irqsoff(regs);
Can we rename __gic_handle_irq_from_irqsoff to gic_handle_pseudo_nmi,
and then encapsulate the logic for obtaining the pseudo-NMI interrupt
number into a function similar to gic_read_nmiar(), such as
gic_read_pseudo_nmi_irqnr()? This would make the code look cleaner.
For example, similar to the following code:
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 2835e8f342be..1184f9446f8c 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -260,28 +260,11 @@ bool gic_supports_v3_3_nmi(void)
{
return gic_data.has_nmi && system_uses_nmi();
}
-
-static inline u64 gic_read_nmiar(void)
-{
- u64 irqstat;
-
- irqstat = read_sysreg_s(SYS_ICC_NMIAR1_EL1);
-
- dsb(sy);
-
- return irqstat;
-}
-
#else
static inline bool gic_supports_v3_3_nmi(void)
{
return false;
}
-
-static inline u64 gic_read_nmiar(void)
-{
- BUG();
-}
#endif
static enum gic_intid_range __get_intid_range(irq_hw_number_t hwirq)
@@ -961,7 +944,7 @@ static void __gic_handle_irq_from_irqson(struct
pt_regs *regs)
*
* The entry code has performed NMI entry.
*/
-static void __gic_handle_irq_from_irqsoff(struct pt_regs *regs)
+static u32 gic_read_pseudo_nmi_irqnr(struct pt_regs *regs)
{
u64 pmr;
u32 irqnr;
@@ -985,19 +968,42 @@ static void __gic_handle_irq_from_irqsoff(struct
pt_regs *regs)
irqnr = gic_read_iar();
gic_write_pmr(pmr);
+ return irqnr;
+}
+
+#ifdef CONFIG_ARM64
+static inline u64 gic_read_nmiar(void)
+{
+ u64 irqstat;
+
+ irqstat = read_sysreg_s(SYS_ICC_NMIAR1_EL1);
+
+ dsb(sy);
+
+ return irqstat;
+}
+#else
+static inline u64 gic_read_nmiar(void) { }
+#endif
+
+static void gic_handle_nmi(struct pt_regs *regs)
+{
+ u32 irqnr;
+
+ if (gic_supports_v3_3_nmi())
+ irqnr = gic_read_nmiar();
+ else if (unlikely(gic_supports_pseudo_nmis()))
+ irqnr = gic_read_pseudo_nmi_irqnr(regs);
+
__gic_handle_nmi(irqnr, regs);
}
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
- if (gic_supports_v3_3_nmi() && in_nmi()) {
- u32 irqnr = gic_read_nmiar();
+ if (in_nmi() || !interrupts_enabled(regs))
+ return gic_handle_nmi(regs);
- __gic_handle_nmi(irqnr, regs);
- } else if (unlikely(gic_supports_pseudo_nmis() &&
!interrupts_enabled(regs)))
- __gic_handle_irq_from_irqsoff(regs);
- else
- __gic_handle_irq_from_irqson(regs);
+ __gic_handle_irq_from_irqson(regs);
}
otherwise, LGTM
Reviewed-by: Jinjie Ruan <ruanjinjie@huawei.com>
> else
> __gic_handle_irq_from_irqson(regs);
> @@ -1162,7 +1243,7 @@ static void gic_cpu_sys_reg_init(void)
> /* Set priority mask register */
> if (!gic_prio_masking_enabled()) {
> write_gicreg(DEFAULT_PMR_VALUE, ICC_PMR_EL1);
> - } else if (gic_supports_nmi()) {
> + } else if (gic_supports_pseudo_nmis()) {
> /*
> * Check that all CPUs use the same priority space.
> *
> @@ -1955,15 +2036,25 @@ static const struct gic_quirk gic_quirks[] = {
> }
> };
>
> -static void gic_enable_nmi_support(void)
> +static void gic_enable_pseudo_nmis(void)
> {
> - if (!gic_prio_masking_enabled() || nmi_support_forbidden)
> - return;
> -
> pr_info("Pseudo-NMIs enabled using %s ICC_PMR_EL1 synchronisation\n",
> gic_has_relaxed_pmr_sync() ? "relaxed" : "forced");
>
> static_branch_enable(&supports_pseudo_nmis);
> +}
> +
> +static void gic_enable_nmi_support(void)
> +{
> + if ((!gic_prio_masking_enabled() || pnmi_support_forbidden) && !gic_supports_v3_3_nmi())
> + return;
> +
> + /*
> + * Initialize pseudo-NMIs only if GIC driver cannot take advantage
> + * of core (FEAT_NMI) and GIC (FEAT_GICv3_NMI) in HW
> + */
> + if (!gic_supports_v3_3_nmi())
> + gic_enable_pseudo_nmis();
>
> if (static_branch_likely(&supports_deactivate_key))
> gic_eoimode1_chip.flags |= IRQCHIP_SUPPORTS_NMI;
> @@ -2032,6 +2123,7 @@ static int __init gic_init_bases(phys_addr_t dist_phys_base,
> irq_domain_update_bus_token(gic_data.domain, DOMAIN_BUS_WIRED);
>
> gic_data.has_rss = !!(typer & GICD_TYPER_RSS);
> + gic_data.has_nmi = !!(typer & GICD_TYPER_NMI);
>
> if (typer & GICD_TYPER_MBIS) {
> err = mbi_init(handle, gic_data.domain);
> diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
> index ea5fd2374ebe..ebccd76cadda 100644
> --- a/include/linux/irqchip/arm-gic-v3.h
> +++ b/include/linux/irqchip/arm-gic-v3.h
> @@ -30,6 +30,7 @@
> #define GICD_ICFGR 0x0C00
> #define GICD_IGRPMODR 0x0D00
> #define GICD_NSACR 0x0E00
> +#define GICD_INMIR 0x0F80
> #define GICD_IGROUPRnE 0x1000
> #define GICD_ISENABLERnE 0x1200
> #define GICD_ICENABLERnE 0x1400
> @@ -39,6 +40,7 @@
> #define GICD_ICACTIVERnE 0x1C00
> #define GICD_IPRIORITYRnE 0x2000
> #define GICD_ICFGRnE 0x3000
> +#define GICD_INMIRnE 0x3B00
> #define GICD_IROUTER 0x6000
> #define GICD_IROUTERnE 0x8000
> #define GICD_IDREGS 0xFFD0
> @@ -83,6 +85,7 @@
> #define GICD_TYPER_LPIS (1U << 17)
> #define GICD_TYPER_MBIS (1U << 16)
> #define GICD_TYPER_ESPI (1U << 8)
> +#define GICD_TYPER_NMI (1U << 9)
>
> #define GICD_TYPER_ID_BITS(typer) ((((typer) >> 19) & 0x1f) + 1)
> #define GICD_TYPER_NUM_LPIS(typer) ((((typer) >> 11) & 0x1f) + 1)
> @@ -238,6 +241,7 @@
> #define GICR_ICFGR0 GICD_ICFGR
> #define GICR_IGRPMODR0 GICD_IGRPMODR
> #define GICR_NSACR GICD_NSACR
> +#define GICR_INMIR0 GICD_INMIR
>
> #define GICR_TYPER_PLPIS (1U << 0)
> #define GICR_TYPER_VLPIS (1U << 1)
^ permalink raw reply related
* Re: [PATCH v5 0/4] Some Minor fixes and cleanups to irqchip/gic-v3-its
From: Kemeng Shi @ 2026-07-21 2:25 UTC (permalink / raw)
To: Thomas Gleixner, maz, jason, lpieralisi, radu
Cc: linux-arm-kernel, linux-kernel
In-Reply-To: <8733xdmrcp.ffs@fw13>
在 2026/7/21 2:46:30, Thomas Gleixner 写道:
> On Mon, Jul 20 2026 at 15:12, Kemeng Shi wrote:
>>
>> Kemeng Shi (4):
>> irqchip/gic-v3-its: Fix memleak in its_probe_one()
>> irqchip/gic-v3-its: Fix its node leak in gic_acpi_parse_madt_its()
>
> Why are you resending already merged patches? You got an email
> notification about it, no?
Sorry for this.
I didn't see the notification before and still can't find the notification
by searching my mailbox.
I will drop these patches in next version and will check the tree before
sending a new patchset.
^ permalink raw reply
* Re: [PATCH v3 1/5] drm/bridge: Implement generic USB Type-C DP HPD bridge
From: Chaoyi Chen @ 2026-07-21 2:22 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Heikki Krogerus, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Sandy Huang, Heiko Stübner, Andy Yan,
Vinod Koul, Nicolas Frattaroli, linux-kernel, dri-devel,
linux-arm-kernel, linux-rockchip, linux-phy
In-Reply-To: <al5fYojQ0kJw2okg@venus>
Hi Sebastian,
On 7/21/2026 1:59 AM, Sebastian Reichel wrote:
> Hi,
>
> On Fri, Jul 17, 2026 at 07:19:29PM +0200, Sebastian Reichel wrote:
>> On Fri, Jul 17, 2026 at 03:23:19PM +0800, Chaoyi Chen wrote:
>>> From: Chaoyi Chen <chaoyi.chen@rock-chips.com>
>>>
>>> The HPD function of Type-C DP is implemented through
>>> drm_connector_oob_hotplug_event(). For embedded DP, it is required
>>> that the DRM connector fwnode corresponds to the Type-C port fwnode.
>>>
>>> To describe the relationship between the DP controller and the Type-C
>>> port device, we usually using drm_bridge to build a bridge chain.
>>>
>>> Now several USB-C controller drivers have already implemented the DP
>>> HPD bridge function provided by aux-hpd-bridge.c, it will build a DP
>>> HPD bridge on USB-C connector port device.
>>>
>>> But this requires the USB-C controller driver to manually register the
>>> HPD bridge. If the driver does not implement this feature, the bridge
>>> will not be create.
>>>
>>> So this patch implements a generic DP HPD bridge based on
>>> aux-hpd-bridge.c. It will monitor Type-C bus events, and when a
>>> Type-C port device containing the DP svid is registered, it will
>>> create an HPD bridge for it without the need for the USB-C controller
>>> driver to implement it.
>>>
>>> Signed-off-by: Chaoyi Chen <chaoyi.chen@rock-chips.com>
>>> Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
>>> Reviewed-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
>>> ---
>>
>> Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.com>
>> Tested-by: Sebastian Reichel <sebastian.reichel@collabora.com> # ArmSom Sige5
>>
>> I gave this a test together with the RK3588/RK3576 USB-C DP AltMode
>> patches I'm working on. As the fusb302 does a manual registration
>> for the DRM bridge in its probe function, the bridge is registered
>> twice:
>>
>> root@sige5 # cat /sys/kernel/debug/dri/bridges
>> ...
>> bridge[1]: drm_aux_hpd_bridge_funcs
>> refcount: 4
>> type: [10] DP
>> OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
>> ops: [0x4] hpd
>> bridge[2]: drm_aux_hpd_bridge_funcs
>> refcount: 2
>> type: [10] DP
>> OF: /soc/i2c@2ac50000/typec-portc@22/connector:usb-c-connector
>> ops: [0x4] hpd
>> ...
>>
>> Apparently the USB-C DP AltMode keeps working, so this just wastes
>> a few CPU cycles and some memory. So this can land and then we can
>> remove the manual code from the driver as a follow-up step. I also
>> gave that a try and things keep working. I won't send the fusb302
>> patch for now to ensure its not applied before this patch lands.
>
> The above test was done with a kernel having all config options
> built-in (i.e. no modules). Using arm64 defconfig one ends up with
>
> CONFIG_DRM_AUX_HPD_TYPEC_BRIDGE=m
>
> But the resulting 'aux-hpd-typec-dp-bridge' module is not loaded
> automatically resulting in missing bridge registration. Running
> 'modprobe aux-hpd-typec-dp-bridge' manually in the booted system
> does not work either as the TypeC controller has already been
> registered and no new BUS_NOTIFY_ADD_DEVICE is generated.
>
Oh, we indeed didn't consider that such a probe ordering issue exists.
The approach I came up with is to iterate through the &typec_bus after
registering the notifier, to ensure we don't miss any events that
occurred prior:
static int __init drm_aux_hpd_typec_dp_bridge_module_init(void)
{
bus_register_notifier(&typec_bus, &drm_typec_event_nb);
-
+ bus_for_each_dev(&typec_bus, NULL, NULL,
+ check_altmode_dev_and_register_hpd_bridge);
return 0;
}
But I'm not sure if there are potential race conditions involved.
Perhaps @Heikki and others have better ideas? Thanks.
> Greetings,
>
> -- Sebastian
--
Best,
Chaoyi
^ permalink raw reply
* [PATCH v4 2/3] clk: nuvoton: ma35d1: fix PLL_CTL1_FRAC bit field width and fractional calc
From: Joey Lu @ 2026-07-21 2:13 UTC (permalink / raw)
To: mturquette, sboyd
Cc: ychuang3, schung, yclu4, linux-arm-kernel, linux-clk,
linux-kernel, Joey Lu, Brian Masney
In-Reply-To: <20260721021356.288506-1-a0987203069@gmail.com>
PLL_CTL1_FRAC was defined as GENMASK(31, 24), covering only 8 bits.
The hardware fractional field occupies bits [31:8] (24 bits), so the
mask must be GENMASK(31, 8).
The previous fractional-mode calculation used FIELD_MAX(PLL_CTL1_FRAC)
as the denominator to obtain 2 decimal places. With the corrected 24-bit
mask the old divisor is wrong; replace the arithmetic with a proper
24-bit fixed-point rounding to 3 decimal places using the kernel's
DIV_ROUND_CLOSEST_ULL helper:
n_frac = n * 1000 + DIV_ROUND_CLOSEST_ULL(x * 1000, 1 << 24)
Fixes: 691521a367cf ("clk: nuvoton: Add clock driver for ma35d1 clock controller")
Signed-off-by: Joey Lu <a0987203069@gmail.com>
Reviewed-by: Brian Masney <bmasney@redhat.com>
---
drivers/clk/nuvoton/clk-ma35d1-pll.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
index bfedd45bd04b..eb9d69d2077b 100644
--- a/drivers/clk/nuvoton/clk-ma35d1-pll.c
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -48,7 +48,7 @@
#define PLL_CTL1_PD BIT(0)
#define PLL_CTL1_BP BIT(1)
#define PLL_CTL1_OUTDIV GENMASK(6, 4)
-#define PLL_CTL1_FRAC GENMASK(31, 24)
+#define PLL_CTL1_FRAC GENMASK(31, 8)
#define PLL_CTL2_SLOPE GENMASK(23, 0)
#define INDIV_MIN 1
@@ -113,9 +113,9 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p
pll_freq = div_u64(pll_freq, m * p);
} else {
x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
- /* 2 decimal places floating to integer (ex. 1.23 to 123) */
- n = n * 100 + ((x * 100) / FIELD_MAX(PLL_CTL1_FRAC));
- pll_freq = div_u64(parent_rate * n, 100 * m * p);
+ /* convert 24-bit fraction to 3 decimal digits, rounding to closest */
+ n = n * 1000 + DIV_ROUND_CLOSEST_ULL((u64)x * 1000, 1ULL << 24);
+ pll_freq = div_u64((u64)parent_rate * n, 1000 * m * p);
}
return pll_freq;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 1/3] clk: nuvoton: ma35d1: fix ignored div_u64 return values in PLL freq calculation
From: Joey Lu @ 2026-07-21 2:13 UTC (permalink / raw)
To: mturquette, sboyd
Cc: ychuang3, schung, yclu4, linux-arm-kernel, linux-clk,
linux-kernel, Joey Lu, Brian Masney
In-Reply-To: <20260721021356.288506-1-a0987203069@gmail.com>
div_u64() does not modify its argument in place; the return value must
be assigned. Both ma35d1_calc_smic_pll_freq() and ma35d1_calc_pll_freq()
called div_u64() and discarded the result, leaving pll_freq holding the
undivided product and thus returning a frequency orders of magnitude too
high.
Fixes: 691521a367cf ("clk: nuvoton: Add clock driver for ma35d1 clock controller")
Signed-off-by: Joey Lu <a0987203069@gmail.com>
Reviewed-by: Brian Masney <bmasney@redhat.com>
---
drivers/clk/nuvoton/clk-ma35d1-pll.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
index 4620acfe47e8..bfedd45bd04b 100644
--- a/drivers/clk/nuvoton/clk-ma35d1-pll.c
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -92,7 +92,7 @@ static unsigned long ma35d1_calc_smic_pll_freq(u32 pll0_ctl0,
p = FIELD_GET(SPLL0_CTL0_OUTDIV, pll0_ctl0);
outdiv = 1 << p;
pll_freq = (u64)parent_rate * n;
- div_u64(pll_freq, m * outdiv);
+ pll_freq = div_u64(pll_freq, m * outdiv);
return pll_freq;
}
@@ -110,7 +110,7 @@ static unsigned long ma35d1_calc_pll_freq(u8 mode, u32 *reg_ctl, unsigned long p
if (mode == PLL_MODE_INT) {
pll_freq = (u64)parent_rate * n;
- div_u64(pll_freq, m * p);
+ pll_freq = div_u64(pll_freq, m * p);
} else {
x = FIELD_GET(PLL_CTL1_FRAC, reg_ctl[1]);
/* 2 decimal places floating to integer (ex. 1.23 to 123) */
--
2.43.0
^ permalink raw reply related
* [PATCH v4 3/3] clk: nuvoton: ma35d1: fix ma35d1_clk_pll_determine_rate logic
From: Joey Lu @ 2026-07-21 2:13 UTC (permalink / raw)
To: mturquette, sboyd
Cc: ychuang3, schung, yclu4, linux-arm-kernel, linux-clk,
linux-kernel, Joey Lu
In-Reply-To: <20260721021356.288506-1-a0987203069@gmail.com>
ma35d1_clk_pll_determine_rate() called ma35d1_pll_find_closest()
unconditionally before the switch statement, and then every case
branch overwrote pll_freq by reading the current hardware registers.
For CAPLL and DDRPLL this means find_closest() ran unnecessarily
(and incorrectly, since those PLLs are read-only) and its result
was silently discarded.
Fix by moving the find_closest() call inside the APLL/EPLL/VPLL
branch where it belongs. Group CAPLL and DDRPLL together as
read-only PLLs that simply report their current rate; handle them
with an explicit if/else to keep the CAPLL (SMIC design) and DDRPLL
(standard design) paths distinct.
Fixes: 691521a367cf ("clk: nuvoton: Add clock driver for ma35d1 clock controller")
Signed-off-by: Joey Lu <a0987203069@gmail.com>
---
drivers/clk/nuvoton/clk-ma35d1-pll.c | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/drivers/clk/nuvoton/clk-ma35d1-pll.c b/drivers/clk/nuvoton/clk-ma35d1-pll.c
index eb9d69d2077b..c7c0dc91a012 100644
--- a/drivers/clk/nuvoton/clk-ma35d1-pll.c
+++ b/drivers/clk/nuvoton/clk-ma35d1-pll.c
@@ -255,32 +255,32 @@ static int ma35d1_clk_pll_determine_rate(struct clk_hw *hw,
if (req->best_parent_rate < PLL_FREF_MIN_FREQ || req->best_parent_rate > PLL_FREF_MAX_FREQ)
return -EINVAL;
- ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate,
- reg_ctl, &pll_freq);
- if (ret < 0)
- return ret;
-
switch (pll->id) {
case CAPLL:
+ case DDRPLL:
+ /* Read-only PLLs: return current rate */
reg_ctl[0] = readl_relaxed(pll->ctl0_base);
- pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate);
+ if (pll->id == CAPLL) {
+ pll_freq = ma35d1_calc_smic_pll_freq(reg_ctl[0], req->best_parent_rate);
+ } else {
+ reg_ctl[1] = readl_relaxed(pll->ctl1_base);
+ pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate);
+ }
req->rate = pll_freq;
-
return 0;
- case DDRPLL:
case APLL:
case EPLL:
case VPLL:
- reg_ctl[0] = readl_relaxed(pll->ctl0_base);
- reg_ctl[1] = readl_relaxed(pll->ctl1_base);
- pll_freq = ma35d1_calc_pll_freq(pll->mode, reg_ctl, req->best_parent_rate);
+ /* Configurable PLLs: find closest achievable rate */
+ ret = ma35d1_pll_find_closest(pll, req->rate, req->best_parent_rate,
+ reg_ctl, &pll_freq);
+ if (ret < 0)
+ return ret;
req->rate = pll_freq;
-
return 0;
}
req->rate = 0;
-
return 0;
}
--
2.43.0
^ permalink raw reply related
* [PATCH v4 0/3] clk: nuvoton: ma35d1: fix PLL frequency calculation
From: Joey Lu @ 2026-07-21 2:13 UTC (permalink / raw)
To: mturquette, sboyd
Cc: ychuang3, schung, yclu4, linux-arm-kernel, linux-clk,
linux-kernel, Joey Lu
Fix four bugs in the MA35D1 PLL clock driver that cause incorrect
frequency values returned from recalc_rate() and determine_rate().
v1 combined all fixes into a single commit. At reviewer request,
split into one patch per logical fix:
1/3 - fix div_u64 return value being discarded (affects both
ma35d1_calc_smic_pll_freq and ma35d1_calc_pll_freq INT mode)
2/3 - fix PLL_CTL1_FRAC mask width (8-bit -> 24-bit) and update
the fractional-mode arithmetic accordingly
3/3 - fix ma35d1_clk_pll_determine_rate: move find_closest() into
the configurable-PLL branch; unify read-only PLL handling
Changes in v4 (vs v3):
- 2/3: add Reviewed-by tag
Changes in v3 (vs v2):
- 2/3: replace the manual round-to-nearest expression
"(u32)(((u64)x * 1000 + 500) >> 24)" with the kernel helper
DIV_ROUND_CLOSEST_ULL((u64)x * 1000, 1ULL << 24); the result
is mathematically identical but more readable and idiomatic
Joey Lu (3):
clk: nuvoton: ma35d1: fix ignored div_u64 return values in PLL freq
calculation
clk: nuvoton: ma35d1: fix PLL_CTL1_FRAC bit field width and fractional
calc
clk: nuvoton: ma35d1: fix ma35d1_clk_pll_determine_rate logic
drivers/clk/nuvoton/clk-ma35d1-pll.c | 38 ++++++++++++++--------------
1 file changed, 19 insertions(+), 19 deletions(-)
--
2.43.0
^ permalink raw reply
* RE: [PATCH RESEND v4 net-next 13/14] net: enetc: use alloc_etherdev_mqs() to create netdev for VF driver
From: Wei Fang (OSS) @ 2026-07-21 2:01 UTC (permalink / raw)
To: Joe Damato, Wei Fang (OSS)
Cc: Claudiu Manoil, Vladimir Oltean, Clark Wang,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com, linux@armlinux.org.uk,
Wei Fang, chleroy@kernel.org, maxime.chevallier@bootlin.com,
imx@lists.linux.dev, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, linuxppc-dev@lists.ozlabs.org,
linux-arm-kernel@lists.infradead.org
In-Reply-To: <al5cgs6CqVKsy/gh@devvm20253.cco0.facebook.com>
> On Mon, Jul 20, 2026 at 09:43:15AM +0800, wei.fang@oss.nxp.com wrote:
> > From: Wei Fang <wei.fang@nxp.com>
> >
> > The VF driver uses alloc_etherdev_mq() with ENETC_MAX_NUM_TXQS as the
> > queue count, which forces the TX and RX queue counts to be equal and
> > uses a compile-time constant rather than the actual hardware capability.
> >
> > After enetc_get_si_caps() is called, si->num_tx_rings and
> > si->num_rx_rings reflect the actual number of rings assigned to the VF
> > by the PF. For the ENETC VF on LS1028A and the upcoming i.MX95/94, their
> > SoCs have no more than 6 CPUs, and the number of TX/RX rings allocated
> > to the VF is less than 8.
> >
> > Therefore, switch to alloc_etherdev_mqs() so that the TX and RX queue
> > counts are set independently, each capped at ENETC_MAX_NUM_TXQS, based
> > on the actual number of rings assigned to the VF by the PF.
> >
> > Note that if future SoCs have more than 6 CPUs and more than 6 RX rings
> > allocated to VFs, the size of the int_vector array in struct
> > enetc_ndev_priv will need to be modified. Similarly, if more than 8 TX
> > rings are allocated to each int_vector, ENETC_MAX_NUM_TXQS will also
> > need to be modified.
> >
> > Signed-off-by: Wei Fang <wei.fang@nxp.com>
> > ---
> > drivers/net/ethernet/freescale/enetc/enetc_vf.c | 9 ++++++++-
> > 1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> > index 9cdb0a4d6baf..7dcb4a0246f5 100644
> > --- a/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> > +++ b/drivers/net/ethernet/freescale/enetc/enetc_vf.c
> > @@ -317,7 +317,14 @@ static int enetc_vf_probe(struct pci_dev *pdev,
> >
> > enetc_get_si_caps(si);
> >
> > - ndev = alloc_etherdev_mq(sizeof(*priv), ENETC_MAX_NUM_TXQS);
> > + /* Currently, the supported SoCs have a max of 6 CPUs and the VFs
> > + * have less than 6 RX/TX rings. So no issues for these supported
> > + * SoCs, but for future SoCs which have more CPUs or more TX/RX
> > + * rings, all the related logic needs to be improved.
> > + */
> > + ndev = alloc_etherdev_mqs(sizeof(*priv),
> > + min(si->num_tx_rings,
> ENETC_MAX_NUM_TXQS),
> > + min(si->num_rx_rings,
> ENETC_MAX_NUM_TXQS));
>
> Code looks right, but looks almost like a typo. I guess it would read nicer if
> ENETC_MAX_NUM_RXQS existed?
Yes, ENETC_MAX_NUM_RXQS is clearer. However, ENETC_MAX_NUM_RXQS
does not exist; in fact, their values are equal, and we plan to remove
ENETC_MAX_NUM_TXQS later, so a new macro ENETC_MAX_NUM_RXQS
was not added in this patch.
>
> That said:
>
> Reviewed-by: Joe Damato <joe@dama.to>
^ permalink raw reply
* Re: [PATCH v2] iommu/rockchip: Drop global rk_ops in favor of per-device ops
From: Simon Xue @ 2026-07-21 1:52 UTC (permalink / raw)
To: Joerg Roedel
Cc: iommu, linux-arm-kernel, linux-rockchip, linux-kernel,
Will Deacon, Robin Murphy, Heiko Stuebner, Shawn Lin
In-Reply-To: <b046ad1c-9cfc-4399-a8df-e4fffff434f9@rock-chips.com>
Hi Joerg, Will, Robin, Heiko,
Second gentle ping on this patch series sent on March 10, 2026:
[PATCH v2] iommu/rockchip: Drop global rk_ops in favor of per-device ops
https://lore.kernel.org/all/20260310105303.128859-1-xxm@rock-chips.com/
This patch removes the global rk_ops and switches to per-device ops to
support SoCs with different IOMMU versions.
Could you please take a look when you have time?
If there are concerns or changes needed, I'd be happy to send a new version.
Thanks for your time and review.
Best regards,
Simon
在 2026/4/1 15:59, Simon Xue 写道:
> Hi all,
>
> A gentle ping on this patch.
>
> 在 2026/3/13 17:32, Shawn Lin 写道:
>> 在 2026/03/10 星期二 18:53, Simon Xue 写道:
>>> The driver currently uses a global rk_ops pointer, forcing all IOMMU
>>> instances to share the same operations. This restricts the driver from
>>> supporting SoCs that might integrate different versions of IOMMU
>>> hardware.
>>>
>>> Since the IOMMU framework passes the master device information to
>>> iommu_paging_domain_alloc(), the global variable is no longer needed.
>>>
>>> Fix this by moving rk_ops into struct rk_iommu and struct
>>> rk_iommu_domain.
>>> Initialize it per-device during probe via of_device_get_match_data(),
>>> and replace all global references with the instance-specific pointers.
>>>
>>
>> Thanks for the patch, Simon. I've tested it on the RK3576 EVB1 with
>> PCIe1 + IOMMU. NVMe works fine on it, and I also verified the IOVA
>> allocated in the NVMe driver, they look correct as I manually limited
>> the memblock to under 2GB, so here it is:
>>
>> nvme 0001:21:00.0: cq_dma_addr: 0x00000000f7fc7000
>>
>> Tested-by: Shawn Lin <shawn.lin@rock-chips.com>
>> Reviewed-by: Shawn Lin <shawn.lin@rock-chips.com>
>>
>>> Signed-off-by: Simon Xue <xxm@rock-chips.com>
>>> ---
>>> v2:
>>> - Remove the one-time-used 'ops' variable in rk_iommu_probe()
>>>
>>> drivers/iommu/rockchip-iommu.c | 71
>>> ++++++++++++++++------------------
>>> 1 file changed, 33 insertions(+), 38 deletions(-)
>>>
>>> diff --git a/drivers/iommu/rockchip-iommu.c
>>> b/drivers/iommu/rockchip-iommu.c
>>> index 0013cf196c57..4da80136933c 100644
>>> --- a/drivers/iommu/rockchip-iommu.c
>>> +++ b/drivers/iommu/rockchip-iommu.c
>>> @@ -82,6 +82,14 @@
>>> */
>>> #define RK_IOMMU_PGSIZE_BITMAP 0x007ff000
>>> +struct rk_iommu_ops {
>>> + phys_addr_t (*pt_address)(u32 dte);
>>> + u32 (*mk_dtentries)(dma_addr_t pt_dma);
>>> + u32 (*mk_ptentries)(phys_addr_t page, int prot);
>>> + u64 dma_bit_mask;
>>> + gfp_t gfp_flags;
>>> +};
>>> +
>>> struct rk_iommu_domain {
>>> struct list_head iommus;
>>> u32 *dt; /* page directory table */
>>> @@ -89,6 +97,7 @@ struct rk_iommu_domain {
>>> spinlock_t iommus_lock; /* lock for iommus list */
>>> spinlock_t dt_lock; /* lock for modifying page directory table */
>>> struct device *dma_dev;
>>> + const struct rk_iommu_ops *rk_ops;
>>> struct iommu_domain domain;
>>> };
>>> @@ -98,14 +107,6 @@ static const char * const rk_iommu_clocks[] = {
>>> "aclk", "iface",
>>> };
>>> -struct rk_iommu_ops {
>>> - phys_addr_t (*pt_address)(u32 dte);
>>> - u32 (*mk_dtentries)(dma_addr_t pt_dma);
>>> - u32 (*mk_ptentries)(phys_addr_t page, int prot);
>>> - u64 dma_bit_mask;
>>> - gfp_t gfp_flags;
>>> -};
>>> -
>>> struct rk_iommu {
>>> struct device *dev;
>>> void __iomem **bases;
>>> @@ -117,6 +118,7 @@ struct rk_iommu {
>>> struct iommu_device iommu;
>>> struct list_head node; /* entry in rk_iommu_domain.iommus */
>>> struct iommu_domain *domain; /* domain to which iommu is
>>> attached */
>>> + const struct rk_iommu_ops *rk_ops;
>>> };
>>> struct rk_iommudata {
>>> @@ -124,7 +126,6 @@ struct rk_iommudata {
>>> struct rk_iommu *iommu;
>>> };
>>> -static const struct rk_iommu_ops *rk_ops;
>>> static struct iommu_domain rk_identity_domain;
>>> static inline void rk_table_flush(struct rk_iommu_domain *dom,
>>> dma_addr_t dma,
>>> @@ -510,7 +511,7 @@ static int rk_iommu_force_reset(struct rk_iommu
>>> *iommu)
>>> * and verifying that upper 5 (v1) or 7 (v2) nybbles are read
>>> back.
>>> */
>>> for (i = 0; i < iommu->num_mmu; i++) {
>>> - dte_addr = rk_ops->pt_address(DTE_ADDR_DUMMY);
>>> + dte_addr = iommu->rk_ops->pt_address(DTE_ADDR_DUMMY);
>>> rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, dte_addr);
>>> if (dte_addr != rk_iommu_read(iommu->bases[i],
>>> RK_MMU_DTE_ADDR)) {
>>> @@ -551,7 +552,7 @@ static void log_iova(struct rk_iommu *iommu, int
>>> index, dma_addr_t iova)
>>> page_offset = rk_iova_page_offset(iova);
>>> mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
>>> - mmu_dte_addr_phys = rk_ops->pt_address(mmu_dte_addr);
>>> + mmu_dte_addr_phys = iommu->rk_ops->pt_address(mmu_dte_addr);
>>> dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
>>> dte_addr = phys_to_virt(dte_addr_phys);
>>> @@ -560,14 +561,14 @@ static void log_iova(struct rk_iommu *iommu,
>>> int index, dma_addr_t iova)
>>> if (!rk_dte_is_pt_valid(dte))
>>> goto print_it;
>>> - pte_addr_phys = rk_ops->pt_address(dte) + (pte_index * 4);
>>> + pte_addr_phys = iommu->rk_ops->pt_address(dte) + (pte_index * 4);
>>> pte_addr = phys_to_virt(pte_addr_phys);
>>> pte = *pte_addr;
>>> if (!rk_pte_is_page_valid(pte))
>>> goto print_it;
>>> - page_addr_phys = rk_ops->pt_address(pte) + page_offset;
>>> + page_addr_phys = iommu->rk_ops->pt_address(pte) + page_offset;
>>> page_flags = pte & RK_PTE_PAGE_FLAGS_MASK;
>>> print_it:
>>> @@ -663,13 +664,13 @@ static phys_addr_t
>>> rk_iommu_iova_to_phys(struct iommu_domain *domain,
>>> if (!rk_dte_is_pt_valid(dte))
>>> goto out;
>>> - pt_phys = rk_ops->pt_address(dte);
>>> + pt_phys = rk_domain->rk_ops->pt_address(dte);
>>> page_table = (u32 *)phys_to_virt(pt_phys);
>>> pte = page_table[rk_iova_pte_index(iova)];
>>> if (!rk_pte_is_page_valid(pte))
>>> goto out;
>>> - phys = rk_ops->pt_address(pte) + rk_iova_page_offset(iova);
>>> + phys = rk_domain->rk_ops->pt_address(pte) +
>>> rk_iova_page_offset(iova);
>>> out:
>>> spin_unlock_irqrestore(&rk_domain->dt_lock, flags);
>>> @@ -730,7 +731,7 @@ static u32 *rk_dte_get_page_table(struct
>>> rk_iommu_domain *rk_domain,
>>> if (rk_dte_is_pt_valid(dte))
>>> goto done;
>>> - page_table = iommu_alloc_pages_sz(GFP_ATOMIC |
>>> rk_ops->gfp_flags,
>>> + page_table = iommu_alloc_pages_sz(GFP_ATOMIC |
>>> rk_domain->rk_ops->gfp_flags,
>>> SPAGE_SIZE);
>>> if (!page_table)
>>> return ERR_PTR(-ENOMEM);
>>> @@ -742,13 +743,13 @@ static u32 *rk_dte_get_page_table(struct
>>> rk_iommu_domain *rk_domain,
>>> return ERR_PTR(-ENOMEM);
>>> }
>>> - dte = rk_ops->mk_dtentries(pt_dma);
>>> + dte = rk_domain->rk_ops->mk_dtentries(pt_dma);
>>> *dte_addr = dte;
>>> rk_table_flush(rk_domain,
>>> rk_domain->dt_dma + dte_index * sizeof(u32), 1);
>>> done:
>>> - pt_phys = rk_ops->pt_address(dte);
>>> + pt_phys = rk_domain->rk_ops->pt_address(dte);
>>> return (u32 *)phys_to_virt(pt_phys);
>>> }
>>> @@ -790,7 +791,7 @@ static int rk_iommu_map_iova(struct
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>> if (rk_pte_is_page_valid(pte))
>>> goto unwind;
>>> - pte_addr[pte_count] = rk_ops->mk_ptentries(paddr, prot);
>>> + pte_addr[pte_count] =
>>> rk_domain->rk_ops->mk_ptentries(paddr, prot);
>>> paddr += SPAGE_SIZE;
>>> }
>>> @@ -812,7 +813,7 @@ static int rk_iommu_map_iova(struct
>>> rk_iommu_domain *rk_domain, u32 *pte_addr,
>>> pte_count * SPAGE_SIZE);
>>> iova += pte_count * SPAGE_SIZE;
>>> - page_phys = rk_ops->pt_address(pte_addr[pte_count]);
>>> + page_phys = rk_domain->rk_ops->pt_address(pte_addr[pte_count]);
>>> pr_err("iova: %pad already mapped to %pa cannot remap to phys:
>>> %pa prot: %#x\n",
>>> &iova, &page_phys, &paddr, prot);
>>> @@ -849,7 +850,7 @@ static int rk_iommu_map(struct iommu_domain
>>> *domain, unsigned long _iova,
>>> pte_index = rk_iova_pte_index(iova);
>>> pte_addr = &page_table[pte_index];
>>> - pte_dma = rk_ops->pt_address(dte_index) + pte_index *
>>> sizeof(u32);
>>> + pte_dma = rk_domain->rk_ops->pt_address(dte_index) + pte_index
>>> * sizeof(u32);
>>> ret = rk_iommu_map_iova(rk_domain, pte_addr, pte_dma, iova,
>>> paddr, size, prot);
>>> @@ -887,7 +888,7 @@ static size_t rk_iommu_unmap(struct
>>> iommu_domain *domain, unsigned long _iova,
>>> return 0;
>>> }
>>> - pt_phys = rk_ops->pt_address(dte);
>>> + pt_phys = rk_domain->rk_ops->pt_address(dte);
>>> pte_addr = (u32 *)phys_to_virt(pt_phys) +
>>> rk_iova_pte_index(iova);
>>> pte_dma = pt_phys + rk_iova_pte_index(iova) * sizeof(u32);
>>> unmap_size = rk_iommu_unmap_iova(rk_domain, pte_addr, pte_dma,
>>> size);
>>> @@ -945,7 +946,7 @@ static int rk_iommu_enable(struct rk_iommu *iommu)
>>> for (i = 0; i < iommu->num_mmu; i++) {
>>> rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR,
>>> - rk_ops->mk_dtentries(rk_domain->dt_dma));
>>> + iommu->rk_ops->mk_dtentries(rk_domain->dt_dma));
>>> rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
>>> rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK,
>>> RK_MMU_IRQ_MASK);
>>> }
>>> @@ -1068,17 +1069,19 @@ static struct iommu_domain
>>> *rk_iommu_domain_alloc_paging(struct device *dev)
>>> if (!rk_domain)
>>> return NULL;
>>> + iommu = rk_iommu_from_dev(dev);
>>> + rk_domain->rk_ops = iommu->rk_ops;
>>> +
>>> /*
>>> * rk32xx iommus use a 2 level pagetable.
>>> * Each level1 (dt) and level2 (pt) table has 1024 4-byte
>>> entries.
>>> * Allocate one 4 KiB page for each table.
>>> */
>>> - rk_domain->dt = iommu_alloc_pages_sz(GFP_KERNEL |
>>> rk_ops->gfp_flags,
>>> + rk_domain->dt = iommu_alloc_pages_sz(GFP_KERNEL |
>>> rk_domain->rk_ops->gfp_flags,
>>> SPAGE_SIZE);
>>> if (!rk_domain->dt)
>>> goto err_free_domain;
>>> - iommu = rk_iommu_from_dev(dev);
>>> rk_domain->dma_dev = iommu->dev;
>>> rk_domain->dt_dma = dma_map_single(rk_domain->dma_dev,
>>> rk_domain->dt,
>>> SPAGE_SIZE, DMA_TO_DEVICE);
>>> @@ -1117,7 +1120,7 @@ static void rk_iommu_domain_free(struct
>>> iommu_domain *domain)
>>> for (i = 0; i < NUM_DT_ENTRIES; i++) {
>>> u32 dte = rk_domain->dt[i];
>>> if (rk_dte_is_pt_valid(dte)) {
>>> - phys_addr_t pt_phys = rk_ops->pt_address(dte);
>>> + phys_addr_t pt_phys = rk_domain->rk_ops->pt_address(dte);
>>> u32 *page_table = phys_to_virt(pt_phys);
>>> dma_unmap_single(rk_domain->dma_dev, pt_phys,
>>> SPAGE_SIZE, DMA_TO_DEVICE);
>>> @@ -1197,7 +1200,6 @@ static int rk_iommu_probe(struct
>>> platform_device *pdev)
>>> struct device *dev = &pdev->dev;
>>> struct rk_iommu *iommu;
>>> struct resource *res;
>>> - const struct rk_iommu_ops *ops;
>>> int num_res = pdev->num_resources;
>>> int err, i;
>>> @@ -1211,16 +1213,9 @@ static int rk_iommu_probe(struct
>>> platform_device *pdev)
>>> iommu->dev = dev;
>>> iommu->num_mmu = 0;
>>> - ops = of_device_get_match_data(dev);
>>> - if (!rk_ops)
>>> - rk_ops = ops;
>>> -
>>> - /*
>>> - * That should not happen unless different versions of the
>>> - * hardware block are embedded the same SoC
>>> - */
>>> - if (WARN_ON(rk_ops != ops))
>>> - return -EINVAL;
>>> + iommu->rk_ops = of_device_get_match_data(dev);
>>> + if (!iommu->rk_ops)
>>> + return -ENOENT;
>>> iommu->bases = devm_kcalloc(dev, num_res,
>>> sizeof(*iommu->bases),
>>> GFP_KERNEL);
>>> @@ -1286,7 +1281,7 @@ static int rk_iommu_probe(struct
>>> platform_device *pdev)
>>> goto err_pm_disable;
>>> }
>>> - dma_set_mask_and_coherent(dev, rk_ops->dma_bit_mask);
>>> + dma_set_mask_and_coherent(dev, iommu->rk_ops->dma_bit_mask);
>>> err = iommu_device_sysfs_add(&iommu->iommu, dev, NULL,
>>> dev_name(dev));
>>> if (err)
>>>
>>
^ permalink raw reply
* [PATCH 2/2] ASoC: meson: meson-card: use priv instead of card on each functions
From: Kuninori Morimoto @ 2026-07-21 1:36 UTC (permalink / raw)
To: Jaroslav Kysela, Jerome Brunet, Kevin Hilman, Liam Girdwood,
Mark Brown, Martin Blumenstingl, Neil Armstrong, Takashi Iwai
Cc: linux-arm-kernel, linux-sound
In-Reply-To: <87v7a9i0o8.wl-kuninori.morimoto.gx@renesas.com>
We can get card via priv->card, use priv for function param.
No functional change, but is preparation for Card capsuling.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
sound/soc/meson/axg-card.c | 63 ++++++++++++++++--------------
sound/soc/meson/gx-card.c | 23 +++++------
sound/soc/meson/meson-card-utils.c | 58 ++++++++++++++-------------
sound/soc/meson/meson-card.h | 11 +++---
4 files changed, 82 insertions(+), 73 deletions(-)
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
index b4dca80e15e40..3d345dc33cb6f 100644
--- a/sound/soc/meson/axg-card.c
+++ b/sound/soc/meson/axg-card.c
@@ -100,28 +100,28 @@ static int axg_card_tdm_dai_lb_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
+static int axg_card_add_tdm_loopback(struct meson_card *priv,
int *index)
{
- struct meson_card *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_dai_link *pad;
struct snd_soc_dai_link *lb;
struct snd_soc_dai_link_component *dlc;
+ struct device *dev = priv->card.dev;
int ret;
/* extend links */
- ret = meson_card_reallocate_links(card, card->num_links + 1);
+ ret = meson_card_reallocate_links(priv, priv->card.num_links + 1);
if (ret)
return ret;
- pad = &card->dai_link[*index];
- lb = &card->dai_link[*index + 1];
+ pad = &priv->card.dai_link[*index];
+ lb = &priv->card.dai_link[*index + 1];
- lb->name = devm_kasprintf(card->dev, GFP_KERNEL, "%s-lb", pad->name);
+ lb->name = devm_kasprintf(dev, GFP_KERNEL, "%s-lb", pad->name);
if (!lb->name)
return -ENOMEM;
- dlc = devm_kzalloc(card->dev, sizeof(*dlc), GFP_KERNEL);
+ dlc = devm_kzalloc(dev, sizeof(*dlc), GFP_KERNEL);
if (!dlc)
return -ENOMEM;
@@ -153,18 +153,19 @@ static int axg_card_add_tdm_loopback(struct snd_soc_card *card,
return 0;
}
-static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
+static int axg_card_parse_cpu_tdm_slots(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node,
struct axg_dai_link_tdm_data *be)
{
+ struct device *dev = priv->card.dev;
char propname[32];
u32 tx, rx;
int i;
- be->tx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
+ be->tx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES,
sizeof(*be->tx_mask), GFP_KERNEL);
- be->rx_mask = devm_kcalloc(card->dev, AXG_TDM_NUM_LANES,
+ be->rx_mask = devm_kcalloc(dev, AXG_TDM_NUM_LANES,
sizeof(*be->rx_mask), GFP_KERNEL);
if (!be->tx_mask || !be->rx_mask)
return -ENOMEM;
@@ -191,7 +192,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
/* ... but the interface should at least have one direction */
if (!tx && !rx) {
- dev_err(card->dev, "tdm link has no cpu slots\n");
+ dev_err(dev, "tdm link has no cpu slots\n");
return -EINVAL;
}
@@ -207,7 +208,7 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
* Error if the slots can't accommodate the largest mask or
* if it is just too big
*/
- dev_err(card->dev, "bad slot number\n");
+ dev_err(dev, "bad slot number\n");
return -EINVAL;
}
@@ -216,14 +217,15 @@ static int axg_card_parse_cpu_tdm_slots(struct snd_soc_card *card,
return 0;
}
-static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
+static int axg_card_parse_codecs_masks(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node,
struct axg_dai_link_tdm_data *be)
{
struct axg_dai_link_tdm_mask *codec_mask;
+ struct device *dev = priv->card.dev;
- codec_mask = devm_kcalloc(card->dev, link->num_codecs,
+ codec_mask = devm_kcalloc(dev, link->num_codecs,
sizeof(*codec_mask), GFP_KERNEL);
if (!codec_mask)
return -ENOMEM;
@@ -242,17 +244,17 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
return 0;
}
-static int axg_card_parse_tdm(struct snd_soc_card *card,
+static int axg_card_parse_tdm(struct meson_card *priv,
struct device_node *node,
int *index)
{
- struct meson_card *priv = snd_soc_card_get_drvdata(card);
- struct snd_soc_dai_link *link = &card->dai_link[*index];
+ struct snd_soc_dai_link *link = &priv->card.dai_link[*index];
struct axg_dai_link_tdm_data *be;
+ struct device *dev = priv->card.dev;
int ret;
/* Allocate tdm link parameters */
- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
+ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL);
if (!be)
return -ENOMEM;
priv->link_data[*index] = be;
@@ -264,19 +266,19 @@ static int axg_card_parse_tdm(struct snd_soc_card *card,
of_property_read_u32(node, "mclk-fs", &be->mclk_fs);
- ret = axg_card_parse_cpu_tdm_slots(card, link, node, be);
+ ret = axg_card_parse_cpu_tdm_slots(priv, link, node, be);
if (ret) {
- dev_err(card->dev, "error parsing tdm link slots\n");
+ dev_err(dev, "error parsing tdm link slots\n");
return ret;
}
- ret = axg_card_parse_codecs_masks(card, link, node, be);
+ ret = axg_card_parse_codecs_masks(priv, link, node, be);
if (ret)
return ret;
/* Add loopback if the pad dai has playback */
if (!link->capture_only) {
- ret = axg_card_add_tdm_loopback(card, index);
+ ret = axg_card_add_tdm_loopback(priv, index);
if (ret)
return ret;
}
@@ -305,14 +307,15 @@ static int axg_card_cpu_is_codec(struct device_node *np)
of_device_is_compatible(np, DT_PREFIX "g12a-toacodec");
}
-static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
+static int axg_card_add_link(struct meson_card *priv, struct device_node *np,
int *index)
{
- struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
+ struct snd_soc_dai_link *dai_link = &priv->card.dai_link[*index];
struct snd_soc_dai_link_component *cpu;
+ struct device *dev = priv->card.dev;
int ret;
- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
+ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL);
if (!cpu)
return -ENOMEM;
@@ -320,17 +323,17 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->num_cpus = 1;
dai_link->nonatomic = true;
- ret = meson_card_parse_dai(card, np, dai_link->cpus);
+ ret = meson_card_parse_dai(priv, np, dai_link->cpus);
if (ret)
return ret;
if (axg_card_cpu_is_playback_fe(dai_link->cpus->of_node))
- return meson_card_set_fe_link(card, dai_link, np, true);
+ return meson_card_set_fe_link(priv, dai_link, np, true);
else if (axg_card_cpu_is_capture_fe(dai_link->cpus->of_node))
- return meson_card_set_fe_link(card, dai_link, np, false);
+ return meson_card_set_fe_link(priv, dai_link, np, false);
- ret = meson_card_set_be_link(card, dai_link, np);
+ ret = meson_card_set_be_link(priv, dai_link, np);
if (ret)
return ret;
@@ -340,7 +343,7 @@ static int axg_card_add_link(struct snd_soc_card *card, struct device_node *np,
} else {
dai_link->no_pcm = 1;
if (axg_card_cpu_is_tdm_iface(dai_link->cpus->of_node))
- ret = axg_card_parse_tdm(card, np, index);
+ ret = axg_card_parse_tdm(priv, np, index);
}
return ret;
diff --git a/sound/soc/meson/gx-card.c b/sound/soc/meson/gx-card.c
index b408cc2bbc919..c0cbf31f28db5 100644
--- a/sound/soc/meson/gx-card.c
+++ b/sound/soc/meson/gx-card.c
@@ -41,16 +41,16 @@ static const struct snd_soc_ops gx_card_i2s_be_ops = {
.hw_params = gx_card_i2s_be_hw_params,
};
-static int gx_card_parse_i2s(struct snd_soc_card *card,
+static int gx_card_parse_i2s(struct meson_card *priv,
struct device_node *node,
int *index)
{
- struct meson_card *priv = snd_soc_card_get_drvdata(card);
- struct snd_soc_dai_link *link = &card->dai_link[*index];
+ struct snd_soc_dai_link *link = &priv->card.dai_link[*index];
struct gx_dai_link_i2s_data *be;
+ struct device *dev = priv->card.dev;
/* Allocate i2s link parameters */
- be = devm_kzalloc(card->dev, sizeof(*be), GFP_KERNEL);
+ be = devm_kzalloc(dev, sizeof(*be), GFP_KERNEL);
if (!be)
return -ENOMEM;
priv->link_data[*index] = be;
@@ -76,28 +76,29 @@ static int gx_card_cpu_identify(struct snd_soc_dai_link_component *c,
return 0;
}
-static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
+static int gx_card_add_link(struct meson_card *priv, struct device_node *np,
int *index)
{
- struct snd_soc_dai_link *dai_link = &card->dai_link[*index];
+ struct snd_soc_dai_link *dai_link = &priv->card.dai_link[*index];
struct snd_soc_dai_link_component *cpu;
+ struct device *dev = priv->card.dev;
int ret;
- cpu = devm_kzalloc(card->dev, sizeof(*cpu), GFP_KERNEL);
+ cpu = devm_kzalloc(dev, sizeof(*cpu), GFP_KERNEL);
if (!cpu)
return -ENOMEM;
dai_link->cpus = cpu;
dai_link->num_cpus = 1;
- ret = meson_card_parse_dai(card, np, dai_link->cpus);
+ ret = meson_card_parse_dai(priv, np, dai_link->cpus);
if (ret)
return ret;
if (gx_card_cpu_identify(dai_link->cpus, "FIFO"))
- return meson_card_set_fe_link(card, dai_link, np, true);
+ return meson_card_set_fe_link(priv, dai_link, np, true);
- ret = meson_card_set_be_link(card, dai_link, np);
+ ret = meson_card_set_be_link(priv, dai_link, np);
if (ret)
return ret;
@@ -109,7 +110,7 @@ static int gx_card_add_link(struct snd_soc_card *card, struct device_node *np,
dai_link->no_pcm = 1;
/* Check if the cpu is the i2s encoder and parse i2s data */
if (gx_card_cpu_identify(dai_link->cpus, "I2S Encoder"))
- ret = gx_card_parse_i2s(card, np, index);
+ ret = gx_card_parse_i2s(priv, np, index);
}
return ret;
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index d144ab79b645b..7329711aefa56 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -39,10 +39,9 @@ int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
}
EXPORT_SYMBOL_GPL(meson_card_i2s_set_sysclk);
-int meson_card_reallocate_links(struct snd_soc_card *card,
+int meson_card_reallocate_links(struct meson_card *priv,
unsigned int num_links)
{
- struct meson_card *priv = snd_soc_card_get_drvdata(card);
struct snd_soc_dai_link *links;
void **ldata;
@@ -67,10 +66,11 @@ int meson_card_reallocate_links(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(meson_card_reallocate_links);
-int meson_card_parse_dai(struct snd_soc_card *card,
+int meson_card_parse_dai(struct meson_card *priv,
struct device_node *node,
struct snd_soc_dai_link_component *dlc)
{
+ struct device *dev = priv->card.dev;
int ret;
if (!dlc || !node)
@@ -78,18 +78,19 @@ int meson_card_parse_dai(struct snd_soc_card *card,
ret = snd_soc_of_get_dlc(node, NULL, dlc, 0);
if (ret)
- return dev_err_probe(card->dev, ret, "can't parse dai\n");
+ return dev_err_probe(dev, ret, "can't parse dai\n");
return ret;
}
EXPORT_SYMBOL_GPL(meson_card_parse_dai);
-static int meson_card_set_link_name(struct snd_soc_card *card,
+static int meson_card_set_link_name(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node,
const char *prefix)
{
- char *name = devm_kasprintf(card->dev, GFP_KERNEL, "%s.%s",
+ struct device *dev = priv->card.dev;
+ char *name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s",
prefix, node->full_name);
if (!name)
return -ENOMEM;
@@ -127,21 +128,22 @@ unsigned int meson_card_parse_daifmt(struct device_node *node,
}
EXPORT_SYMBOL_GPL(meson_card_parse_daifmt);
-int meson_card_set_be_link(struct snd_soc_card *card,
+int meson_card_set_be_link(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node)
{
struct snd_soc_dai_link_component *codec;
+ struct device *dev = priv->card.dev;
int ret, num_codecs;
num_codecs = of_get_child_count(node);
if (!num_codecs) {
- dev_err(card->dev, "be link %s has no codec\n",
+ dev_err(dev, "be link %s has no codec\n",
node->full_name);
return -EINVAL;
}
- codec = devm_kcalloc(card->dev, num_codecs, sizeof(*codec), GFP_KERNEL);
+ codec = devm_kcalloc(dev, num_codecs, sizeof(*codec), GFP_KERNEL);
if (!codec)
return -ENOMEM;
@@ -149,22 +151,22 @@ int meson_card_set_be_link(struct snd_soc_card *card,
link->num_codecs = num_codecs;
for_each_child_of_node_scoped(node, np) {
- ret = meson_card_parse_dai(card, np, codec);
+ ret = meson_card_parse_dai(priv, np, codec);
if (ret)
return ret;
codec++;
}
- ret = meson_card_set_link_name(card, link, node, "be");
+ ret = meson_card_set_link_name(priv, link, node, "be");
if (ret)
- dev_err(card->dev, "error setting %pOFn link name\n", node);
+ dev_err(dev, "error setting %pOFn link name\n", node);
return ret;
}
EXPORT_SYMBOL_GPL(meson_card_set_be_link);
-int meson_card_set_fe_link(struct snd_soc_card *card,
+int meson_card_set_fe_link(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node,
bool is_playback)
@@ -182,29 +184,29 @@ int meson_card_set_fe_link(struct snd_soc_card *card,
else
link->capture_only = 1;
- return meson_card_set_link_name(card, link, node, "fe");
+ return meson_card_set_link_name(priv, link, node, "fe");
}
EXPORT_SYMBOL_GPL(meson_card_set_fe_link);
-static int meson_card_add_links(struct snd_soc_card *card)
+static int meson_card_add_links(struct meson_card *priv)
{
- struct meson_card *priv = snd_soc_card_get_drvdata(card);
- struct device_node *node = card->dev->of_node;
+ struct device *dev = priv->card.dev;
+ struct device_node *node = dev->of_node;
int num, i, ret;
num = of_get_child_count(node);
if (!num) {
- dev_err(card->dev, "card has no links\n");
+ dev_err(dev, "card has no links\n");
return -EINVAL;
}
- ret = meson_card_reallocate_links(card, num);
+ ret = meson_card_reallocate_links(priv, num);
if (ret)
return ret;
i = 0;
for_each_child_of_node_scoped(node, np) {
- ret = priv->match_data->add_link(card, np, &i);
+ ret = priv->match_data->add_link(priv, np, &i);
if (ret)
return ret;
@@ -214,17 +216,19 @@ static int meson_card_add_links(struct snd_soc_card *card)
return 0;
}
-static int meson_card_parse_of_optional(struct snd_soc_card *card,
+static int meson_card_parse_of_optional(struct meson_card *priv,
const char *propname,
int (*func)(struct snd_soc_card *c,
const char *p))
{
+ struct device *dev = priv->card.dev;
+
/* If property is not provided, don't fail ... */
- if (!of_property_present(card->dev->of_node, propname))
+ if (!of_property_present(dev->of_node, propname))
return 0;
/* ... but do fail if it is provided and the parsing fails */
- return func(card, propname);
+ return func(&priv->card, propname);
}
static void meson_card_clean_references(struct meson_card *priv)
@@ -282,25 +286,25 @@ int meson_card_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- ret = meson_card_parse_of_optional(&priv->card, "audio-routing",
+ ret = meson_card_parse_of_optional(priv, "audio-routing",
snd_soc_of_parse_audio_routing);
if (ret) {
dev_err(dev, "error while parsing routing\n");
return ret;
}
- ret = meson_card_parse_of_optional(&priv->card, "audio-widgets",
+ ret = meson_card_parse_of_optional(priv, "audio-widgets",
snd_soc_of_parse_audio_simple_widgets);
if (ret) {
dev_err(dev, "error while parsing widgets\n");
return ret;
}
- ret = meson_card_add_links(&priv->card);
+ ret = meson_card_add_links(priv);
if (ret)
goto out_err;
- ret = meson_card_parse_of_optional(&priv->card, "audio-aux-devs",
+ ret = meson_card_parse_of_optional(priv, "audio-aux-devs",
snd_soc_of_parse_aux_devs);
if (ret)
goto out_err;
diff --git a/sound/soc/meson/meson-card.h b/sound/soc/meson/meson-card.h
index a0d693e4f4608..ce8d9042793bb 100644
--- a/sound/soc/meson/meson-card.h
+++ b/sound/soc/meson/meson-card.h
@@ -16,8 +16,9 @@ struct snd_pcm_hw_params;
#define DT_PREFIX "amlogic,"
+struct meson_card;
struct meson_card_match_data {
- int (*add_link)(struct snd_soc_card *card,
+ int (*add_link)(struct meson_card *priv,
struct device_node *node,
int *index);
};
@@ -35,15 +36,15 @@ int meson_card_i2s_set_sysclk(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
unsigned int mclk_fs);
-int meson_card_reallocate_links(struct snd_soc_card *card,
+int meson_card_reallocate_links(struct meson_card *priv,
unsigned int num_links);
-int meson_card_parse_dai(struct snd_soc_card *card,
+int meson_card_parse_dai(struct meson_card *priv,
struct device_node *node,
struct snd_soc_dai_link_component *dlc);
-int meson_card_set_be_link(struct snd_soc_card *card,
+int meson_card_set_be_link(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node);
-int meson_card_set_fe_link(struct snd_soc_card *card,
+int meson_card_set_fe_link(struct meson_card *priv,
struct snd_soc_dai_link *link,
struct device_node *node,
bool is_playback);
--
2.53.0
^ permalink raw reply related
* [PATCH RFC 1/2] ASoC: meson: meson-card-utils: use meson_card_parse_of_optional()
From: Kuninori Morimoto @ 2026-07-21 1:36 UTC (permalink / raw)
To: Jaroslav Kysela, Jerome Brunet, Kevin Hilman, Liam Girdwood,
Mark Brown, Martin Blumenstingl, Neil Armstrong, Takashi Iwai
Cc: linux-arm-kernel, linux-sound
In-Reply-To: <87v7a9i0o8.wl-kuninori.morimoto.gx@renesas.com>
We already have meson_card_parse_of_optional(). Let's use it for
snd_soc_of_parse_aux_devs() too.
It is using snd_soc_of_parse_card_name(), but let's keep as-is
for it for now. Beuase it will be not able to share code when
Card capsuling.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
---
sound/soc/meson/meson-card-utils.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index 8617a4661a339..d144ab79b645b 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -300,7 +300,8 @@ int meson_card_probe(struct platform_device *pdev)
if (ret)
goto out_err;
- ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs");
+ ret = meson_card_parse_of_optional(&priv->card, "audio-aux-devs",
+ snd_soc_of_parse_aux_devs);
if (ret)
goto out_err;
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox