* Re: [PATCH] KVM: arm64: ptdump: Flush the last region
From: Dev Jain @ 2026-07-20 9:30 UTC (permalink / raw)
To: Marc Zyngier
Cc: Wei-Lin Chang, linux-arm-kernel, kvmarm, linux-kernel,
Oliver Upton, Fuad Tabba, Joey Gouly, Steffen Eiden,
Suzuki K Poulose, Zenghui Yu, Catalin Marinas, Will Deacon,
Ryan Roberts, Sebastian Ene, Vincent Donnefort
In-Reply-To: <4a598a6c-496f-441c-b268-a725f9ee8090@arm.com>
On 20/07/26 2:40 pm, Dev Jain wrote:
>
>
> On 20/07/26 2:33 pm, Marc Zyngier wrote:
>> On Mon, 20 Jul 2026 09:53:29 +0100,
>> Dev Jain <dev.jain@arm.com> wrote:
>>>
>>>
>>>
>>> On 18/07/26 4:42 am, Wei-Lin Chang wrote:
>>>> Currently the stage-2 ptdump calls note_page() at each leaf entry visit.
>>>> This simply misses the output of the last region, because note_page()
>>>> only dumps output when it detects a change in level/prot, or when the
>>>> walk enters a next marker section. The last region in the guest IPA
>>>> space with the same level/prot is not dumped since there is no change
>>>> after it.
>>>>
>>>> To dump the final region, manually issue a note_page() call with address
>>>> BIT(ia_bits) (end of guest IPA space) and level == -1 to trigger a level
>>>> change after the last region. Additionally treat level == -1 as a last
>>>> flushing note_page() call and avoid dumping the name of the next marker
>>>> for this case.
>>>>
>>>> Fixes: 7c4f73548ed1 ("KVM: arm64: Register ptdump with debugfs on guest creation")
>>>> Reported-by: Sashiko AI <sashiko-bot@kernel.org>
>>>> Closes: https://lore.kernel.org/kvmarm/20260630122758.891011F00A3A@smtp.kernel.org/
>>>> Signed-off-by: Wei-Lin Chang <weilin.chang@arm.com>
>>>> ---
>>>> arch/arm64/kvm/ptdump.c | 8 +++++---
>>>> arch/arm64/mm/ptdump.c | 5 +++--
>>>> 2 files changed, 8 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
>>>> index c9140e22abcf..4096e4a92fae 100644
>>>> --- a/arch/arm64/kvm/ptdump.c
>>>> +++ b/arch/arm64/kvm/ptdump.c
>>>> @@ -155,11 +155,13 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
>>>> .seq = m,
>>>> };
>>>>
>>>> - write_lock(&kvm->mmu_lock);
>>>> + guard(write_lock)(&kvm->mmu_lock);
>>>
>>> The guard clause change has nothing to do with the fix I think? Although the change
>>> is small and the fix is for a recent commit so maybe we don't care having this
>>> in the same patch - not sure how strict kvm reviewers are about this : )
>>>
>>
>> Look at the quality spaghetti code this would otherwise result in:
>>
>> write_lock();
>> ret = kvm_pgtable_walk();
>> if (ret) {
>> write_unlock();
>> return ret;
>> }
>> note_page_flush();
>> write_unlock();
>>
>> return 0;
>>
>> How is that better? So it has *everything* to do with the fix.
>>
>> And FWIW, the policy for KVM is that we don't do lock->guard
>> conversions as a separate patches. Only when we need to change the
>> code, and as part of the patch that changes that code. Which is
>> exactly what is happening here.
>
> I agree, thanks.
Yeah in my head I was thinking that the guard clause is a "functional change"
but it really isn't, the fix implies that without the guard you will get
horrible code, and had the code been correct in the first place, it would
have been written *with* the guard, so yes that was a stupid
observation from my side : )
>
>
>>
>>>
>>>> ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
>>>> - write_unlock(&kvm->mmu_lock);
>>>> + if (ret)
>>>> + return ret;
>>>> + note_page(&st->parser_state.ptdump, BIT(mmu->pgt->ia_bits), -1, 0);
>>>>
>>>> - return ret;
>>>> + return 0;
>>>> }
>>>>
>>>> static int kvm_ptdump_guest_open(struct inode *m, struct file *file)
>>>> diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
>>>> index ab9899ca1e5f..fed4e4407e0e 100644
>>>> --- a/arch/arm64/mm/ptdump.c
>>>> +++ b/arch/arm64/mm/ptdump.c
>>>> @@ -194,6 +194,7 @@ void note_page(struct ptdump_state *pt_st, unsigned long addr, int level,
>>>> struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
>>>> struct ptdump_pg_level *pg_level = st->pg_level;
>>>> static const char units[] = "KMGTPE";
>>>> + bool flush = level == -1;
>>>
>>> Can we do something similar to what S1 ptdump does (note_page_flush in ptdump_walk_pgd).
>>
>> See Mark's email.
>>
>> M.
>>
>
^ permalink raw reply
* Re: [PATCH] net: stmmac: dwmac4: mask interrupts before stopping DMA in suspend
From: Maxime Chevallier @ 2026-07-20 9:33 UTC (permalink / raw)
To: Luis Lang, netdev
Cc: Andrew Lunn, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Russell King (Oracle), Ovidiu Panait, Oleksij Rempel,
Rohan G Thomas, moderated list:ARM/STM32 ARCHITECTURE,
moderated list:ARM/STM32 ARCHITECTURE, open list
In-Reply-To: <20260718152802.83553-1-luis.la@mail.de>
Hi Luis,
On 7/18/26 17:27, Luis Lang wrote:
> Since commit 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU
> interrupts"), suspending causes an interrupt storm from the RPS
> interrupt.
> Fix this by adding a deinit_chan() op to stmmac_dma_ops, which
> masks all default dma channel interrupts. This is called from
> stmmac_stop_all_dma(), so interrupts don't trigger while suspending.
>
> Fixes: 1b9707e6f1a9 ("net: stmmac: enable RPS and RBU interrupts")
> Suggested-by: Andrew Lunn <andrew@lunn.ch>
> Suggested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
> Signed-off-by: Luis Lang <luis.la@mail.de>
This is almost perfect for a new submission, only 2 things to improve :
- you're missing the tree name in the patch subject. As this is a fix, it should be :
[PATCH net] net: stmmac: dwmac4: mask interrupts before stopping DMA in suspend
you can find instructions for this in the doc Andrew linked :
https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
- When submitting a patch, do so in a dedicated thread, and not in reply to
other threads.
You should send a V2 addressing the tree name in the patch subject.
Sashiko noted something on the patch :
https://sashiko.dev/#/patchset/20260718152802.83553-1-luis.la%40mail.de
It looks to me that sashiko is just wrong there though, I can't make sense
of what it is saying.
Maxime
^ permalink raw reply
* [PATCH 2/3] sched: Convert paravirt_steal to new static key APIs
From: Hongyan Xia @ 2026-07-20 9:38 UTC (permalink / raw)
To: mingo@redhat.com, peterz@infradead.org, juri.lelli@redhat.com,
vincent.guittot@linaro.org, dietmar.eggemann@arm.com,
rostedt@goodmis.org, bsegall@google.com, mgorman@suse.de,
vschneid@redhat.com, kprateek.nayak@amd.com, Juergen Gross,
Ajay Kaher, Alexey Makhalov, Broadcom internal kernel review list,
Catalin Marinas, Will Deacon, Huacai Chen, WANG Xuerui,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Thomas Gleixner, Borislav Petkov,
Dave Hansen, x86@kernel.org, H. Peter Anvin, Paolo Bonzini,
Vitaly Kuznetsov, Stefano Stabellini, Oleksandr Tyshchenko
Cc: Jiazi Li, linux-kernel@vger.kernel.org,
virtualization@lists.linux.dev,
linux-arm-kernel@lists.infradead.org, loongarch@lists.linux.dev,
linuxppc-dev@lists.ozlabs.org, linux-riscv@lists.infradead.org,
kvm@vger.kernel.org, xen-devel@lists.xenproject.org
In-Reply-To: <cover.1784538478.git.hongyan.xia@transsion.com>
From: Hongyan Xia <hongyan.xia@transsion.com>
paravirt_steal_rq_enabled and paravirt_steal_enabled use raw static_key
APIs which are now deprecated. Use the new API instead.
No functional change.
Signed-off-by: Hongyan Xia <hongyan.xia@transsion.com>
---
arch/arm64/kernel/paravirt.c | 4 ++--
arch/loongarch/kernel/paravirt.c | 4 ++--
arch/powerpc/platforms/pseries/setup.c | 4 ++--
arch/riscv/kernel/paravirt.c | 4 ++--
arch/x86/kernel/cpu/vmware.c | 4 ++--
arch/x86/kernel/kvm.c | 4 ++--
drivers/xen/time.c | 4 ++--
include/linux/sched/cputime.h | 6 +++---
kernel/sched/core.c | 4 ++--
kernel/sched/cputime.c | 4 ++--
10 files changed, 21 insertions(+), 21 deletions(-)
diff --git a/arch/arm64/kernel/paravirt.c b/arch/arm64/kernel/paravirt.c
index 572efb96b23f..30bf61d031eb 100644
--- a/arch/arm64/kernel/paravirt.c
+++ b/arch/arm64/kernel/paravirt.c
@@ -157,9 +157,9 @@ int __init pv_time_init(void)
static_call_update(pv_steal_clock, para_steal_clock);
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
if (steal_acc)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
pr_info("using stolen time PV\n");
diff --git a/arch/loongarch/kernel/paravirt.c b/arch/loongarch/kernel/paravirt.c
index 10821cce554c..e8965a3f8082 100644
--- a/arch/loongarch/kernel/paravirt.c
+++ b/arch/loongarch/kernel/paravirt.c
@@ -308,10 +308,10 @@ int __init pv_time_init(void)
static_call_update(pv_steal_clock, paravt_steal_clock);
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
if (steal_acc)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
#endif
if (static_key_enabled(&virt_preempt_key))
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 1223dc961242..8dcbc4bb7025 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -852,9 +852,9 @@ static void __init pSeries_setup_arch(void)
static_branch_enable(&shared_processor);
pv_spinlocks_init();
#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
if (steal_acc)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
#endif
}
diff --git a/arch/riscv/kernel/paravirt.c b/arch/riscv/kernel/paravirt.c
index 5f56be79cd06..9c13a6f1ea2a 100644
--- a/arch/riscv/kernel/paravirt.c
+++ b/arch/riscv/kernel/paravirt.c
@@ -116,9 +116,9 @@ int __init pv_time_init(void)
static_call_update(pv_steal_clock, pv_time_steal_clock);
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
if (steal_acc)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
pr_info("Computing paravirt steal-time\n");
diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c
index 34b73573b108..f7ab9e7902cf 100644
--- a/arch/x86/kernel/cpu/vmware.c
+++ b/arch/x86/kernel/cpu/vmware.c
@@ -328,9 +328,9 @@ static int vmware_cpu_down_prepare(unsigned int cpu)
static __init int activate_jump_labels(void)
{
if (has_steal_clock) {
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
if (steal_acc)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
}
return 0;
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index dcef84da304b..d3dcd64f22c2 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -1052,9 +1052,9 @@ const __initconst struct hypervisor_x86 x86_hyper_kvm = {
static __init int activate_jump_labels(void)
{
if (has_steal_clock) {
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
if (steal_acc)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
}
return 0;
diff --git a/drivers/xen/time.c b/drivers/xen/time.c
index a2be0a4d45b0..a02d48a2aa68 100644
--- a/drivers/xen/time.c
+++ b/drivers/xen/time.c
@@ -169,7 +169,7 @@ void __init xen_time_setup_guest(void)
static_call_update(pv_steal_clock, xen_steal_clock);
- static_key_slow_inc(¶virt_steal_enabled);
+ static_branch_inc(¶virt_steal_enabled);
if (xen_runstate_remote)
- static_key_slow_inc(¶virt_steal_rq_enabled);
+ static_branch_inc(¶virt_steal_rq_enabled);
}
diff --git a/include/linux/sched/cputime.h b/include/linux/sched/cputime.h
index e90efaf6d26e..694126411dfe 100644
--- a/include/linux/sched/cputime.h
+++ b/include/linux/sched/cputime.h
@@ -182,9 +182,9 @@ extern unsigned long long
task_sched_runtime(struct task_struct *task);
#ifdef CONFIG_PARAVIRT
-struct static_key;
-extern struct static_key paravirt_steal_enabled;
-extern struct static_key paravirt_steal_rq_enabled;
+#include <linux/jump_label.h>
+DECLARE_STATIC_KEY_FALSE(paravirt_steal_enabled);
+DECLARE_STATIC_KEY_FALSE(paravirt_steal_rq_enabled);
#ifdef CONFIG_HAVE_PV_STEAL_CLOCK_GEN
u64 dummy_steal_clock(int cpu);
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 96226707c2f6..786975d4ca1e 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -795,7 +795,7 @@ struct rq *_task_rq_lock(struct task_struct *p, struct rq_flags *rf)
/* Use CONFIG_PARAVIRT as this will avoid more #ifdef in arch code. */
#ifdef CONFIG_PARAVIRT
-struct static_key paravirt_steal_rq_enabled;
+DEFINE_STATIC_KEY_FALSE(paravirt_steal_rq_enabled);
#endif
static void update_rq_clock_task(struct rq *rq, s64 delta)
@@ -834,7 +834,7 @@ static void update_rq_clock_task(struct rq *rq, s64 delta)
}
#endif
#ifdef CONFIG_PARAVIRT_TIME_ACCOUNTING
- if (static_key_false((¶virt_steal_rq_enabled))) {
+ if (static_branch_unlikely(¶virt_steal_rq_enabled)) {
u64 prev_steal;
steal = prev_steal = paravirt_steal_clock(cpu_of(rq));
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
index 06bddaa738e5..f16970ca81d0 100644
--- a/kernel/sched/cputime.c
+++ b/kernel/sched/cputime.c
@@ -255,7 +255,7 @@ void __account_forceidle_time(struct task_struct *p, u64 delta)
* occasion account more time than the calling functions think elapsed.
*/
#ifdef CONFIG_PARAVIRT
-struct static_key paravirt_steal_enabled;
+DEFINE_STATIC_KEY_FALSE(paravirt_steal_enabled);
#ifdef CONFIG_HAVE_PV_STEAL_CLOCK_GEN
static u64 native_steal_clock(int cpu)
@@ -270,7 +270,7 @@ DEFINE_STATIC_CALL(pv_steal_clock, native_steal_clock);
static __always_inline u64 steal_account_process_time(u64 maxtime)
{
#ifdef CONFIG_PARAVIRT
- if (static_key_false(¶virt_steal_enabled)) {
+ if (static_branch_unlikely(¶virt_steal_enabled)) {
u64 steal;
steal = paravirt_steal_clock(smp_processor_id());
--
2.47.3
^ permalink raw reply related
* [PATCH v7 0/3] Add RP1 PWM controller support
From: Andrea della Porta @ 2026-07-20 9:44 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov, mbrugger, Sean Young,
Julian Braha
This patchset adds support for the PWM controller found on the
Raspberry Pi RP1 southbridge. This is necessary to operate the
cooling fan connected to one of the PWM channels.
The tachometer pin for the fan speed is managed by the firmware
running on the RP1's M-core. It uses the PHASE2 register
to report the RPM, which is then exported by this driver via
syscon registers. A subsequent patch will add a new device
and driver to read the RPM and export this value via hwmon.
Subsequent patches will also add the CPU thermal zone, which
acts as a consumer of the PWM device.
Best regards,
Andrea
CHANGES in V7:
- _tohw(): use *wfhw = (typeof(*wfhw)){...} to initialize the
structure in the disable case
- _tohw(): fixed the offset_ticks calculation
- _fromhw(): fixed a rounding error in the duty_offset_ns calculation
- _write_waveform(): dropped the goto in favor of return in diasble
channel exit path
- _read_waveform(): use *wfhw = (typeof(*wfhw)){...} to initialize
the structure in the disable case
- dropped rp1_pwm_remove() since this driver depends on syscon
and cannot be removed/unbound (added a comment to explain that)
Naushir Patuck (2):
dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
pwm: rp1: Add RP1 PWM controller driver
Stanimir Varbanov (1):
arm64: dts: broadcom: rpi-5: Add RP1 PWM node
.../bindings/pwm/raspberrypi,rp1-pwm.yaml | 66 +++
.../boot/dts/broadcom/bcm2712-rpi-5-b.dts | 12 +
arch/arm64/boot/dts/broadcom/rp1-common.dtsi | 9 +
drivers/pwm/Kconfig | 9 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-rp1.c | 428 ++++++++++++++++++
6 files changed, 525 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
create mode 100644 drivers/pwm/pwm-rp1.c
--
2.35.3
^ permalink raw reply
* [PATCH v7 1/3] dt-bindings: pwm: Add Raspberry Pi RP1 PWM controller
From: Andrea della Porta @ 2026-07-20 9:44 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov, mbrugger, Sean Young,
Julian Braha
Cc: Krzysztof Kozlowski
In-Reply-To: <cover.1784539788.git.andrea.porta@suse.com>
From: Naushir Patuck <naush@raspberrypi.com>
Add the devicetree binding documentation for the PWM
controller found in the Raspberry Pi RP1 chipset.
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../bindings/pwm/raspberrypi,rp1-pwm.yaml | 66 +++++++++++++++++++
1 file changed, 66 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
diff --git a/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
new file mode 100644
index 0000000000000..970d0f3b33bbb
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/raspberrypi,rp1-pwm.yaml
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/pwm/raspberrypi,rp1-pwm.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Raspberry Pi RP1 PWM controller
+
+maintainers:
+ - Naushir Patuck <naush@raspberrypi.com>
+ - Andrea della Porta <andrea.porta@suse.com>
+ - Stanimir Varbanov <svarbanov@suse.de>
+
+allOf:
+ - $ref: pwm.yaml#
+
+description: |
+ The PWM peripheral is a flexible waveform generator with a
+ variety of operational modes. It has the following features:
+ - four independent output channels
+ - 32-bit counter widths
+ - Seven output generation modes
+ - Optional per-channel output inversion
+ - Optional duty-cycle data FIFO with DMA support
+ - Optional sigma-delta noise shaping engine
+ Serves as a fan speed provider to other nodes for a PWM-connected
+ fan using shared registers (syscon).
+
+properties:
+ compatible:
+ const: raspberrypi,rp1-pwm
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ "#pwm-cells":
+ const: 3
+
+ dmas:
+ maxItems: 1
+ description: |
+ Phandle to the DMA controller and the channel specifier for
+ the duty-cycle FIFO.
+
+ dma-names:
+ items:
+ - const: tx
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ pwm@98000 {
+ compatible = "raspberrypi,rp1-pwm";
+ reg = <0x98000 0x100>;
+ clocks = <&rp1_clocks 17>;
+ #pwm-cells = <3>;
+ };
--
2.35.3
^ permalink raw reply related
* [PATCH v7 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Andrea della Porta @ 2026-07-20 9:44 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov, mbrugger, Sean Young,
Julian Braha
In-Reply-To: <cover.1784539788.git.andrea.porta@suse.com>
From: Naushir Patuck <naush@raspberrypi.com>
The Raspberry Pi RP1 southbridge features an embedded PWM
controller with 4 output channels, alongside an RPM interface
to read the fan speed on the Raspberry Pi 5.
Add the supporting driver.
Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
drivers/pwm/Kconfig | 9 +
drivers/pwm/Makefile | 1 +
drivers/pwm/pwm-rp1.c | 428 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 438 insertions(+)
create mode 100644 drivers/pwm/pwm-rp1.c
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index e8886a9b64d96..0373dcf8d400d 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -637,6 +637,15 @@ config PWM_ROCKCHIP
Generic PWM framework driver for the PWM controller found on
Rockchip SoCs.
+config PWM_RASPBERRYPI_RP1
+ tristate "RP1 PWM support"
+ depends on MISC_RP1 || COMPILE_TEST
+ depends on HAS_IOMEM
+ select REGMAP_MMIO
+ select MFD_SYSCON
+ help
+ PWM framework driver for Raspberry Pi RP1 controller.
+
config PWM_SAMSUNG
tristate "Samsung PWM support"
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 5630a521a7cff..c07fd24f69f39 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT) += pwm-rzg2l-gpt.o
obj-$(CONFIG_PWM_RENESAS_RZ_MTU3) += pwm-rz-mtu3.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
+obj-$(CONFIG_PWM_RASPBERRYPI_RP1) += pwm-rp1.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c
new file mode 100644
index 0000000000000..e2d7af80dc071
--- /dev/null
+++ b/drivers/pwm/pwm-rp1.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * pwm-rp1.c
+ *
+ * Raspberry Pi RP1 PWM.
+ *
+ * Copyright © 2026 Raspberry Pi Ltd.
+ *
+ * Author: Naushir Patuck (naush@raspberrypi.com)
+ *
+ * Based on the pwm-bcm2835 driver by:
+ * Bart Tanghe <bart.tanghe@thomasmore.be>
+ *
+ * Datasheet: https://pip-assets.raspberrypi.com/categories/892-raspberry-pi-5/documents/RP-008370-DS-1-rp1-peripherals.pdf?disposition=inline
+ *
+ * Limitations:
+ * - Channels can be enabled/disabled through a global update flag, while the
+ * period and duty per-channel registers are independently updatable, and
+ * they are latched on the end of (specific channel) period strobe.
+ * This means that period and duty changes might result in glitches if the
+ * period/duty is changed exactly during an end of period strobe.
+ * - Since the duty/period registers are freely updatable (do not depend on
+ * the global update flag), setting one of them close to the period end and
+ * the other right afterwards results in a mixed output for that cycle because
+ * the write ops are not atomic.
+ * - The global update flag prevents mis-sampling of multi-bit bus signals in
+ * the PWM clock domain. This ensures that all PWM channel settings update
+ * on the same PWM clock cycle. Channels start in sync only if they share the
+ * same period.
+ * - If both duty and period are set to 0, the output is a constant low signal
+ * if polarity is normal or a constant high signal if polarity is inversed.
+ * - When disabled the output is driven to 0 if polarity is normal, or to 1
+ * if polarity is inversed.
+ * - Disabling the PWM stops the output immediately, without waiting for current
+ * period to complete first.
+ * - Channels are phase-capable, but on RPi5, the firmware can use a channel
+ * phase register to report the RPM of the fan connected to that PWM
+ * channel. As a result, phase control will be ignored for now.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+#include <linux/units.h>
+
+#define RP1_PWM_GLB_CTRL 0x000
+#define RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(chan) BIT(chan)
+#define RP1_PWM_GLB_CTRL_SET_UPDATE BIT(31)
+
+#define RP1_PWM_CHAN_CTRL(chan) (0x014 + ((chan) * 0x10))
+#define RP1_PWM_CHAN_CTRL_POLARITY BIT(3)
+#define RP1_PWM_CHAN_CTRL_FIFO_POP_MASK BIT(8)
+#define RP1_PWM_CHAN_CTRL_MODE GENMASK(2, 0)
+enum rp1_pwm_ctrl_mode {
+ RP1_PWM_CHAN_CTRL_MODE_ZERO,
+ RP1_PWM_CHAN_CTRL_MODE_TE_MS,
+ RP1_PWM_CHAN_CTRL_MODE_PC_MS,
+ RP1_PWM_CHAN_CTRL_MODE_PD_ENC,
+ RP1_PWM_CHAN_CTRL_MODE_MSB_SER,
+ RP1_PWM_CHAN_CTRL_MODE_PPM,
+ RP1_PWM_CHAN_CTRL_MODE_LE_MS,
+ RP1_PWM_CHAN_CTRL_MODE_LSB_SER,
+};
+
+#define RP1_PWM_CHAN_CTRL_DEFAULT (RP1_PWM_CHAN_CTRL_FIFO_POP_MASK + \
+ FIELD_PREP(RP1_PWM_CHAN_CTRL_MODE, \
+ RP1_PWM_CHAN_CTRL_MODE_TE_MS))
+
+#define RP1_PWM_RANGE(chan) (0x018 + ((chan) * 0x10))
+#define RP1_PWM_PHASE(chan) (0x01C + ((chan) * 0x10))
+#define RP1_PWM_DUTY(chan) (0x020 + ((chan) * 0x10))
+
+#define RP1_PWM_NUM_PWMS 4
+
+struct rp1_pwm {
+ struct regmap *regmap;
+ struct clk *clk;
+ unsigned long clk_rate;
+ bool clk_enabled;
+};
+
+struct rp1_pwm_waveform {
+ u32 period_ticks;
+ u32 duty_ticks;
+ bool enabled;
+ bool inverted_polarity;
+};
+
+static const struct regmap_config rp1_pwm_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x60,
+};
+
+static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ u32 value;
+
+ /* update the changed registers on the next strobe to avoid glitches */
+ regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &value);
+ value |= RP1_PWM_GLB_CTRL_SET_UPDATE;
+ regmap_write(rp1->regmap, RP1_PWM_GLB_CTRL, value);
+}
+
+static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+
+ /* init channel to reset defaults, preserving the polarity bit */
+ regmap_update_bits(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm),
+ ~(u32)RP1_PWM_CHAN_CTRL_POLARITY, RP1_PWM_CHAN_CTRL_DEFAULT);
+ return 0;
+}
+
+static int rp1_pwm_round_waveform_tohw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const struct pwm_waveform *wf,
+ void *_wfhw)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ u64 period_ticks, duty_ticks, offset_ticks;
+ struct rp1_pwm_waveform *wfhw = _wfhw;
+ u64 clk_rate = rp1->clk_rate;
+ int ret = 0;
+
+ if (!wf->period_length_ns) {
+ *wfhw = (typeof(*wfhw)){
+ .enabled = false,
+ .inverted_polarity = false,
+ };
+ return 0;
+ }
+
+ period_ticks = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC);
+
+ /*
+ * The period is limited to U32_MAX, and it will be decremented by one later
+ * to allow 100% duty cycle.
+ */
+ if (period_ticks > U32_MAX) {
+ period_ticks = U32_MAX;
+ } else if (period_ticks < 2) {
+ period_ticks = 2;
+ ret = 1;
+ }
+
+ duty_ticks = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC);
+ duty_ticks = min(duty_ticks, period_ticks);
+ offset_ticks = mul_u64_u64_div_u64(wf->duty_offset_ns, clk_rate, NSEC_PER_SEC);
+ if (offset_ticks >= period_ticks)
+ offset_ticks = period_ticks - 1;
+ if (duty_ticks && offset_ticks &&
+ duty_ticks + offset_ticks >= period_ticks) {
+ wfhw->duty_ticks = period_ticks - duty_ticks;
+ wfhw->inverted_polarity = true;
+ } else {
+ wfhw->duty_ticks = duty_ticks;
+ wfhw->inverted_polarity = false;
+ }
+ /* Account for the extra tick at the end of the period */
+ wfhw->period_ticks = period_ticks - 1;
+
+ wfhw->enabled = true;
+
+ return ret;
+}
+
+static int rp1_pwm_round_waveform_fromhw(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw,
+ struct pwm_waveform *wf)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ const struct rp1_pwm_waveform *wfhw = _wfhw;
+ u64 clk_rate = rp1->clk_rate;
+ u64 ticks;
+
+ *wf = (struct pwm_waveform){ };
+
+ if (!wfhw->enabled)
+ return 0;
+
+ wf->period_length_ns = DIV_ROUND_UP_ULL(((u64)wfhw->period_ticks + 1) * NSEC_PER_SEC,
+ clk_rate);
+
+ if (!wfhw->inverted_polarity) {
+ wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,
+ (u32)clk_rate);
+ } else {
+ if (wfhw->duty_ticks > (u64)wfhw->period_ticks + 1) {
+ /* 100% duty cycle case */
+ ticks = 0;
+ } else {
+ ticks = (u64)wfhw->period_ticks + 1 - wfhw->duty_ticks;
+ }
+ wf->duty_length_ns = DIV_ROUND_UP_ULL(ticks * NSEC_PER_SEC, clk_rate);
+ wf->duty_offset_ns = DIV_ROUND_UP_ULL((u64)(wfhw->period_ticks + 1 - ticks) *
+ NSEC_PER_SEC, clk_rate);
+ }
+
+ return 0;
+}
+
+static int rp1_pwm_write_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ const void *_wfhw)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ const struct rp1_pwm_waveform *wfhw = _wfhw;
+ u32 value, ctrl;
+
+ /* set polarity */
+ regmap_read(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), &value);
+ if (!wfhw->inverted_polarity)
+ value &= ~RP1_PWM_CHAN_CTRL_POLARITY;
+ else
+ value |= RP1_PWM_CHAN_CTRL_POLARITY;
+ regmap_write(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), value);
+
+ /* early exit if disabled */
+ regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &ctrl);
+ if (!wfhw->enabled) {
+ ctrl &= ~RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm);
+ /* just disable the channel and bail out early */
+ regmap_write(rp1->regmap, RP1_PWM_GLB_CTRL, ctrl);
+ rp1_pwm_apply_config(chip, pwm);
+ return 0;
+ }
+
+ /* set period and duty cycle */
+ regmap_write(rp1->regmap,
+ RP1_PWM_RANGE(pwm->hwpwm), wfhw->period_ticks);
+ regmap_write(rp1->regmap,
+ RP1_PWM_DUTY(pwm->hwpwm), wfhw->duty_ticks);
+
+ /* enable the channel */
+ ctrl |= RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm);
+ regmap_write(rp1->regmap, RP1_PWM_GLB_CTRL, ctrl);
+
+ rp1_pwm_apply_config(chip, pwm);
+
+ return 0;
+}
+
+static int rp1_pwm_read_waveform(struct pwm_chip *chip,
+ struct pwm_device *pwm,
+ void *_wfhw)
+{
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ struct rp1_pwm_waveform *wfhw = _wfhw;
+ u32 value;
+
+ regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &value);
+ wfhw->enabled = !!(value & RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm));
+
+ regmap_read(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), &value);
+ wfhw->inverted_polarity = !!(value & RP1_PWM_CHAN_CTRL_POLARITY);
+
+ if (wfhw->enabled) {
+ regmap_read(rp1->regmap, RP1_PWM_RANGE(pwm->hwpwm), &wfhw->period_ticks);
+ regmap_read(rp1->regmap, RP1_PWM_DUTY(pwm->hwpwm), &wfhw->duty_ticks);
+ } else {
+ *wfhw = (typeof(*wfhw)) {
+ .period_ticks = 0,
+ .duty_ticks = 0,
+ };
+ }
+
+ return 0;
+}
+
+static const struct pwm_ops rp1_pwm_ops = {
+ .sizeof_wfhw = sizeof(struct rp1_pwm_waveform),
+ .request = rp1_pwm_request,
+ .round_waveform_tohw = rp1_pwm_round_waveform_tohw,
+ .round_waveform_fromhw = rp1_pwm_round_waveform_fromhw,
+ .read_waveform = rp1_pwm_read_waveform,
+ .write_waveform = rp1_pwm_write_waveform,
+};
+
+static int rp1_pwm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ unsigned long clk_rate;
+ struct pwm_chip *chip;
+ void __iomem *base;
+ struct rp1_pwm *rp1;
+ int ret;
+
+ chip = devm_pwmchip_alloc(dev, RP1_PWM_NUM_PWMS, sizeof(*rp1));
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ rp1 = pwmchip_get_drvdata(chip);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rp1->regmap = devm_regmap_init_mmio(dev, base, &rp1_pwm_regmap_config);
+ if (IS_ERR(rp1->regmap))
+ return dev_err_probe(dev, PTR_ERR(rp1->regmap), "Cannot initialize regmap\n");
+
+ rp1->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(rp1->clk))
+ return dev_err_probe(dev, PTR_ERR(rp1->clk), "Clock not found\n");
+
+ ret = clk_prepare_enable(rp1->clk);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable clock\n");
+ rp1->clk_enabled = true;
+
+ ret = devm_clk_rate_exclusive_get(dev, rp1->clk);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to get exclusive rate\n");
+ goto err_disable_clk;
+ }
+
+ clk_rate = clk_get_rate(rp1->clk);
+ if (!clk_rate) {
+ ret = dev_err_probe(dev, -EINVAL, "Failed to get clock rate\n");
+ goto err_disable_clk;
+ }
+ /*
+ * To prevent u64 overflow in period calculations:
+ * mul_u64_u64_div_u64(period_ns, clk_rate, NSEC_PER_SEC)
+ * If clk_rate > 1 GHz, the result can overflow.
+ */
+ if (clk_rate > HZ_PER_GHZ) {
+ ret = dev_err_probe(dev, -EINVAL, "Clock rate > 1 GHz is not supported\n");
+ goto err_disable_clk;
+ }
+ rp1->clk_rate = clk_rate;
+
+ chip->ops = &rp1_pwm_ops;
+ chip->atomic = true;
+
+ platform_set_drvdata(pdev, chip);
+
+ ret = pwmchip_add(chip);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to register PWM chip\n");
+ goto err_disable_clk;
+ }
+
+ ret = of_syscon_register_regmap(np, rp1->regmap);
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to register syscon\n");
+ goto err_remove_chip;
+ }
+
+ return 0;
+
+err_remove_chip:
+ pwmchip_remove(chip);
+err_disable_clk:
+ clk_disable_unprepare(rp1->clk);
+
+ return ret;
+}
+
+static int rp1_pwm_suspend(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+
+ if (rp1->clk_enabled) {
+ clk_disable_unprepare(rp1->clk);
+ rp1->clk_enabled = false;
+ }
+
+ return 0;
+}
+
+static int rp1_pwm_resume(struct device *dev)
+{
+ struct pwm_chip *chip = dev_get_drvdata(dev);
+ struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
+ int ret;
+
+ ret = clk_prepare_enable(rp1->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock on resume: %pe\n", ERR_PTR(ret));
+ return ret;
+ }
+
+ rp1->clk_enabled = true;
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(rp1_pwm_pm_ops, rp1_pwm_suspend, rp1_pwm_resume);
+
+static const struct of_device_id rp1_pwm_of_match[] = {
+ { .compatible = "raspberrypi,rp1-pwm" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
+
+static struct platform_driver rp1_pwm_driver = {
+ .probe = rp1_pwm_probe,
+ /*
+ * .remove is not needed because this driver cannot be removed/unbound
+ * cause it relies on syscon which cannot be removed
+ */
+ .driver = {
+ .name = "rp1-pwm",
+ .of_match_table = rp1_pwm_of_match,
+ .pm = pm_ptr(&rp1_pwm_pm_ops),
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(rp1_pwm_driver);
+
+MODULE_DESCRIPTION("RP1 PWM driver");
+MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
+MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>");
+MODULE_LICENSE("GPL");
--
2.35.3
^ permalink raw reply related
* [PATCH v7 3/3] arm64: dts: broadcom: rpi-5: Add RP1 PWM node
From: Andrea della Porta @ 2026-07-20 9:44 UTC (permalink / raw)
To: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, Andrea della Porta,
devicetree, linux-rpi-kernel, linux-arm-kernel, linux-kernel,
Naushir Patuck, Stanimir Varbanov, mbrugger, Sean Young,
Julian Braha
In-Reply-To: <cover.1784539788.git.andrea.porta@suse.com>
From: Stanimir Varbanov <svarbanov@suse.de>
The RP1 chipset used on the Raspberry Pi 5 features an integrated
PWM controller to drive the cooling fan.
Add the corresponding DT node for this PWM controller.
Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
Co-developed-by: Andrea della Porta <andrea.porta@suse.com>
Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
---
arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts | 12 ++++++++++++
arch/arm64/boot/dts/broadcom/rp1-common.dtsi | 9 +++++++++
2 files changed, 21 insertions(+)
diff --git a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
index 0fc57e72632ed..748be8f1ee9e2 100644
--- a/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
+++ b/arch/arm64/boot/dts/broadcom/bcm2712-rpi-5-b.dts
@@ -64,6 +64,12 @@ phy1: ethernet-phy@1 {
};
&rp1_gpio {
+ fan_pwm_default_state: fan-pwm-default-state {
+ function = "pwm1";
+ pins = "gpio45";
+ bias-pull-down;
+ };
+
usb_vbus_default_state: usb-vbus-default-state {
function = "vbus1";
groups = "vbus1";
@@ -94,6 +100,12 @@ &rp1_i2c6 {
pinctrl-names = "default";
};
+&rp1_pwm1 {
+ pinctrl-0 = <&fan_pwm_default_state>;
+ pinctrl-names = "default";
+ status = "okay";
+};
+
&rp1_usb0 {
pinctrl-0 = <&usb_vbus_default_state>;
pinctrl-names = "default";
diff --git a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
index 16f5359395835..df4c2d09c8d34 100644
--- a/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
+++ b/arch/arm64/boot/dts/broadcom/rp1-common.dtsi
@@ -99,7 +99,16 @@ rp1_i2c6: i2c@40088000 {
clocks = <&rp1_clocks RP1_CLK_SYS>;
i2c-scl-rising-time-ns = <65>;
i2c-scl-falling-time-ns = <100>;
+ status = "disabled";
+ };
+ rp1_pwm1: pwm@4009c000 {
+ compatible = "raspberrypi,rp1-pwm";
+ reg = <0x00 0x4009c000 0x0 0x100>;
+ clocks = <&rp1_clocks RP1_CLK_PWM1>;
+ assigned-clocks = <&rp1_clocks RP1_CLK_PWM1>;
+ assigned-clock-rates = <50000000>;
+ #pwm-cells = <3>;
status = "disabled";
};
--
2.35.3
^ permalink raw reply related
* Re: [PATCH 2/8] i2c: pxa-pci: use platform_device_set_fwnode()
From: Wolfram Sang @ 2026-07-20 9:41 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Greg Kroah-Hartman, Rafael J. Wysocki, Danilo Krummrich,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Andi Shyti, Joerg Roedel (AMD),
Will Deacon, Robin Murphy, Andy Shevchenko, Doug Berger,
Florian Fainelli, Broadcom internal kernel review list,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Ulf Hansson, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Lee Jones,
Sebastian Hesselbarth, Srinivas Kandagatla, brgl, driver-core,
linuxppc-dev, linux-kernel, linux-i2c, iommu, netdev, linux-pm,
imx, linux-arm-kernel, mfd, linux-arm-msm, linux-sound
In-Reply-To: <20260720-pdev-set-fwnode-instead-of-of-node-v1-2-2dee93f42c54@oss.qualcomm.com>
On Mon, Jul 20, 2026 at 11:24:49AM +0200, Bartosz Golaszewski wrote:
> Prefer the higher-level platform_device_set_fwnode() over the
> OF-specific platform_device_set_of_node() for dynamically allocated
> platform devices.
>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Fine with me.
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Note that we still need Andi's ack.
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: amlogic: a9: Add reset controller support
From: Neil Armstrong @ 2026-07-20 9:50 UTC (permalink / raw)
To: xianwei.zhao, Philipp Zabel, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Kevin Hilman, Jerome Brunet, Martin Blumenstingl
Cc: devicetree, linux-arm-kernel, linux-amlogic, linux-kernel
In-Reply-To: <20260714-a9-reset-v2-3-8df09b2f437c@amlogic.com>
On 7/14/26 04:33, Xianwei Zhao via B4 Relay wrote:
> From: Xianwei Zhao <xianwei.zhao@amlogic.com>
>
> Add reset controller descriptions for the Amlogic A9 SoC and include the
> corresponding reset IDs header.
>
> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
> ---
> arch/arm64/boot/dts/amlogic/amlogic-a9-reset.h | 247 +++++++++++++++++++++++++
> arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi | 14 ++
> 2 files changed, 261 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/amlogic/amlogic-a9-reset.h b/arch/arm64/boot/dts/amlogic/amlogic-a9-reset.h
> new file mode 100644
> index 000000000000..55fcaa54cd56
> --- /dev/null
> +++ b/arch/arm64/boot/dts/amlogic/amlogic-a9-reset.h
> @@ -0,0 +1,247 @@
> +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */
> +/*
> + * Copyright (c) 2026 Amlogic, Inc. All rights reserved.
> + * Author: Zelong Dong <zelong.dong@amlogic.com>
> + *
> + */
> +
> +#ifndef _DT_BINDINGS_AMLOGIC_MESON_A9_RESET_H
> +#define _DT_BINDINGS_AMLOGIC_MESON_A9_RESET_H
> +
> +/* AO RESET0 */
> +#define AO_RESET_APB 0
> +#define AO_RESET_RTC 1
> +#define AO_RESET_BRG_NIC_RTC 2
> +#define AO_RESET_AO2EE 3
> +#define AO_RESET_BRG_NIC_EE 4
> +#define AO_RESET_WATCHDOG 5
> +#define AO_RESET_I3C 6
> +#define AO_RESET_PWR 7
> +#define AO_RESET_PWM_A 8
> +#define AO_RESET_PWM_B 9
> +#define AO_RESET_PWM_C 10
> +#define AO_RESET_PWM_D 11
> +#define AO_RESET_PWM_E 12
> +#define AO_RESET_PWM_F 13
> +#define AO_RESET_PWM_G 14
> +#define AO_RESET_I2C_M_A 15
> +#define AO_RESET_I2C_M_B 16
> +#define AO_RESET_I2C_M_C 17
> +#define AO_RESET_I2C_M_D 18
> +#define AO_RESET_IR 19
> +#define AO_RESET_UART_B 20
> +#define AO_RESET_UART_C 21
> +#define AO_RESET_UART_D 22
> +#define AO_RESET_SPISG 23
> +#define AO_RESET_SED 24
> +#define AO_RESET_CEC 25
> +#define AO_RESET_AOCPU 26
> +#define AO_RESET_AOCPU_POR 27
> +#define AO_RESET_AOCPU_CORE 28
> +#define AO_RESET_SRAM 29
> +#define AO_RESET_CAPU 30
> +#define AO_RESET_UART_E 31
> +
> +/* RESET0 */
> +#define RESET_ETH_1G 0
> +#define RESET_ISP 1
> +#define RESET_U3DRD_USB3PHY_APB 2
> +#define RESET_U3DRD_USB3PHY 3
> +#define RESET_U3DRD_USB2PHY 4
> +#define RESET_U3DRD 5
> +#define RESET_U3DRD_COMB 6
> +#define RESET_U3DRD_USB2PHY_APB 7
> +#define RESET_DP_PHY_APB 8
> +#define RESET_DP_PHY 9
> +#define RESET_DPTX_1P4 10
> +#define RESET_DPTX_1P4_APB 11
> +#define RESET_EDPTX_1P4 12
> +#define RESET_USB2DRD_PHY_APB 13
> +#define RESET_U2DRD_COMB 14
> +#define RESET_U2DRD 15
> +#define RESET_HDMI20_AES 16
> +#define RESET_HDMITX_CBUS_APB 17
> +#define RESET_BRG_VCBUS_DEC 18
> +#define RESET_VCBUS 19
> +#define RESET_VID_PLL_DIV 20
> +#define RESET_VDI6 21
> +#define RESET_HDMITXPHY 22
> +#define RESET_VID_LOCK 23
> +#define RESET_VENC_2 24
> +#define RESET_VDAC 25
> +#define RESET_VENC_1 26
> +#define RESET_VENC_0 27
> +#define RESET_RDMA 28
> +#define RESET_HDMITX 29
> +#define RESET_VIU 30
> +#define RESET_VENC 31
> +
> +/* RESET1 */
> +#define RESET_AUDIO 32
> +#define RESET_MALI_CBUS_APB 33
> +#define RESET_MALI 34
> +#define RESET_PCIE_B_PHY 35
> +#define RESET_PCIE_B_POR 36
> +#define RESET_DOS_CBUS_APB 37
> +#define RESET_DOS 38
> +#define RESET_MALI_SYS 39
> +#define RESET_CC 40
> +#define RESET_DSP_A_DEBUG 41
> +#define RESET_PCIE_A_PHY_APB 42
> +#define RESET_PCIE_A_PIPE 43
> +#define RESET_PCIE_A_POR 44
> +#define RESET_PCIE_A_PHY 45
> +#define RESET_PCIE_A_MAC_APB 46
> +#define RESET_AMFC_APB 47
> +#define RESET_ETH 48
> +/* 49 */
> +#define RESET_MALI_MBIST 50
> +#define RESET_ETH_1G_AXI 51
> +#define RESET_VICP 52
> +#define RESET_DEWARP 53
> +#define RESET_GE2D 54
> +#define RESET_VGE 55
> +#define RESET_PCIE_A_0 56
> +#define RESET_PCIE_A_1 57
> +#define RESET_PCIE_A_2 58
> +#define RESET_PCIE_A_3 59
> +#define RESET_PCIE_A_4 60
> +#define RESET_PCIE_A_5 61
> +#define RESET_PCIE_A_6 62
> +#define RESET_PCIE_A_7 63
> +
> +/* RESET2 */
> +#define RESET_AM2AXI 64
> +#define RESET_DSP_A 65
> +#define RESET_MIPI_DSI_PHY 66
> +#define RESET_TS_PLL 67
> +#define RESET_TS_A55 68
> +#define RESET_ETH_AXI 69
> +#define RESET_TS_CORE 70
> +#define RESET_MIPI_DSI1_PHY 71
> +#define RESET_SMART_CARD 72
> +#define RESET_SPISG 73
> +#define RESET_TS_DOS 74
> +#define RESET_U2DRD_USB2PHY 75
> +#define RESET_PIO 76
> +#define RESET_U2H_COMB 77
> +#define RESET_U2H 78
> +#define RESET_USB2H_PHY_APB 79
> +#define RESET_MSR_CLK 80
> +/* 81 */
> +#define RESET_AUX_DIG 82
> +/* 83 */
> +#define RESET_U2H_USB2PHY 84
> +#define RESET_U3HSG_PCIE_PIPE 85
> +#define RESET_AMFC 86
> +#define RESET_U3HSG_PCIE_PHY_APB 87
> +#define RESET_U3HSG_PCIE_PHY 88
> +#define RESET_PP_DMA 89
> +#define RESET_I3C 90
> +#define RESET_WATCHDOG 91
> +#define RESET_PP_WRAPPER 92
> +#define RESET_MIPI_DSI_HOST 93
> +#define RESET_DSI_PLL_DIV 94
> +#define RESET_MIPI_DSI_B_HOST 95
> +
> +/* RESET3 */
> +/* 96 */
> +#define RESET_HDMIRX_WRAP_APB 97
> +#define RESET_HDMIRX 98
> +#define RESET_PCIE_B_0 99
> +#define RESET_PCIE_B_1 100
> +#define RESET_PCIE_B_2 101
> +#define RESET_PCIE_B_3 102
> +#define RESET_PCIE_B_4 103
> +#define RESET_PCIE_B_5 104
> +#define RESET_PCIE_B_6 105
> +#define RESET_PCIE_B_7 106
> +#define RESET_PCIE_B_PIPE 107
> +#define RESET_PCIE_B_MAC_APB 108
> +#define RESET_NNA_TO_VGA_PIPE 109
> +#define RESET_CVE 110
> +#define RESET_GLOBAL_TIMER 111
> +#define RESET_COMBO_DPHY_PCLK 112
> +#define RESET_COMBO_DPHY 113
> +/* 114 - 118 */
> +#define RESET_U3PHY30_APB 119
> +#define RESET_U3PHY30 120
> +#define RESET_HSG 121
> +#define RESET_U3HSG_HSG 122
> +#define RESET_U3DRDB 123
> +#define RESET_U3DRDB_APB 124
> +#define RESET_U3PHY20_APB 125
> +#define RESET_U3PHY20 126
> +#define RESET_A55_ACE 127
> +
> +/* RESET4 */
> +#define RESET_CAN_0 128
> +#define RESET_CAN_1 129
> +#define RESET_TAHOE_CORE 130
> +#define RESET_TAHOE 131
> +#define RESET_TAHOE_APB 132
> +#define RESET_TAHOE_SYS 133
> +/* 134 - 135 */
> +#define RESET_PWM_I 136
> +#define RESET_PWM_J 137
> +#define RESET_UART_A 138
> +/* 139 - 143 */
> +#define RESET_MALI_AVBCD 144
> +#define RESET_MALI_AVBCD_APB 145
> +#define RESET_MALI_MCR_TOP 146
> +#define RESET_I2C_M_E 147
> +#define RESET_I2C_M_F 148
> +#define RESET_I2C_M_G 149
> +#define RESET_I2C_M_H 150
> +#define RESET_I2C_M_I 151
> +#define RESET_SD_EMMC_A 152
> +#define RESET_SD_EMMC_B 153
> +#define RESET_SD_EMMC_C 154
> +#define RESET_UART_F 155
> +#define RESET_PWM_N 156
> +#define RESET_PWM_M 157
> +#define RESET_PWM_L 158
> +#define RESET_PWM_K 159
> +
> +/* RESET5 */
> +#define RESET_BRG_ISP_PIPE 160
> +#define RESET_BRG_HEVCF_DMC_PIPE 161
> +#define RESET_BRG_HEVCB_PIPE 162
> +#define RESET_BRG_EMMC_PIPE 163
> +#define RESET_BRG_VGE_PIPE 164
> +#define RESET_BRG_DMC_VPU1_PIPE 165
> +#define RESET_BRG_DMC_VPU0_PIPE 166
> +#define RESET_BRG_NNA_PIPE 167
> +#define RESET_BRG_NNA_SRAM_PIPE 168
> +#define RESET_BRG_U2DRDA_PIPE 169
> +#define RESET_BRG_U3DRDA_PIPE 170
> +/* 171 - 173 */
> +#define RESET_BRG_NIC_AOSYS 174
> +#define RESET_BRG_NIC_AMFC 175
> +#define RESET_BRG_NIC_GIC 176
> +#define RESET_BRG_SRAM_NIC_NNA 177
> +#define RESET_BRG_SRAM_NIC_MAIN 178
> +#define RESET_BRG_SRAM_NIC_ALL 179
> +#define RESET_BRG_NIC_SOC_BRG 180
> +#define RESET_BRG_NIC_GPV 181
> +#define RESET_BRG_NIC_AO 182
> +#define RESET_BRG_NIC_EMMC 183
> +#define RESET_BRG_NIC_DSP_A 184
> +#define RESET_BRG_NIC_SDIO_B 185
> +#define RESET_BRG_NIC_SDIO_A 186
> +#define RESET_BRG_NIC_VAP_B 187
> +#define RESET_BRG_NIC_DSU 188
> +#define RESET_BRG_NIC_CLK81 189
> +#define RESET_BRG_NIC_MAIN 190
> +#define RESET_BRG_NIC_ALL 191
> +
> +/* RESET6 */
> +/* 192 - 216 */
> +#define RESET_BRG_PP_CLK 217
> +#define RESET_BRG_PP_NIC_CLK81 218
> +/* 219 - 220 */
> +#define RESET_BRG_PERIPH_APB_SYNC 221
> +#define RESET_BRG_VPU_APB_SYNC 222
> +#define RESET_BRG_DSP_A_PIPE 223
> +
> +#endif
> diff --git a/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi b/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi
> index b0e0fadeed82..fedde52c2e89 100644
> --- a/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/amlogic-a9.dtsi
> @@ -7,6 +7,7 @@
> #include <dt-bindings/interrupt-controller/arm-gic.h>
> #include <dt-bindings/gpio/gpio.h>
> #include <dt-bindings/pinctrl/amlogic,pinctrl.h>
> +#include "amlogic-a9-reset.h"
>
> / {
> interrupt-parent = <&gic>;
> @@ -105,6 +106,13 @@ apb: bus@fe000000 {
> #size-cells = <2>;
> ranges = <0x0 0x0 0x0 0xfe000000 0x0 0x480000>;
>
> + reset: reset-controller@2000 {
> + compatible = "amlogic,a9-reset",
> + "amlogic,t7-reset";
> + reg = <0x0 0x2000 0x0 0x98>;
> + #reset-cells = <1>;
> + };
> +
> periphs_pinctrl: pinctrl@4000 {
> compatible = "amlogic,pinctrl-a9";
> #address-cells = <2>;
> @@ -204,6 +212,12 @@ aobus: bus@ffa00000 {
> #size-cells = <2>;
> ranges = <0x0 0x0 0x0 0xffa00000 0x0 0x100000>;
>
> + ao_reset: reset-controller@2000 {
> + compatible = "amlogic,a9-ao-reset";
> + reg = <0x0 0x2000 0x0 0xc>;
> + #reset-cells = <1>;
> + };
> +
> aobus_pinctrl: pinctrl@4000 {
> compatible = "amlogic,pinctrl-a9";
> #address-cells = <2>;
>
Reviewed-by: Neil Armstrong <neil.armstrong@linaro.org>
Thanks,
Neil
^ permalink raw reply
* [PATCH 0/4] ASoC: fsl: expose individual DAIs per ASRC/EASRC conversion pair/context
From: shengjiu.wang @ 2026-07-20 9:54 UTC (permalink / raw)
To: lgirdwood, broonie, robh, krzk+dt, conor+dt, Frank.Li, s.hauer,
kernel, festevam, shawnguo, linux-sound, devicetree, imx,
linux-arm-kernel, linux-kernel, shengjiu.wang, Xiubo.Lee,
nicoleotsuka, perex, tiwai, linuxppc-dev
Cc: Shengjiu Wang
From: Shengjiu Wang <shengjiu.wang@nxp.com>
The i.MX ASRC and EASRC hardware each support multiple independent
conversion pairs or contexts, but both drivers previously registered
only a single DAI with generic stream names. This prevented individual
pairs or contexts from being routed to separate audio paths at the same
time.
This series updates the DT bindings and drivers to expose one DAI per
conversion unit:
- fsl,imx-asrc: add a ports container (port@0/1/2 for pairs A/B/C)
alongside the deprecated single port property
- fsl,easrc: add a ports container (port@0/1/2/3 for contexts A/B/C/D)
- fsl_asrc: register three DAIs (paira/pairb/pairc); pair A keeps the
original stream names for backward compatibility
- fsl_easrc: register four DAIs (ctxa/ctxb/ctxc/ctxd); context A keeps
the original stream names for backward compatibility
Shengjiu Wang (4):
dt-bindings: sound: fsl,imx-asrc: update port binding to support
multiple pairs
dt-bindings: sound: fsl,easrc: add ports binding for multiple contexts
ASoC: fsl_asrc: expose individual DAIs per conversion pair
ASoC: fsl_easrc: expose individual DAIs per conversion context
.../devicetree/bindings/sound/fsl,easrc.yaml | 79 ++++++++++++
.../bindings/sound/fsl,imx-asrc.yaml | 78 ++++++++++++
sound/soc/fsl/fsl_asrc.c | 89 ++++++++++----
sound/soc/fsl/fsl_easrc.c | 112 ++++++++++++++----
4 files changed, 318 insertions(+), 40 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH 1/4] dt-bindings: sound: fsl,imx-asrc: update port binding to support multiple pairs
From: shengjiu.wang @ 2026-07-20 9:54 UTC (permalink / raw)
To: lgirdwood, broonie, robh, krzk+dt, conor+dt, Frank.Li, s.hauer,
kernel, festevam, shawnguo, linux-sound, devicetree, imx,
linux-arm-kernel, linux-kernel, shengjiu.wang, Xiubo.Lee,
nicoleotsuka, perex, tiwai, linuxppc-dev
Cc: Shengjiu Wang
In-Reply-To: <20260720095450.1618747-1-shengjiu.wang@oss.nxp.com>
From: Shengjiu Wang <shengjiu.wang@nxp.com>
The i.MX ASRC hardware supports up to three conversion pairs (A, B, C).
The existing binding exposed only a single generic audio-graph port,
which cannot represent the individual pair endpoints.
Add a ports container that enumerates three named sub-ports:
port@0 -- pair A
port@1 -- pair B
port@2 -- pair C
Each sub-port references audio-graph-port.yaml as before.
For backwards compatibility, keep the old single port property but mark
it deprecated. A schema constraint (not: required: [port, ports]) ensures
that both forms cannot be used at the same time. Update the examples to
show both the new ports form and the deprecated port form.
No existing upstream DTS file for fsl,imx-asrc uses the previously
documented single 'port' node, so this change does not break any
in-tree device tree.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
.../bindings/sound/fsl,imx-asrc.yaml | 78 +++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
index 608defc93c1e..18d252722aa8 100644
--- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
@@ -78,9 +78,29 @@ properties:
power-domains:
maxItems: 1
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for pair A
+
+ port@1:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for pair B
+
+ port@2:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for pair C
+
port:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
+ deprecated: true
+ description: deprecated, use ports instead
fsl,asrc-rate:
$ref: /schemas/types.yaml#/definitions/uint32
@@ -125,6 +145,10 @@ required:
- fsl,asrc-width
allOf:
+ - not:
+ required:
+ - port
+ - ports
- $ref: dai-common.yaml#
- if:
properties:
@@ -188,3 +212,57 @@ examples:
};
};
};
+
+ - |
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/clock/imx6qdl-clock.h>
+ asrc1: asrc@2034000 {
+ compatible = "fsl,imx53-asrc";
+ reg = <0x02034000 0x4000>;
+ interrupts = <0 50 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clks IMX6QDL_CLK_ASRC_IPG>,
+ <&clks IMX6QDL_CLK_ASRC_MEM>, <&clks 0>,
+ <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+ <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+ <&clks 0>, <&clks 0>, <&clks 0>, <&clks 0>,
+ <&clks IMX6QDL_CLK_ASRC>, <&clks 0>, <&clks 0>,
+ <&clks IMX6QDL_CLK_SPBA>;
+ clock-names = "mem", "ipg", "asrck_0",
+ "asrck_1", "asrck_2", "asrck_3", "asrck_4",
+ "asrck_5", "asrck_6", "asrck_7", "asrck_8",
+ "asrck_9", "asrck_a", "asrck_b", "asrck_c",
+ "asrck_d", "asrck_e", "asrck_f", "spba";
+ dmas = <&sdma 17 23 1>, <&sdma 18 23 1>, <&sdma 19 23 1>,
+ <&sdma 20 23 1>, <&sdma 21 23 1>, <&sdma 22 23 1>;
+ dma-names = "rxa", "rxb", "rxc",
+ "txa", "txb", "txc";
+ fsl,asrc-rate = <48000>;
+ fsl,asrc-width = <16>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ playback-only;
+ asrc1_endpoint0: endpoint {
+ remote-endpoint = <&fe00_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ capture-only;
+ asrc1_endpoint1: endpoint {
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ capture-only;
+ asrc1_endpoint2: endpoint {
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH 2/4] dt-bindings: sound: fsl,easrc: add ports binding for multiple contexts
From: shengjiu.wang @ 2026-07-20 9:54 UTC (permalink / raw)
To: lgirdwood, broonie, robh, krzk+dt, conor+dt, Frank.Li, s.hauer,
kernel, festevam, shawnguo, linux-sound, devicetree, imx,
linux-arm-kernel, linux-kernel, shengjiu.wang, Xiubo.Lee,
nicoleotsuka, perex, tiwai, linuxppc-dev
Cc: Shengjiu Wang
In-Reply-To: <20260720095450.1618747-1-shengjiu.wang@oss.nxp.com>
From: Shengjiu Wang <shengjiu.wang@nxp.com>
The i.MX EASRC hardware supports up to four conversion contexts
(A, B, C, D). Add a ports container property to the binding to allow
each context to be represented as an individual audio-graph port:
port@0 -- context A
port@1 -- context B
port@2 -- context C
port@3 -- context D
Each sub-port references audio-graph-port.yaml and follows the standard
audio-graph binding conventions.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
.../devicetree/bindings/sound/fsl,easrc.yaml | 79 +++++++++++++++++++
1 file changed, 79 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
index d5727f8bfb0b..0f579870110d 100644
--- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
@@ -55,6 +55,29 @@ properties:
- const: imx/easrc/easrc-imx8mn.bin
description: The coefficient table for the filters
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ properties:
+ port@0:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for context A
+
+ port@1:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for context B
+
+ port@2:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for context C
+
+ port@3:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for context D
+
fsl,asrc-rate:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 8000
@@ -107,3 +130,59 @@ examples:
fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
};
+
+ - |
+ #include <dt-bindings/clock/imx8mn-clock.h>
+
+ easrc1: easrc@300c0000 {
+ compatible = "fsl,imx8mn-easrc";
+ reg = <0x300c0000 0x10000>;
+ interrupts = <0x0 122 0x4>;
+ clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
+ clock-names = "mem";
+ dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
+ <&sdma2 18 23 0> , <&sdma2 19 23 0>,
+ <&sdma2 20 23 0> , <&sdma2 21 23 0>,
+ <&sdma2 22 23 0> , <&sdma2 23 23 0>;
+ dma-names = "ctx0_rx", "ctx0_tx",
+ "ctx1_rx", "ctx1_tx",
+ "ctx2_rx", "ctx2_tx",
+ "ctx3_rx", "ctx3_tx";
+ firmware-name = "imx/easrc/easrc-imx8mn.bin";
+ fsl,asrc-rate = <8000>;
+ fsl,asrc-format = <2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ playback-only;
+ easrc1_endpoint0: endpoint {
+ remote-endpoint = <&fe00_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ capture-only;
+ easrc1_endpoint1: endpoint {
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ capture-only;
+ easrc1_endpoint2: endpoint {
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ capture-only;
+ easrc1_endpoint3: endpoint {
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH 3/4] ASoC: fsl_asrc: expose individual DAIs per conversion pair
From: shengjiu.wang @ 2026-07-20 9:54 UTC (permalink / raw)
To: lgirdwood, broonie, robh, krzk+dt, conor+dt, Frank.Li, s.hauer,
kernel, festevam, shawnguo, linux-sound, devicetree, imx,
linux-arm-kernel, linux-kernel, shengjiu.wang, Xiubo.Lee,
nicoleotsuka, perex, tiwai, linuxppc-dev
Cc: Shengjiu Wang
In-Reply-To: <20260720095450.1618747-1-shengjiu.wang@oss.nxp.com>
From: Shengjiu Wang <shengjiu.wang@nxp.com>
The i.MX ASRC hardware supports three independent conversion pairs
(A, B, C). The driver previously registered a single DAI with generic
stream names "ASRC-Playback" and "ASRC-Capture", which prevents
individual pairs from being routed to separate audio paths simultaneously.
Replace the single fsl_asrc_dai instance with an array of three DAI
drivers, one per pair:
paira -- ASRC-Playback / ASRC-Capture (backward compatible)
pairb -- ASRCB-Playback / ASRCB-Capture
pairc -- ASRCC-Playback / ASRCC-Capture
Pair A retains the original generic stream names to preserve backward
compatibility with existing machine drivers and board configurations.
Each DAI retains the same channel, rate and format capabilities as the
original. Update the devm_snd_soc_register_component() call to register
all three DAIs.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl_asrc.c | 89 +++++++++++++++++++++++++++++++---------
1 file changed, 69 insertions(+), 20 deletions(-)
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 0b28bcfa47fe..f83d6cdc6412 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -803,27 +803,76 @@ static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE)
-static struct snd_soc_dai_driver fsl_asrc_dai = {
- .playback = {
- .stream_name = "ASRC-Playback",
- .channels_min = 1,
- .channels_max = 10,
- .rate_min = 5512,
- .rate_max = 192000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_ASRC_FORMATS |
- SNDRV_PCM_FMTBIT_S8,
+static struct snd_soc_dai_driver fsl_asrc_dai[] = {
+ {
+ .name = "paira",
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
+ },
+ {
+ .name = "pairb",
+ .playback = {
+ .stream_name = "ASRCB-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "ASRCB-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
},
- .capture = {
- .stream_name = "ASRC-Capture",
- .channels_min = 1,
- .channels_max = 10,
- .rate_min = 5512,
- .rate_max = 192000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_ASRC_FORMATS,
+ {
+ .name = "pairc",
+ .playback = {
+ .stream_name = "ASRCC-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "ASRCC-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
},
- .ops = &fsl_asrc_dai_ops,
};
static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
@@ -1404,7 +1453,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
goto err_pm_get_sync;
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
- &fsl_asrc_dai, 1);
+ fsl_asrc_dai, ARRAY_SIZE(fsl_asrc_dai));
if (ret) {
dev_err(&pdev->dev, "failed to register ASoC DAI\n");
goto err_pm_get_sync;
--
2.34.1
^ permalink raw reply related
* [PATCH 4/4] ASoC: fsl_easrc: expose individual DAIs per conversion context
From: shengjiu.wang @ 2026-07-20 9:54 UTC (permalink / raw)
To: lgirdwood, broonie, robh, krzk+dt, conor+dt, Frank.Li, s.hauer,
kernel, festevam, shawnguo, linux-sound, devicetree, imx,
linux-arm-kernel, linux-kernel, shengjiu.wang, Xiubo.Lee,
nicoleotsuka, perex, tiwai, linuxppc-dev
Cc: Shengjiu Wang
In-Reply-To: <20260720095450.1618747-1-shengjiu.wang@oss.nxp.com>
From: Shengjiu Wang <shengjiu.wang@nxp.com>
The i.MX EASRC hardware supports four independent conversion contexts
(A, B, C, D). The driver previously registered a single DAI with generic
stream names "ASRC-Playback" and "ASRC-Capture", which prevents
individual contexts from being routed to separate audio paths
simultaneously.
Replace the single fsl_easrc_dai instance with an array of four DAI
drivers, one per context:
ctxa -- ASRC-Playback / ASRC-Capture (backward compatible)
ctxb -- ASRCB-Playback / ASRCB-Capture
ctxc -- ASRCC-Playback / ASRCC-Capture
ctxd -- ASRCD-Playback / ASRCD-Capture
Context A retains the original generic stream names to preserve backward
compatibility with existing machine drivers and board configurations.
Each DAI retains the same channel, rate and format capabilities as the
original, including IEC958 subframe support on the capture side. Update
the devm_snd_soc_register_component() call to register all four DAIs.
Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl_easrc.c | 112 +++++++++++++++++++++++++++++++-------
1 file changed, 92 insertions(+), 20 deletions(-)
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index edfd943197a0..3c5303d9b9cf 100644
--- a/sound/soc/fsl/fsl_easrc.c
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -1600,27 +1600,99 @@ static const struct snd_soc_dai_ops fsl_easrc_dai_ops = {
.hw_free = fsl_easrc_hw_free,
};
-static struct snd_soc_dai_driver fsl_easrc_dai = {
- .playback = {
- .stream_name = "ASRC-Playback",
- .channels_min = 1,
- .channels_max = 32,
- .rate_min = 8000,
- .rate_max = 768000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_EASRC_FORMATS,
+static struct snd_soc_dai_driver fsl_easrc_dai[] = {
+ {
+ .name = "ctxa",
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
},
- .capture = {
- .stream_name = "ASRC-Capture",
- .channels_min = 1,
- .channels_max = 32,
- .rate_min = 8000,
- .rate_max = 768000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_EASRC_FORMATS |
- SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ {
+ .name = "ctxb",
+ .playback = {
+ .stream_name = "ASRCB-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRCB-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
+ },
+ {
+ .name = "ctxc",
+ .playback = {
+ .stream_name = "ASRCC-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRCC-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
+ },
+ {
+ .name = "ctxd",
+ .playback = {
+ .stream_name = "ASRCD-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRCD-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
},
- .ops = &fsl_easrc_dai_ops,
};
static const struct snd_soc_component_driver fsl_easrc_component = {
@@ -2245,7 +2317,7 @@ static int fsl_easrc_probe(struct platform_device *pdev)
regcache_cache_only(easrc->regmap, true);
ret = devm_snd_soc_register_component(dev, &fsl_easrc_component,
- &fsl_easrc_dai, 1);
+ fsl_easrc_dai, ARRAY_SIZE(fsl_easrc_dai));
if (ret) {
dev_err(dev, "failed to register ASoC DAI\n");
goto err_pm_disable;
--
2.34.1
^ permalink raw reply related
* Re: [RFC PATCH v1 1/8] misc/arm-cla: Add driver skeleton and documentation
From: Ryan Roberts @ 2026-07-20 9:54 UTC (permalink / raw)
To: Arnd Bergmann, Greg Kroah-Hartman, Catalin Marinas, Will Deacon,
Mark Rutland, Jean-Philippe Brucker, Oded Gabbay, Jonathan Corbet
Cc: linux-kernel, linux-arm-kernel, dri-devel, linux-doc
In-Reply-To: <2732dc3b-e3b0-4df6-9b63-f0de4eaca993@app.fastmail.com>
On 17/07/2026 17:10, Arnd Bergmann wrote:
> On Fri, Jul 17, 2026, at 17:44, Ryan Roberts wrote:
>> On 17/07/2026 14:49, Arnd Bergmann wrote:
>>> On Fri, Jul 17, 2026, at 12:47, Ryan Roberts wrote:
>>> This sounds a lot like st64bv or st64bv0, passing an 8-word payload and returning
>>> a single word per accelerator operation with shared addressing.
>>
>> We're actually passing 9 words here; 8 DATA words plus the LAUNCH word. CLA
>> supports only 64 bit aligned and sized accesses (other accesses are RAZ/WI) so
>> they have to be written as 9x64bit stores. Then poll using 64bit loads.
>>
>>>
>>> Why are there now two interfaces to do the same thing?
>>
>> Good question. This is how the HW operates.
>>
>>>
>>> Can a user process use st64bv to do the four steps in a
>>> single instruction?
>>
>> No, unfortunately not.
>>
>
> Ok
>
>>> Can a CPU start multiple asynchronous operations concurrently?
>>
>> Yes; STATUS indicates READY while it can accept more asynchronous operations
>> ("comamnds").
>
> How does userspace know which operations have already completed
> then? (not worried about this bit, just trying to understand)
There is nothing architected in the CLA to determine this; You can determine
READY (i.e. can accept new commands) and IDLE (i.e. completed all previously
submitted commands). If you want to know when a specific command completed
without having to wait for all commands to complete, then you need extra
knowledge of the specific accelerator.
>
>>> Do these continue to run if the starting process is scheduled out
>>> and another process also tries to use CLA?
>>
>> Yes; the driver manages assignment of a CLA to a process context completely
>> separately from the thread scheduler's decisions about which threads run on
>> which CPUs and when. If another process is scheduled onto the CPU and it
>> attempts to access it's VA for the CLA, it will fault into the driver's handler
>> and be put to sleep until the driver decides to reassign the CLA.
>
> This part does sound dangerous, not in the sense that I think it's
> fundamentally broken, but in the complexity it adds.
Can you describe more about the complexity you see here? The intention is to
_reduce_ complexity by not coupling with the CPU scheduler.
>
> I wonder if it's feasible to simplify this by always canceling
> any ongoing CLA operations during switch_mm():
The advice I've had so far is to avoid at all costs, coupling the CLA context
switch with the CPU context switch; there are concerns about the latency
impliciaitons for the CLA context switch and also a desire not to couple the CLA
device driver with arch code.
>
> Is there an upper bound on how long a single operation can take,
> or a guarantee that an operation at least provides a partial result
> in hardware?
For the compute accelerator we have, operations are gauranteed to complete in
finite time, but that time could technically be very large. In practice
operations for known use cases range from a few microseconds to ~10 milliseconds.
As I described in a previous response, the accelerator does not support full
save/restore so if it is interrupted while running an operation, the operation
appears aborted when the state is next restored. Due to forward progress
concerns, we are looking at the possibility of a grace period (of the order of a
few milliseconds) during CLA reassignment. I'm pretty sure we wouldn't want to
insert that into the (non-preemptible) CPU context switch path...
>
> From your earlier descriptions, it sounds like the CPU is usually
> assumed to wait for completion with WFE anyway, so from the
> scheduler's perspective, the thread is active while waiting for
> the accelerator to complete a job (even if from hardware side
> the CPU is powered down during WFE).
In practice the CPU will have some parallel work to prepare the next
operation(s) for submission, but yes, eventually it will enter a polling loop
waiting for accelarator completion with WFE.
>
> If this is how it generally operates, and the accelerator jobs
> are usually fast, forcing the CLA TTBR0 to be the same as the
> CPU TTBR0 would avoid the entire problem of unmapping the registers
> on context switch, but instead let this hook into the same place
> as the corresponding iommu_mm_data switch on x86, which seems> to handle this
more nicely.
>
I agree this feels like a much simpler model on the surface but because the jobs
are not always fast and because the accelerator doesn't support full
save/restore, I don't think this approach flies in practice.
Thanks,
Ryan
>> I'm not sure what you mean by "Which instruction specifically causes the fault".
>> A fault occurs within the accelerator if it tries to access a virtual address
>> that is not mapped by the page table or if the permissions of the mapping are
>> not sufficient, etc... The fact that the accelerator has faulted is reported to
>> the SW that is polling the accelerator's STATUS register within user space. That
>> SW is expected to trigger fault handling by the usual kernel mechanisms by
>> accessing the VA. Then it issues a RESOLVE operation to tell the accelerator it
>> can continue.
>
> Got it now. I had assumed that the page fault is delivered asynchronously
> to the kernel without user space getting involved. In this case, I think
> the interface is actually cleaner, though it does add a little bit
> of overhead for userspace having to decipher the status.
>
>>>> +User space software is expected to create a thread to drive each CLA it is
>>>> +using, and for each thread to be pinned to the CLA's local CPU.
>>>
>>> What happens if multiple processes have the same chardev open and
>>> each mmap() that, e.g. after a fork()? Does each process see its
>>> own virtual instance of the accelerator and interact with it through
>>> the same physical MMIO register range but its own process address space,
>>> or do you have to rely on the registers being mapped only into a
>>> single mm_struct to prevent a process from messing with another process
>>> data?
>>
>> The driver maintains a cla_ctx for each {file description, mm_struct} pair. So
>> in this case, even though the file description is shared between the parent and
>> child processes, they still have distinct mm_structs so still have separate
>> contexts allowing the driver to virtualize access correctly.
>
> Ok, got it.
>
> Arnd
^ permalink raw reply
* [PATCH v13 0/3] media: nxp: Add CSI Pixel Formatter support
From: guoniu.zhou @ 2026-07-20 9:59 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Loic Poulain,
Bryan O'Donoghue, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou, Frank Li, Krzysztof Kozlowski
CSI Pixel Formatter is a module found on i.MX95. It could unpack the
pixels received by the formatter and reformat them to meet the pixel
link format requirement.
This patch series adds a new V4L2 driver for CSI Pixel Formatter.
Background
----------
The Camera CSR binding was upstreamed during the pre-silicon phase when
the hardware integration details were not fully finalized. At that time,
the syscon binding focused on the CSR's primary role as a shared register
provider for clock gating, QoS, and error handling across Camera domain IPs.
However, the Camera CSR hardware is actually a mixed-function register
container with varying control levels over different IP blocks:
- ISP/CSI: Clock gating only
- ISI: QoS configuration
- CSI: Buffer overflow handling
- LPCAC: Error handling only
- ISP: Pixel link selection
- Formatter: Complete IP control (full register set)
Why Child Nodes Now
-------------------
The CSI formatter is unique among these IPs - it's an independent hardware
block whose complete control registers reside within the CSR address
space, not just a few configuration bits. This architectural detail was
not reflected in the original binding because:
1. The pre-silicon binding focused on simple register-level controls
2. The syscon pattern for complete IP control was not initially considered
Discussion with original CSR author(Peng Fan) confirmed this reflects the
intended hardware design that wasn't fully captured during pre-silicon
upstreaming.
v4l2-compliance 1.28.1-5233, 64 bits, 64-bit time_t
v4l2-compliance SHA: fc15e229d9d3 2024-07-23 19:22:15
Compliance test for device /dev/v4l-subdev9:
Driver Info:
Driver version : 7.1.0
Capabilities : 0x00000002
Streams Support
Client Capabilities: 0x0000000000000003
streams interval-uses-which
Required ioctls:
test VIDIOC_SUDBEV_QUERYCAP: OK
test invalid ioctls: OK
Allow for multiple opens:
test second /dev/v4l-subdev9 open: OK
test VIDIOC_SUBDEV_QUERYCAP: OK
test for unlimited opens: OK
Debug ioctls:
test VIDIOC_LOG_STATUS: OK (Not Supported)
Input ioctls:
test VIDIOC_G/S_TUNER/ENUM_FREQ_BANDS: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_S_HW_FREQ_SEEK: OK (Not Supported)
test VIDIOC_ENUMAUDIO: OK (Not Supported)
test VIDIOC_G/S/ENUMINPUT: OK (Not Supported)
test VIDIOC_G/S_AUDIO: OK (Not Supported)
Inputs: 0 Audio Inputs: 0 Tuners: 0
Output ioctls:
test VIDIOC_G/S_MODULATOR: OK (Not Supported)
test VIDIOC_G/S_FREQUENCY: OK (Not Supported)
test VIDIOC_ENUMAUDOUT: OK (Not Supported)
test VIDIOC_G/S/ENUMOUTPUT: OK (Not Supported)
test VIDIOC_G/S_AUDOUT: OK (Not Supported)
Outputs: 0 Audio Outputs: 0 Modulators: 0
Input/Output configuration ioctls:
test VIDIOC_ENUM/G/S/QUERY_STD: OK (Not Supported)
test VIDIOC_ENUM/G/S/QUERY_DV_TIMINGS: OK (Not Supported)
test VIDIOC_DV_TIMINGS_CAP: OK (Not Supported)
test VIDIOC_G/S_EDID: OK (Not Supported)
Sub-Device routing ioctls:
test Try VIDIOC_SUBDEV_G_ROUTING/VIDIOC_SUBDEV_S_ROUTING: OK
test Active VIDIOC_SUBDEV_G_ROUTING/VIDIOC_SUBDEV_S_ROUTING: OK
Control ioctls:
test VIDIOC_QUERY_EXT_CTRL/QUERYMENU: OK (Not Supported)
test VIDIOC_QUERYCTRL: OK (Not Supported)
test VIDIOC_G/S_CTRL: OK (Not Supported)
test VIDIOC_G/S/TRY_EXT_CTRLS: OK (Not Supported)
test VIDIOC_(UN)SUBSCRIBE_EVENT/DQEVENT: OK (Not Supported)
test VIDIOC_G/S_JPEGCOMP: OK (Not Supported)
Standard Controls: 0 Private Controls: 0
Format ioctls:
test VIDIOC_ENUM_FMT/FRAMESIZES/FRAMEINTERVALS: OK (Not Supported)
test VIDIOC_G/S_PARM: OK (Not Supported)
test VIDIOC_G_FBUF: OK (Not Supported)
test VIDIOC_G_FMT: OK (Not Supported)
test VIDIOC_TRY_FMT: OK (Not Supported)
test VIDIOC_S_FMT: OK (Not Supported)
test VIDIOC_G_SLICED_VBI_CAP: OK (Not Supported)
test Cropping: OK (Not Supported)
test Composing: OK (Not Supported)
test Scaling: OK (Not Supported)
Codec ioctls:
test VIDIOC_(TRY_)ENCODER_CMD: OK (Not Supported)
test VIDIOC_G_ENC_INDEX: OK (Not Supported)
test VIDIOC_(TRY_)DECODER_CMD: OK (Not Supported)
Buffer ioctls:
test VIDIOC_REQBUFS/CREATE_BUFS/QUERYBUF: OK (Not Supported)
test CREATE_BUFS maximum buffers: OK
test VIDIOC_REMOVE_BUFS: OK
test VIDIOC_EXPBUF: OK (Not Supported)
test Requests: OK (Not Supported)
Total for device /dev/v4l-subdev9: 47, Succeeded: 47, Failed: 0, Warnings: 0
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
---
Changes in v13:
- [2/3] Add Reviewed-by tag from Frank and Krzysztof
- [3/3] Add Reviewed-by tag from Frank
- [3/3] Replace pr_warn_once() with dev_warn() in csi_formatter_get_index_by_dt()
to provide device context and warn on every occurrence (Loic)
- [3/3] Add WARN_ON() check for csi_formatter_find_format() return value in
start/stop_stream functions to catch unexpected NULL (Loic)
- [3/3] Use regmap_set_bits()/regmap_clear_bits() instead of read-modify-write
pattern and remove unused csi_formatter_read/write helper functions (Loic)
- Link to v12: https://lore.kernel.org/r/20260716-csi_formatter-v12-0-2601d17fc4af@oss.nxp.com
Changes in v12:
- Fix stream ID handling: iterate routing table instead of assuming
stream ID equals loop index (0-7)
- Remove stream_to_vc[] array: derive VC from routing table and frame
descriptor on each start/stop operation
- Remove V4L2_SUBDEV_FL_HAS_EVENTS flag since driver does not generate events
- Support stream IDs 0-63 by using BIT_ULL() for stream masks
- Add get_frame_desc call in stop_stream with proper error handling
- Add csi_formatter_read() helper function for register reads
- Use read-modify-write for CSI_VC_PIXEL_DATA_TYPE register to support
multiplexed streams sharing the same virtual channel
- Use route->sink_pad instead of hardcoded CSI_FORMATTER_PAD_SINK
- Write back coerced format in set_fmt before propagating to source stream
- Drop Frank's Reviewed-by tag due to significant changes, requesting re-review
- Link to v11: https://lore.kernel.org/r/20260623-csi_formatter-v11-0-a792fe9c1502@oss.nxp.com
Changes in v11:
- Drop [PATCH v10 1/4] dt-bindings: clock: imx95-blk-ctl: Use single quotes consistently (Krzysztof)
- Move properties to top-level and use if:then:else (Krzysztof/Frank)
- Link to v10: https://lore.kernel.org/r/20260618-csi_formatter-v10-0-f23830312ba5@oss.nxp.com
Changes in v10:
- Rebase to latest media/next
- [NEW PATCH] Use single quotes consistently (Krzysztof Kozlowski)
- Drop syscon parent node from example
- Drop Reviewed-by tags from Frank and Krzysztof due to binding changes,
requesting re-review
- Add description for reg property
- Add space after formatter@20 before opening brace in example
- Enhance the port description with more detailed information
- Delete the blank line immediately following the endpoint in example
- Use single quotes for regex pattern to be consistent (Krzysztof Kozlowski)
- Add formatter subnode binding and camera-csr syscon example
- Update commit title and message
- Use u8 for vc in csi_formatter_get_vc() and drop vc < 0 check
- Add MFD_SYSCON dependency to Kconfig
- Fix stream/VC mapping potential mismatch in start/stop_stream functions
- Link to v9: https://lore.kernel.org/r/20260526-csi_formatter-v9-0-ca3d8c334c39@oss.nxp.com
Changes in v9:
- [NEW PATCH] Fix formatter as syscon child node issue
- Link to v8: https://lore.kernel.org/r/20260525-csi_formatter-v8-0-6b646231224b@oss.nxp.com
Changes in v8:
- Rebase to latest media/next
- Use standard port reference instead of video-interfaces.yaml
- Add parent syscon node in example to show device integration
- Remove fmt field and look up format from subdev state instead
- Unify function and structure naming to use csi_formatter_ prefix
- Remove misleading alignment comment from set_fmt function
- Optimize get_frame_desc to call once per start_stream
- Replace V4L2_FRAME_DESC_ENTRY_MAX with CSI_FORMATTER_VC_NUM in loops
- Remove redundant debug message in enable_streams
- Use MEDIA_PAD_FL_MUST_CONNECT flag instead of manual link check
- Link to v7: https://lore.kernel.org/r/20260518-csi_formatter-v7-0-562b750557e3@oss.nxp.com
Changes in v7:
- Change compatible to imx95-csi-formatter as IP is i.MX95 specific per Marco's suggestion
Link: https://lore.kernel.org/linux-media/20260511-csi_formatter-v6-0-01028e312e2b@oss.nxp.com/T/#mcd135b3de179b3cb69daa1fd6e0e8e27c85b3332
- Update references from imx9 to imx95 for consistency with dt-bindings
- Enable PM runtime before async registration
- Link to v6: https://lore.kernel.org/r/20260511-csi_formatter-v6-0-01028e312e2b@oss.nxp.com
Changes in v6:
- Rebase to latest media/next
- Update v4l2-compliace test
- Remove unused header includes
- Unify macro naming: VCx/VCX -> VC and parameter x -> vc
- Remove unused format field from csi_formatter struct
- Use compact initialization for formats array
- Make find_csi_format() return NULL instead of default format
- Use unsigned int for array index in find_csi_format()
- Add err_ prefix to error handling labels
- Add v4l2_subdev_cleanup() and reorder cleanup sequence
- Update enable_streams debug output format
- Rename VC_MAX to VC_NUM and fix boundary check
- Update CSI formatter Kconfig description
- Use v4l2_subdev_get_frame_desc_passthrough() helper
- Fix error paths in async registration and probe
- Add mutex to protect enabled_streams
- Switch to devm_pm_runtime_enable()
- Remove redundant num_routes check in set_routing
- Optimize get_index_by_dt() and add warning for unsupported type
- csi_formatter_start/stop_stream: Process all streams in mask
- Link to v5: https://lore.kernel.org/r/20260123-csi_formatter-v5-0-d5b803f867bf@nxp.com
Changes in v5:
- Remove CSI_FORMATTER_DRV_NAME macro since only use once.
- Remove sd->owner = THIS_MODULE;
- Simplify code by using DEFINE_RUNTIME_DEV_PM_OPS macro.
- Link to v4: https://lore.kernel.org/r/20260122-csi_formatter-v4-0-6f6fcad1c33a@nxp.com
Changes in v4:
- Rebase to latest media/next.
- Add comments to describe the index field in formatter_dt_to_index_map array.
- Link to v3: https://lore.kernel.org/r/20251219-csi_formatter-v3-0-8680d6d87091@nxp.com
Changes in v3:
- Rename nxp,imx9-csi-formatter.yaml to fsl,imx9-csi-formatter.yaml.
- Drop clock-names property.
- Drop macro IMX95_PD_CAMERA definition and use a constant directly.
[PATCH 1/2] media: dt-bindings: Add CSI Pixel Formatter DT bindings
- Remove the assignment driver.owner = THIS_MODULE.
- Assign struct fwnode_handle *ep __free(fwnode_handle) when definition.
- Update yaml file name for csi formatter in MAINTAINERS.
[PATCH 2/2] media: nxp: Add i.MX9 CSI pixel formatter v4l2 driver
- Link to v2: https://lore.kernel.org/r/20251217-csi_formatter-v2-0-62168af80210@nxp.com
Changes in v2:
- Delete "|" for description key.
- Add empty line between child node and property.
- Delete labels for endpoint of child nodes.
[PATCH 1/2] media: dt-bindings: Add CSI Pixel Formatter DT bindings
- Update commit message.
- Use the value defined by bellow macros directly since they are used only once.
#define CSI_FORMATTER_DEF_MBUS_CODE MEDIA_BUS_FMT_UYVY8_1X16
#define CSI_FORMATTER_DEF_PIX_WIDTH 1920U
#define CSI_FORMATTER_DEF_PIX_HEIGHT 1080U
#define CSI_FORMATTER_MAX_PIX_WIDTH 0xffff
#define CSI_FORMATTER_MAX_PIX_HEIGHT 0xffff
- Use macro pm_ptr() to fix build warning when CONFIG_PM is disabled.
- Finish route loop by break statement, instead of goto.
- Return dev_err_probe() when meet errors in probe() function instead of dev_err().
- Remove MODULE_ALIAS().
- Refine .enable(.dsable)_stream callback implementation, include bellow changes:
Add stream checking.
Fix potential pm runtime count unbalance issue.
Add stop stream error handling when enabling remote subdev stream.
- Use __free(fwnode_handle) to drop reference to a device node automatically.
[PATCH 2/2] media: nxp: Add i.MX9 CSI pixel formatter v4l2 driver
- Link to v1: https://lore.kernel.org/r/20251203-csi_formatter-v1-0-eb9e1147b49e@nxp.com
---
Guoniu Zhou (3):
media: dt-bindings: Add CSI Pixel Formatter DT bindings
dt-bindings: clock: imx95-blk-ctl: Define formatter child node schema
media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
.../bindings/clock/nxp,imx95-blk-ctl.yaml | 71 ++
.../bindings/media/fsl,imx95-csi-formatter.yaml | 88 +++
MAINTAINERS | 8 +
drivers/media/platform/nxp/Kconfig | 15 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx95-csi-formatter.c | 808 +++++++++++++++++++++
6 files changed, 991 insertions(+)
---
base-commit: 06cb687a5132fcffe624c0070576ab852ac6b568
change-id: 20251125-csi_formatter-e6d29316dce6
Best regards,
--
Guoniu Zhou <guoniu.zhou@oss.nxp.com>
^ permalink raw reply
* [PATCH v13 1/3] media: dt-bindings: Add CSI Pixel Formatter DT bindings
From: guoniu.zhou @ 2026-07-20 9:59 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Loic Poulain,
Bryan O'Donoghue, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou, Frank Li, Krzysztof Kozlowski
In-Reply-To: <20260720-csi_formatter-v13-0-4dc9a80e4cfd@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
The i.MX95 CSI pixel formatting module uses packet info, pixel and
non-pixel data from the CSI-2 host controller and reformat them to
match Pixel Link(PL) definition.
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
Changes in v11:
- Add Rb tags from Frank Li and Krzysztof Kozlowski
Changes in v10:
- Drop syscon parent node from example
- Drop Reviewed-by tags from Frank and Krzysztof due to binding changes
- Add description for reg property
- Add space after formatter@20 before opening brace in example
- Enhance the port description with more detailed information
- Delete the blank line immediately following the endpoint in example
Changes in v9:
- Use direct node instead of syscon wrapper in example
Changes in v8:
- Use standard port reference instead of video-interfaces.yaml
- Add parent syscon node in example to show device integration
- Add required constraints for port@0 and port@1 in ports node
Changes in v7:
- Change compatible to imx95-csi-formatter as IP is i.MX95 specific per Marco's suggestion
Link: https://lore.kernel.org/linux-media/20260511-csi_formatter-v6-0-01028e312e2b@oss.nxp.com/T/#mcd135b3de179b3cb69daa1fd6e0e8e27c85b3332
---
.../bindings/media/fsl,imx95-csi-formatter.yaml | 88 ++++++++++++++++++++++
1 file changed, 88 insertions(+)
diff --git a/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml b/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
new file mode 100644
index 000000000000..58c4e1cc056b
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
@@ -0,0 +1,88 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/media/fsl,imx95-csi-formatter.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: i.MX95 CSI Pixel Formatter
+
+maintainers:
+ - Guoniu Zhou <guoniu.zhou@nxp.com>
+
+description:
+ The CSI pixel formatting module found on i.MX95 uses packet info, pixel
+ and non-pixel data from the CSI-2 host controller and reformat them to
+ match Pixel Link(PL) definition.
+
+properties:
+ compatible:
+ const: fsl,imx95-csi-formatter
+
+ reg:
+ maxItems: 1
+ description: Register offset and size within the parent syscon
+
+ clocks:
+ maxItems: 1
+
+ power-domains:
+ maxItems: 1
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Input port, connects to MIPI CSI-2 receiver output (IDI interface)
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description:
+ Output port, connects to ISI input via Pixel Link (PL)
+
+ required:
+ - port@0
+ - port@1
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - power-domains
+ - ports
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ formatter@20 {
+ compatible = "fsl,imx95-csi-formatter";
+ reg = <0x20 0x100>;
+ clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
+ power-domains = <&scmi_devpd 3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mipi_csi_0_out>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&isi_in_2>;
+ };
+ };
+ };
+ };
--
2.34.1
^ permalink raw reply related
* [PATCH v13 2/3] dt-bindings: clock: imx95-blk-ctl: Define formatter child node schema
From: guoniu.zhou @ 2026-07-20 9:59 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Loic Poulain,
Bryan O'Donoghue, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou, Frank Li, Krzysztof Kozlowski
In-Reply-To: <20260720-csi_formatter-v13-0-4dc9a80e4cfd@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
The Camera CSR contains control registers for multiple CSI formatter IPs
at different register offsets. Each formatter is an independent hardware
block with its own clock input and media pipeline connection.
Define schema to allow formatter child nodes under nxp,imx95-camera-csr,
with 'reg' property specifying the formatter's register offset within the
CSR address space.
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
Changes in v13:
- Add Reviewed-by tag from Frank and Krzysztof
Changes in v11:
- Move properties to top-level and use if:then:else (Krzysztof/Frank)
Changes in v10:
- Use single quotes for regex pattern to be consistent (Krzysztof Kozlowski)
- Add formatter subnode binding and camera-csr syscon example
- Update commit title and message
Changes in v9:
- New patch to address the issue of formatter acting as a child node of syscon
---
.../bindings/clock/nxp,imx95-blk-ctl.yaml | 71 ++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
index 27403b4c52d6..fbbf1b3f1790 100644
--- a/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
+++ b/Documentation/devicetree/bindings/clock/nxp,imx95-blk-ctl.yaml
@@ -39,6 +39,18 @@ properties:
ID in its "clocks" phandle cell. See
include/dt-bindings/clock/nxp,imx95-clock.h
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 1
+
+patternProperties:
+ '^formatter@[0-9a-f]+$':
+ type: object
+ $ref: /schemas/media/fsl,imx95-csi-formatter.yaml#
+ unevaluatedProperties: false
+
required:
- compatible
- reg
@@ -46,6 +58,23 @@ required:
- power-domains
- clocks
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: nxp,imx95-camera-csr
+ then:
+ required:
+ - '#address-cells'
+ - '#size-cells'
+ else:
+ properties:
+ '#address-cells': false
+ '#size-cells': false
+ patternProperties:
+ '^formatter@[0-9a-f]+$': false
+
additionalProperties: false
examples:
@@ -57,4 +86,46 @@ examples:
clocks = <&scmi_clk 114>;
power-domains = <&scmi_devpd 21>;
};
+
+ - |
+ #include <dt-bindings/clock/nxp,imx95-clock.h>
+
+ syscon@4ac10000 {
+ compatible = "nxp,imx95-camera-csr", "syscon";
+ reg = <0x4ac10000 0x10000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ #clock-cells = <1>;
+ clocks = <&scmi_clk 62>;
+ power-domains = <&scmi_devpd 3>;
+
+ formatter@20 {
+ compatible = "fsl,imx95-csi-formatter";
+ reg = <0x20 0x100>;
+ clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
+ power-domains = <&scmi_devpd 3>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ endpoint {
+ remote-endpoint = <&mipi_csi_0_out>;
+ };
+
+ };
+
+ port@1 {
+ reg = <1>;
+
+ endpoint {
+ remote-endpoint = <&isi_in_2>;
+ };
+ };
+ };
+ };
+ };
...
--
2.34.1
^ permalink raw reply related
* [PATCH v13 3/3] media: nxp: Add i.MX95 CSI pixel formatter v4l2 driver
From: guoniu.zhou @ 2026-07-20 9:59 UTC (permalink / raw)
To: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, Loic Poulain,
Bryan O'Donoghue, Abel Vesa, Peng Fan, Michael Turquette,
Stephen Boyd
Cc: imx, linux-media, devicetree, linux-arm-kernel, linux-kernel,
linux-clk, Guoniu Zhou, Frank Li
In-Reply-To: <20260720-csi_formatter-v13-0-4dc9a80e4cfd@oss.nxp.com>
From: Guoniu Zhou <guoniu.zhou@nxp.com>
The CSI pixel formatter is a module found on i.MX95 used to reformat
packet info, pixel and non-pixel data from CSI-2 host controller to
match Pixel Link(PL) definition.
Add data formatting support.
Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v13:
- Replace pr_warn_once() with dev_warn() in csi_formatter_get_index_by_dt()
to provide device context and warn on every occurrence (Loic)
- Add WARN_ON() check for csi_formatter_find_format() return value in
start/stop_stream functions to catch unexpected NULL (Loic)
- Use regmap_set_bits()/regmap_clear_bits() instead of read-modify-write
pattern and remove unused csi_formatter_read/write helper functions (Loic)
- Add Reviewed-by tag from Frank Li
Changes in v12:
- Fix stream ID handling: iterate routing table instead of assuming
stream ID equals loop index (0-7)
- Remove stream_to_vc[] array: derive VC from routing table and frame
descriptor on each start/stop operation
- Remove V4L2_SUBDEV_FL_HAS_EVENTS flag since driver does not generate events
- Support stream IDs 0-63 by using BIT_ULL() for stream masks
- Add get_frame_desc call in stop_stream with proper error handling
- Add csi_formatter_read() helper function for register reads
- Use read-modify-write for CSI_VC_PIXEL_DATA_TYPE register to support
multiplexed streams sharing the same virtual channel
- Use route->sink_pad instead of hardcoded CSI_FORMATTER_PAD_SINK
- Write back coerced format in set_fmt before propagating to source stream
- Drop Frank's Reviewed-by tag due to significant changes, requesting re-review
Changes in v10:
- Use u8 for vc in csi_formatter_get_vc() and drop vc < 0 check
- Add MFD_SYSCON dependency to Kconfig
- Fix stream/VC mapping potential mismatch in start/stop_stream functions
Changes in v8:
- Remove fmt field and look up format from subdev state instead
- Unify function and structure naming to use csi_formatter_ prefix
- Remove misleading alignment comment from set_fmt function
- Optimize get_frame_desc to call once per start_stream
- Replace V4L2_FRAME_DESC_ENTRY_MAX with CSI_FORMATTER_VC_NUM in loops
- Remove redundant debug message in enable_streams
- Use MEDIA_PAD_FL_MUST_CONNECT flag instead of manual link check
- Fix typo: Formater -> Formatter in Kconfig help text
- Improve grammar in data type index mapping comment
Changes in v7:
- Update references from imx9 to imx95 for consistency with dt-bindings
- Enable PM runtime before async registration
Changes in v6:
- Remove unused header includes
- Unify macro naming: VCx/VCX -> VC and parameter x -> vc
- Remove unused format field from csi_formatter struct
- Use compact initialization for formats array
- Make find_csi_format() return NULL instead of default format
- Use unsigned int for array index in find_csi_format()
- Add err_ prefix to error handling labels
- Add v4l2_subdev_cleanup() and reorder cleanup sequence
- Update enable_streams debug output format
- Rename VC_MAX to VC_NUM and fix boundary check
- Update CSI formatter Kconfig description
- Use v4l2_subdev_get_frame_desc_passthrough() helper
- Fix error paths in async registration and probe
- Add mutex to protect enabled_streams
- Switch to devm_pm_runtime_enable()
- Remove redundant num_routes check in set_routing
- Optimize get_index_by_dt() and add warning for unsupported type
- csi_formatter_start/stop_stream: Process all streams in mask
---
MAINTAINERS | 8 +
drivers/media/platform/nxp/Kconfig | 15 +
drivers/media/platform/nxp/Makefile | 1 +
drivers/media/platform/nxp/imx95-csi-formatter.c | 808 +++++++++++++++++++++++
4 files changed, 832 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index efbf808063e5..05009228b162 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -19275,6 +19275,14 @@ S: Maintained
F: Documentation/devicetree/bindings/media/nxp,imx8-jpeg.yaml
F: drivers/media/platform/nxp/imx-jpeg
+NXP i.MX 95 CSI PIXEL FORMATTER V4L2 DRIVER
+M: Guoniu Zhou <guoniu.zhou@nxp.com>
+L: imx@lists.linux.dev
+L: linux-media@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/media/fsl,imx95-csi-formatter.yaml
+F: drivers/media/platform/nxp/imx95-csi-formatter.c
+
NXP i.MX CLOCK DRIVERS
M: Abel Vesa <abelvesa@kernel.org>
R: Peng Fan <peng.fan@nxp.com>
diff --git a/drivers/media/platform/nxp/Kconfig b/drivers/media/platform/nxp/Kconfig
index 40e3436669e2..8f49908b0022 100644
--- a/drivers/media/platform/nxp/Kconfig
+++ b/drivers/media/platform/nxp/Kconfig
@@ -28,6 +28,21 @@ config VIDEO_IMX8MQ_MIPI_CSI2
Video4Linux2 driver for the MIPI CSI-2 receiver found on the i.MX8MQ
SoC.
+config VIDEO_IMX95_CSI_FORMATTER
+ tristate "NXP i.MX95 CSI Pixel Formatter driver"
+ depends on ARCH_MXC || COMPILE_TEST
+ depends on MFD_SYSCON
+ depends on VIDEO_DEV
+ select MEDIA_CONTROLLER
+ select V4L2_FWNODE
+ select VIDEO_V4L2_SUBDEV_API
+ help
+ This driver provides support for the CSI Pixel Formatter found on
+ i.MX95 series SoCs. This module unpacks the pixels received from the
+ CSI-2 interface and reformats them to meet pixel link requirements.
+
+ Say Y here to enable CSI Pixel Formatter module for i.MX95 SoC.
+
config VIDEO_IMX_MIPI_CSIS
tristate "NXP MIPI CSI-2 CSIS receiver found on i.MX7 and i.MX8 models"
depends on ARCH_MXC || COMPILE_TEST
diff --git a/drivers/media/platform/nxp/Makefile b/drivers/media/platform/nxp/Makefile
index 4d90eb713652..6410115d870e 100644
--- a/drivers/media/platform/nxp/Makefile
+++ b/drivers/media/platform/nxp/Makefile
@@ -6,6 +6,7 @@ obj-y += imx8-isi/
obj-$(CONFIG_VIDEO_IMX7_CSI) += imx7-media-csi.o
obj-$(CONFIG_VIDEO_IMX8MQ_MIPI_CSI2) += imx8mq-mipi-csi2.o
+obj-$(CONFIG_VIDEO_IMX95_CSI_FORMATTER) += imx95-csi-formatter.o
obj-$(CONFIG_VIDEO_IMX_MIPI_CSIS) += imx-mipi-csis.o
obj-$(CONFIG_VIDEO_IMX_PXP) += imx-pxp.o
obj-$(CONFIG_VIDEO_MX2_EMMAPRP) += mx2_emmaprp.o
diff --git a/drivers/media/platform/nxp/imx95-csi-formatter.c b/drivers/media/platform/nxp/imx95-csi-formatter.c
new file mode 100644
index 000000000000..b0e8e753e94a
--- /dev/null
+++ b/drivers/media/platform/nxp/imx95-csi-formatter.c
@@ -0,0 +1,808 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <media/mipi-csi2.h>
+#include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
+#include <media/v4l2-mc.h>
+#include <media/v4l2-subdev.h>
+
+/* CSI Pixel Formatter registers map */
+
+#define CSI_VC_INTERLACED_LINE_CNT(vc) (0x00 + (vc) * 0x04)
+#define INTERLACED_ODD_LINE_CNT_SET(x) FIELD_PREP(GENMASK(13, 0), (x))
+#define INTERLACED_EVEN_LINE_CNT_SET(x) FIELD_PREP(GENMASK(29, 16), (x))
+
+#define CSI_VC_INTERLACED_CTRL 0x20
+
+#define CSI_VC_INTERLACED_ERR 0x24
+#define CSI_VC_ERR_MASK GENMASK(7, 0)
+#define CSI_VC_ERR(vc) BIT((vc))
+
+#define CSI_VC_YUV420_FIRST_LINE_EVEN 0x28
+#define YUV420_FIRST_LINE_EVEN(vc) BIT((vc))
+
+#define CSI_RAW32_CTRL 0x30
+#define CSI_VC_RAW32_MODE(vc) BIT((vc))
+#define CSI_VC_RAW32_SWAP_MODE(vc) BIT((vc) + 8)
+
+#define CSI_STREAM_FENCING_CTRL 0x34
+#define CSI_VC_STREAM_FENCING(vc) BIT((vc))
+#define CSI_VC_STREAM_FENCING_RST(vc) BIT((vc) + 8)
+
+#define CSI_STREAM_FENCING_STS 0x38
+#define CSI_STREAM_FENCING_STS_MASK GENMASK(7, 0)
+
+#define CSI_VC_NON_PIXEL_DATA_TYPE(vc) (0x40 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_CTRL(vc) (0x60 + (vc) * 0x04)
+#define NEW_VC(vc) FIELD_PREP(GENMASK(3, 1), vc)
+#define REROUTE_VC_ENABLE BIT(0)
+
+#define CSI_VC_ROUTE_PIXEL_DATA_TYPE(vc) (0x80 + (vc) * 0x04)
+
+#define CSI_VC_NON_PIXEL_DATA_CTRL(vc) (0xa0 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_TYPE(vc) (0xc0 + (vc) * 0x04)
+
+#define CSI_VC_PIXEL_DATA_TYPE_ERR(vc) (0xe0 + (vc) * 0x04)
+
+#define CSI_FORMATTER_PAD_SINK 0
+#define CSI_FORMATTER_PAD_SOURCE 1
+#define CSI_FORMATTER_PAD_NUM 2
+
+#define CSI_FORMATTER_VC_NUM 8 /* Number of virtual channels */
+
+struct csi_formatter_pix_format {
+ u32 code;
+ u32 data_type;
+};
+
+struct csi_formatter {
+ struct device *dev;
+ struct regmap *regs;
+ struct clk *clk;
+
+ struct v4l2_subdev sd;
+ struct v4l2_subdev *csi_sd;
+ struct v4l2_async_notifier notifier;
+ struct media_pad pads[CSI_FORMATTER_PAD_NUM];
+
+ u32 remote_pad;
+ u32 reg_offset;
+
+ /* Protects enabled_streams */
+ struct mutex lock;
+ u64 enabled_streams;
+};
+
+struct csi_formatter_dt_index {
+ u8 dtype;
+ u8 index;
+};
+
+/*
+ * The index corresponds to the bit index in the register that enables
+ * the data type of pixel data transported by the Formatter.
+ */
+static const struct csi_formatter_dt_index formatter_dt_to_index_map[] = {
+ { .dtype = MIPI_CSI2_DT_YUV420_8B, .index = 0 },
+ { .dtype = MIPI_CSI2_DT_YUV420_8B_LEGACY, .index = 2 },
+ { .dtype = MIPI_CSI2_DT_YUV422_8B, .index = 6 },
+ { .dtype = MIPI_CSI2_DT_RGB444, .index = 8 },
+ { .dtype = MIPI_CSI2_DT_RGB555, .index = 9 },
+ { .dtype = MIPI_CSI2_DT_RGB565, .index = 10 },
+ { .dtype = MIPI_CSI2_DT_RGB666, .index = 11 },
+ { .dtype = MIPI_CSI2_DT_RGB888, .index = 12 },
+ { .dtype = MIPI_CSI2_DT_RAW6, .index = 16 },
+ { .dtype = MIPI_CSI2_DT_RAW7, .index = 17 },
+ { .dtype = MIPI_CSI2_DT_RAW8, .index = 18 },
+ { .dtype = MIPI_CSI2_DT_RAW10, .index = 19 },
+ { .dtype = MIPI_CSI2_DT_RAW12, .index = 20 },
+ { .dtype = MIPI_CSI2_DT_RAW14, .index = 21 },
+ { .dtype = MIPI_CSI2_DT_RAW16, .index = 22 },
+};
+
+static const struct csi_formatter_pix_format formats[] = {
+ /* YUV formats */
+ { MEDIA_BUS_FMT_UYVY8_1X16, MIPI_CSI2_DT_YUV422_8B },
+ /* RGB formats */
+ { MEDIA_BUS_FMT_RGB565_1X16, MIPI_CSI2_DT_RGB565 },
+ { MEDIA_BUS_FMT_RGB888_1X24, MIPI_CSI2_DT_RGB888 },
+ /* RAW (Bayer and greyscale) formats */
+ { MEDIA_BUS_FMT_SBGGR8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_Y8_1X8, MIPI_CSI2_DT_RAW8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_Y10_1X10, MIPI_CSI2_DT_RAW10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_Y12_1X12, MIPI_CSI2_DT_RAW12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, MIPI_CSI2_DT_RAW14 },
+ { MEDIA_BUS_FMT_SBGGR16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SGBRG16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SGRBG16_1X16, MIPI_CSI2_DT_RAW16 },
+ { MEDIA_BUS_FMT_SRGGB16_1X16, MIPI_CSI2_DT_RAW16 },
+};
+
+static const struct v4l2_mbus_framefmt formatter_default_fmt = {
+ .code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .width = 1920U,
+ .height = 1080U,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
+ .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SMPTE170M),
+ .quantization = V4L2_QUANTIZATION_LIM_RANGE,
+};
+
+static const struct csi_formatter_pix_format *csi_formatter_find_format(u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (code == formats[i].code)
+ return &formats[i];
+
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * V4L2 subdev operations
+ */
+
+static inline struct csi_formatter *sd_to_formatter(struct v4l2_subdev *sdev)
+{
+ return container_of(sdev, struct csi_formatter, sd);
+}
+
+static int __csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ struct v4l2_subdev_krouting *routing)
+{
+ int ret;
+
+ ret = v4l2_subdev_routing_validate(sd, routing,
+ V4L2_SUBDEV_ROUTING_ONLY_1_TO_1);
+ if (ret)
+ return ret;
+
+ return v4l2_subdev_set_routing_with_fmt(sd, state, routing,
+ &formatter_default_fmt);
+}
+
+static int csi_formatter_subdev_init_state(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state)
+{
+ struct v4l2_subdev_route routes[] = {
+ {
+ .sink_pad = CSI_FORMATTER_PAD_SINK,
+ .sink_stream = 0,
+ .source_pad = CSI_FORMATTER_PAD_SOURCE,
+ .source_stream = 0,
+ .flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
+ },
+ };
+
+ struct v4l2_subdev_krouting routing = {
+ .num_routes = ARRAY_SIZE(routes),
+ .routes = routes,
+ };
+
+ return __csi_formatter_subdev_set_routing(sd, sd_state, &routing);
+}
+
+static int csi_formatter_subdev_enum_mbus_code(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_mbus_code_enum *code)
+{
+ if (code->pad == CSI_FORMATTER_PAD_SOURCE) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (code->index > 0)
+ return -EINVAL;
+
+ fmt = v4l2_subdev_state_get_format(sd_state, code->pad,
+ code->stream);
+ code->code = fmt->code;
+ return 0;
+ }
+
+ if (code->index >= ARRAY_SIZE(formats))
+ return -EINVAL;
+
+ code->code = formats[code->index].code;
+
+ return 0;
+}
+
+static int csi_formatter_subdev_set_fmt(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_format *sdformat)
+{
+ struct csi_formatter_pix_format const *format;
+ struct v4l2_mbus_framefmt *fmt;
+
+ if (sdformat->pad == CSI_FORMATTER_PAD_SOURCE)
+ return v4l2_subdev_get_fmt(sd, sd_state, sdformat);
+
+ format = csi_formatter_find_format(sdformat->format.code);
+ if (!format)
+ format = &formats[0];
+
+ v4l_bound_align_image(&sdformat->format.width, 1, 0xffff, 2,
+ &sdformat->format.height, 1, 0xffff, 0, 0);
+
+ fmt = v4l2_subdev_state_get_format(sd_state, sdformat->pad,
+ sdformat->stream);
+ *fmt = sdformat->format;
+
+ /* Set default code if user set an invalid value */
+ fmt->code = format->code;
+ sdformat->format = *fmt;
+
+ /* Propagate the format from sink stream to source stream */
+ fmt = v4l2_subdev_state_get_opposite_stream_format(sd_state, sdformat->pad,
+ sdformat->stream);
+ if (!fmt)
+ return -EINVAL;
+
+ *fmt = sdformat->format;
+
+ return 0;
+}
+
+static int csi_formatter_subdev_set_routing(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ enum v4l2_subdev_format_whence which,
+ struct v4l2_subdev_krouting *routing)
+{
+ if (which == V4L2_SUBDEV_FORMAT_ACTIVE &&
+ media_entity_is_streaming(&sd->entity))
+ return -EBUSY;
+
+ return __csi_formatter_subdev_set_routing(sd, state, routing);
+}
+
+static u8 csi_formatter_get_index_by_dt(struct csi_formatter *formatter,
+ u8 data_type)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formatter_dt_to_index_map); ++i) {
+ const struct csi_formatter_dt_index *entry =
+ &formatter_dt_to_index_map[i];
+
+ if (data_type == entry->dtype)
+ return entry->index;
+ }
+
+ dev_warn(formatter->dev, "Unsupported data type 0x%x, using default\n",
+ data_type);
+
+ return formatter_dt_to_index_map[0].index;
+}
+
+static int csi_formatter_get_vc(struct csi_formatter *formatter,
+ struct v4l2_mbus_frame_desc *fd,
+ unsigned int stream)
+{
+ struct v4l2_mbus_frame_desc_entry *entry = NULL;
+ unsigned int i;
+ u8 vc;
+
+ for (i = 0; i < fd->num_entries; ++i) {
+ if (fd->entry[i].stream == stream) {
+ entry = &fd->entry[i];
+ break;
+ }
+ }
+
+ if (!entry) {
+ dev_err(formatter->dev,
+ "No frame desc entry for stream %u\n", stream);
+ return -EPIPE;
+ }
+
+ vc = entry->bus.csi2.vc;
+
+ if (vc >= CSI_FORMATTER_VC_NUM) {
+ dev_err(formatter->dev, "Invalid virtual channel %u\n", vc);
+ return -EINVAL;
+ }
+
+ return vc;
+}
+
+static void csi_formatter_stop_stream(struct csi_formatter *formatter,
+ struct v4l2_subdev_state *state,
+ u64 stream_mask)
+{
+ const struct csi_formatter_pix_format *pix_fmt;
+ struct v4l2_mbus_frame_desc fd = {};
+ struct v4l2_subdev_route *route;
+ struct v4l2_mbus_framefmt *fmt;
+ unsigned int reg;
+ unsigned int mask;
+ int vc;
+ int ret;
+
+ ret = v4l2_subdev_call(formatter->csi_sd, pad, get_frame_desc,
+ formatter->remote_pad, &fd);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
+ return;
+ }
+
+ for_each_active_route(&state->routing, route) {
+ if (route->source_pad != CSI_FORMATTER_PAD_SOURCE)
+ continue;
+
+ if (!(stream_mask & BIT_ULL(route->source_stream)))
+ continue;
+
+ if (ret == -ENOIOCTLCMD) {
+ /*
+ * Source doesn't implement get_frame_desc, use
+ * default VC 0
+ */
+ vc = 0;
+ } else {
+ vc = csi_formatter_get_vc(formatter, &fd,
+ route->sink_stream);
+ if (vc < 0)
+ continue;
+ }
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+
+ pix_fmt = csi_formatter_find_format(fmt->code);
+ if (WARN_ON(!pix_fmt))
+ continue;
+
+ reg = CSI_VC_PIXEL_DATA_TYPE(vc) + formatter->reg_offset;
+ mask = BIT(csi_formatter_get_index_by_dt(formatter,
+ pix_fmt->data_type));
+
+ /* Clear the data type bit to disable this VC */
+ regmap_clear_bits(formatter->regs, reg, mask);
+ }
+}
+
+static int csi_formatter_start_stream(struct csi_formatter *formatter,
+ struct v4l2_subdev_state *state,
+ u64 stream_mask)
+{
+ const struct csi_formatter_pix_format *pix_fmt;
+ struct v4l2_subdev_route *route;
+ struct v4l2_mbus_framefmt *fmt;
+ struct v4l2_mbus_frame_desc fd = {};
+ u64 configured_streams = 0;
+ unsigned int reg;
+ unsigned int mask;
+ int vc;
+ int ret;
+
+ ret = v4l2_subdev_call(formatter->csi_sd, pad, get_frame_desc,
+ formatter->remote_pad, &fd);
+ if (ret < 0 && ret != -ENOIOCTLCMD) {
+ dev_err(formatter->dev, "Failed to get frame desc: %d\n", ret);
+ return ret;
+ }
+
+ for_each_active_route(&state->routing, route) {
+ if (route->source_pad != CSI_FORMATTER_PAD_SOURCE)
+ continue;
+
+ if (!(stream_mask & BIT_ULL(route->source_stream)))
+ continue;
+
+ if (ret == -ENOIOCTLCMD) {
+ /*
+ * Source doesn't implement get_frame_desc, use
+ * default VC 0
+ */
+ vc = 0;
+ } else {
+ vc = csi_formatter_get_vc(formatter, &fd,
+ route->sink_stream);
+ if (vc < 0) {
+ ret = vc;
+ goto err_cleanup;
+ }
+ }
+
+ fmt = v4l2_subdev_state_get_format(state, route->sink_pad,
+ route->sink_stream);
+
+ pix_fmt = csi_formatter_find_format(fmt->code);
+ if (WARN_ON(!pix_fmt)) {
+ ret = -EINVAL;
+ goto err_cleanup;
+ }
+
+ reg = CSI_VC_PIXEL_DATA_TYPE(vc) + formatter->reg_offset;
+ mask = BIT(csi_formatter_get_index_by_dt(formatter,
+ pix_fmt->data_type));
+
+ /* Set the data type bit to enable this VC */
+ regmap_set_bits(formatter->regs, reg, mask);
+
+ configured_streams |= BIT_ULL(route->source_stream);
+ }
+
+ return 0;
+
+err_cleanup:
+ csi_formatter_stop_stream(formatter, state, configured_streams);
+ return ret;
+}
+
+static int csi_formatter_subdev_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+ struct device *dev = formatter->dev;
+ u64 sink_streams;
+ int ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ CSI_FORMATTER_PAD_SOURCE,
+ CSI_FORMATTER_PAD_SINK,
+ &streams_mask);
+ if (!sink_streams || !streams_mask)
+ return -EINVAL;
+
+ guard(mutex)(&formatter->lock);
+
+ if (!formatter->enabled_streams) {
+ ret = pm_runtime_resume_and_get(formatter->dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to resume runtime PM: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = csi_formatter_start_stream(formatter, state, streams_mask);
+ if (ret)
+ goto err_runtime_put;
+
+ ret = v4l2_subdev_enable_streams(formatter->csi_sd,
+ formatter->remote_pad,
+ sink_streams);
+ if (ret)
+ goto err_stop_stream;
+
+ formatter->enabled_streams |= streams_mask;
+
+ return 0;
+
+err_stop_stream:
+ csi_formatter_stop_stream(formatter, state, streams_mask);
+err_runtime_put:
+ if (!formatter->enabled_streams)
+ pm_runtime_put(formatter->dev);
+ return ret;
+}
+
+static int csi_formatter_subdev_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state,
+ u32 pad, u64 streams_mask)
+{
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+ u64 sink_streams;
+ int ret;
+
+ sink_streams = v4l2_subdev_state_xlate_streams(state,
+ CSI_FORMATTER_PAD_SOURCE,
+ CSI_FORMATTER_PAD_SINK,
+ &streams_mask);
+ if (!sink_streams || !streams_mask)
+ return -EINVAL;
+
+ guard(mutex)(&formatter->lock);
+
+ ret = v4l2_subdev_disable_streams(formatter->csi_sd, formatter->remote_pad,
+ sink_streams);
+ if (ret)
+ dev_err(formatter->dev, "Failed to disable streams: %d\n", ret);
+
+ csi_formatter_stop_stream(formatter, state, streams_mask);
+
+ formatter->enabled_streams &= ~streams_mask;
+
+ if (!formatter->enabled_streams)
+ pm_runtime_put(formatter->dev);
+
+ return ret;
+}
+
+static const struct v4l2_subdev_pad_ops formatter_subdev_pad_ops = {
+ .enum_mbus_code = csi_formatter_subdev_enum_mbus_code,
+ .get_fmt = v4l2_subdev_get_fmt,
+ .set_fmt = csi_formatter_subdev_set_fmt,
+ .get_frame_desc = v4l2_subdev_get_frame_desc_passthrough,
+ .set_routing = csi_formatter_subdev_set_routing,
+ .enable_streams = csi_formatter_subdev_enable_streams,
+ .disable_streams = csi_formatter_subdev_disable_streams,
+};
+
+static const struct v4l2_subdev_ops formatter_subdev_ops = {
+ .pad = &formatter_subdev_pad_ops,
+};
+
+static const struct v4l2_subdev_internal_ops formatter_internal_ops = {
+ .init_state = csi_formatter_subdev_init_state,
+};
+
+/* -----------------------------------------------------------------------------
+ * Media entity operations
+ */
+
+static const struct media_entity_operations formatter_entity_ops = {
+ .link_validate = v4l2_subdev_link_validate,
+ .get_fwnode_pad = v4l2_subdev_get_fwnode_pad_1_to_1,
+};
+
+static int csi_formatter_subdev_init(struct csi_formatter *formatter)
+{
+ struct v4l2_subdev *sd = &formatter->sd;
+ int ret;
+
+ v4l2_subdev_init(sd, &formatter_subdev_ops);
+
+ snprintf(sd->name, sizeof(sd->name), "%s", dev_name(formatter->dev));
+ sd->internal_ops = &formatter_internal_ops;
+
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_STREAMS;
+ sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
+ sd->entity.ops = &formatter_entity_ops;
+ sd->dev = formatter->dev;
+
+ formatter->pads[CSI_FORMATTER_PAD_SINK].flags = MEDIA_PAD_FL_SINK
+ | MEDIA_PAD_FL_MUST_CONNECT;
+ formatter->pads[CSI_FORMATTER_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
+
+ ret = media_entity_pads_init(&sd->entity, CSI_FORMATTER_PAD_NUM,
+ formatter->pads);
+ if (ret) {
+ dev_err(formatter->dev, "Failed to init pads\n");
+ return ret;
+ }
+
+ ret = v4l2_subdev_init_finalize(sd);
+ if (ret)
+ media_entity_cleanup(&sd->entity);
+
+ return ret;
+}
+
+static inline struct csi_formatter *
+notifier_to_csi_formatter(struct v4l2_async_notifier *n)
+{
+ return container_of(n, struct csi_formatter, notifier);
+}
+
+static int csi_formatter_notify_bound(struct v4l2_async_notifier *notifier,
+ struct v4l2_subdev *sd,
+ struct v4l2_async_connection *asc)
+{
+ const unsigned int link_flags = MEDIA_LNK_FL_IMMUTABLE
+ | MEDIA_LNK_FL_ENABLED;
+ struct csi_formatter *formatter = notifier_to_csi_formatter(notifier);
+ struct v4l2_subdev *sdev = &formatter->sd;
+ struct media_pad *sink = &sdev->entity.pads[CSI_FORMATTER_PAD_SINK];
+ struct media_pad *remote_pad;
+ int ret;
+
+ formatter->csi_sd = sd;
+
+ dev_dbg(formatter->dev, "Bound subdev: %s pad\n", sd->name);
+
+ ret = v4l2_create_fwnode_links_to_pad(sd, sink, link_flags);
+ if (ret < 0)
+ return ret;
+
+ remote_pad = media_pad_remote_pad_first(sink);
+ if (!remote_pad) {
+ dev_err(formatter->dev, "Pipe not setup correctly\n");
+ return -EPIPE;
+ }
+ formatter->remote_pad = remote_pad->index;
+
+ return 0;
+}
+
+static const struct v4l2_async_notifier_operations formatter_notify_ops = {
+ .bound = csi_formatter_notify_bound,
+};
+
+static int csi_formatter_async_register(struct csi_formatter *formatter)
+{
+ struct device *dev = formatter->dev;
+ struct v4l2_async_connection *asc;
+ int ret;
+
+ struct fwnode_handle *ep __free(fwnode_handle) =
+ fwnode_graph_get_endpoint_by_id(dev_fwnode(dev), 0, 0,
+ FWNODE_GRAPH_ENDPOINT_NEXT);
+ if (!ep)
+ return -ENOTCONN;
+
+ v4l2_async_subdev_nf_init(&formatter->notifier, &formatter->sd);
+
+ asc = v4l2_async_nf_add_fwnode_remote(&formatter->notifier, ep,
+ struct v4l2_async_connection);
+ if (IS_ERR(asc)) {
+ ret = PTR_ERR(asc);
+ goto err_cleanup_notifier;
+ }
+
+ formatter->notifier.ops = &formatter_notify_ops;
+
+ ret = v4l2_async_nf_register(&formatter->notifier);
+ if (ret)
+ goto err_cleanup_notifier;
+
+ ret = v4l2_async_register_subdev(&formatter->sd);
+ if (ret)
+ goto err_unregister_notifier;
+
+ return 0;
+
+err_unregister_notifier:
+ v4l2_async_nf_unregister(&formatter->notifier);
+err_cleanup_notifier:
+ v4l2_async_nf_cleanup(&formatter->notifier);
+ return ret;
+}
+
+static void csi_formatter_async_unregister(struct csi_formatter *formatter)
+{
+ v4l2_async_unregister_subdev(&formatter->sd);
+ v4l2_async_nf_unregister(&formatter->notifier);
+ v4l2_async_nf_cleanup(&formatter->notifier);
+}
+
+/* -----------------------------------------------------------------------------
+ * Suspend/resume
+ */
+
+static int csi_formatter_runtime_suspend(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+
+ clk_disable_unprepare(formatter->clk);
+
+ return 0;
+}
+
+static int csi_formatter_runtime_resume(struct device *dev)
+{
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+
+ return clk_prepare_enable(formatter->clk);
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(csi_formatter_pm_ops,
+ csi_formatter_runtime_suspend,
+ csi_formatter_runtime_resume, NULL);
+
+static int csi_formatter_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct csi_formatter *formatter;
+ u32 val;
+ int ret;
+
+ formatter = devm_kzalloc(dev, sizeof(*formatter), GFP_KERNEL);
+ if (!formatter)
+ return -ENOMEM;
+
+ formatter->dev = dev;
+
+ ret = devm_mutex_init(dev, &formatter->lock);
+ if (ret)
+ return ret;
+
+ formatter->regs = syscon_node_to_regmap(dev->parent->of_node);
+ if (IS_ERR(formatter->regs))
+ return dev_err_probe(dev, PTR_ERR(formatter->regs),
+ "Failed to get csi formatter regmap\n");
+
+ ret = of_property_read_u32(dev->of_node, "reg", &val);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Failed to get csi formatter reg property\n");
+
+ formatter->reg_offset = val;
+
+ formatter->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(formatter->clk))
+ return dev_err_probe(dev, PTR_ERR(formatter->clk),
+ "Failed to get pixel clock\n");
+
+ ret = csi_formatter_subdev_init(formatter);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to initialize formatter subdev\n");
+
+ platform_set_drvdata(pdev, &formatter->sd);
+
+ /* Enable runtime PM. */
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ goto err_cleanup_subdev;
+
+ ret = csi_formatter_async_register(formatter);
+ if (ret < 0) {
+ dev_err_probe(dev, ret, "Failed to register async subdevice\n");
+ goto err_cleanup_subdev;
+ }
+
+ return 0;
+
+err_cleanup_subdev:
+ v4l2_subdev_cleanup(&formatter->sd);
+ media_entity_cleanup(&formatter->sd.entity);
+ return ret;
+}
+
+static void csi_formatter_remove(struct platform_device *pdev)
+{
+ struct v4l2_subdev *sd = platform_get_drvdata(pdev);
+ struct csi_formatter *formatter = sd_to_formatter(sd);
+
+ csi_formatter_async_unregister(formatter);
+
+ v4l2_subdev_cleanup(&formatter->sd);
+ media_entity_cleanup(&formatter->sd.entity);
+}
+
+static const struct of_device_id csi_formatter_of_match[] = {
+ { .compatible = "fsl,imx95-csi-formatter" },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, csi_formatter_of_match);
+
+static struct platform_driver csi_formatter_device_driver = {
+ .driver = {
+ .name = "csi-pixel-formatter",
+ .of_match_table = csi_formatter_of_match,
+ .pm = pm_ptr(&csi_formatter_pm_ops),
+ },
+ .probe = csi_formatter_probe,
+ .remove = csi_formatter_remove,
+};
+
+module_platform_driver(csi_formatter_device_driver);
+
+MODULE_AUTHOR("NXP Semiconductor, Inc.");
+MODULE_DESCRIPTION("NXP i.MX95 CSI Pixel Formatter driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH 0/7] soc: Remove redundant error messages on IRQ request failure
From: Pan Chuang @ 2026-07-20 9:59 UTC (permalink / raw)
To: Roy Pledge, Christophe Leroy (CS GROUP), Linus Walleij,
Imre Kaloz, Matthias Brugger, AngeloGioacchino Del Regno,
Bjorn Andersson, Konrad Dybcio, Thierry Reding, Jonathan Hunter,
Nishanth Menon, Santosh Shilimkar, Michal Simek, Shashank Balaji,
Gary Guo, Sumit Gupta, Rahul Bukte, Bruno Sobreira França,
Pan Chuang, Prasanna Kumar T S M, Marco Crivellari,
Jay Buddhabhatti, open list:DPAA2 DATAPATH I/O (DPIO) DRIVER,
open list:FREESCALE SOC DRIVERS,
moderated list:FREESCALE SOC DRIVERS,
moderated list:ARM/Mediatek SoC support,
open list:ARM/QUALCOMM MAILING LIST,
open list:TEGRA ARCHITECTURE SUPPORT
Commit 55b48e23f5c4 ("genirq/devres: Add error handling in
devm_request_*_irq()") added automatic error logging to
devm_request_threaded_irq() and devm_request_any_context_irq()
via the new devm_request_result() helper, which prints device
name, IRQ number, handler functions, and error code on failure.
Since devm_request_irq() is a static inline wrapper around
devm_request_threaded_irq(), it also benefits from this
automatic logging.
Remove the now-redundant dev_err() and dev_err_probe() calls
in soc drivers that follow these devm_request_*_irq()
functions, as the core now provides more detailed diagnostic
information on failure.
Pan Chuang (7):
soc: fsl: dpio: Remove redundant dev_err()
soc: ixp4xx: Remove redundant dev_err()
soc: mediatek: mtk-svs: Remove redundant dev_err_probe()
soc: qcom: Remove redundant dev_err()
soc/tegra: cbb: Remove redundant dev_err()
soc: ti: wkup_m3_ipc: Remove redundant dev_err()
soc: xilinx: Remove redundant dev_err()
drivers/soc/fsl/dpio/dpio-driver.c | 6 +-----
drivers/soc/ixp4xx/ixp4xx-qmgr.c | 10 ++--------
drivers/soc/mediatek/mtk-svs.c | 4 +---
drivers/soc/qcom/qcom_aoss.c | 4 +---
drivers/soc/qcom/smp2p.c | 4 +---
drivers/soc/qcom/smsm.c | 4 +---
drivers/soc/tegra/cbb/tegra194-cbb.c | 9 ++-------
drivers/soc/ti/wkup_m3_ipc.c | 4 +---
drivers/soc/xilinx/zynqmp_power.c | 5 +----
9 files changed, 11 insertions(+), 39 deletions(-)
--
2.34.1
^ permalink raw reply
* [PATCH 2/7] soc: ixp4xx: Remove redundant dev_err()
From: Pan Chuang @ 2026-07-20 9:59 UTC (permalink / raw)
To: Linus Walleij, Imre Kaloz,
moderated list:ARM/INTEL IXP4XX ARM ARCHITECTURE, open list
Cc: Pan Chuang
In-Reply-To: <20260720095920.542801-1-panchuang@vivo.com>
Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in
devm_request_*_irq()"), devm_request_irq() automatically logs
detailed error messages on failure. Remove the now-redundant
driver-specific dev_err() calls.
Signed-off-by: Pan Chuang <panchuang@vivo.com>
---
drivers/soc/ixp4xx/ixp4xx-qmgr.c | 10 ++--------
1 file changed, 2 insertions(+), 8 deletions(-)
diff --git a/drivers/soc/ixp4xx/ixp4xx-qmgr.c b/drivers/soc/ixp4xx/ixp4xx-qmgr.c
index 475e229039e3..6e1fca630b80 100644
--- a/drivers/soc/ixp4xx/ixp4xx-qmgr.c
+++ b/drivers/soc/ixp4xx/ixp4xx-qmgr.c
@@ -421,19 +421,13 @@ static int ixp4xx_qmgr_probe(struct platform_device *pdev)
err = devm_request_irq(dev, irq1, handler1, 0, "IXP4xx Queue Manager",
NULL);
- if (err) {
- dev_err(dev, "failed to request IRQ%i (%i)\n",
- irq1, err);
+ if (err)
return err;
- }
err = devm_request_irq(dev, irq2, handler2, 0, "IXP4xx Queue Manager",
NULL);
- if (err) {
- dev_err(dev, "failed to request IRQ%i (%i)\n",
- irq2, err);
+ if (err)
return err;
- }
used_sram_bitmap[0] = 0xF; /* 4 first pages reserved for config */
spin_lock_init(&qmgr_lock);
--
2.34.1
^ permalink raw reply related
* [PATCH 1/7] soc: fsl: dpio: Remove redundant dev_err()
From: Pan Chuang @ 2026-07-20 9:59 UTC (permalink / raw)
To: Roy Pledge, Christophe Leroy (CS GROUP),
open list:DPAA2 DATAPATH I/O (DPIO) DRIVER,
open list:FREESCALE SOC DRIVERS,
moderated list:FREESCALE SOC DRIVERS
Cc: Pan Chuang
In-Reply-To: <20260720095920.542801-1-panchuang@vivo.com>
Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in
devm_request_*_irq()"), devm_request_irq() automatically logs
detailed error messages on failure. Remove the now-redundant
driver-specific dev_err() calls.
Signed-off-by: Pan Chuang <panchuang@vivo.com>
---
drivers/soc/fsl/dpio/dpio-driver.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/soc/fsl/dpio/dpio-driver.c b/drivers/soc/fsl/dpio/dpio-driver.c
index 9e3fddd8f5a9..d5198ffc4455 100644
--- a/drivers/soc/fsl/dpio/dpio-driver.c
+++ b/drivers/soc/fsl/dpio/dpio-driver.c
@@ -102,12 +102,8 @@ static int register_dpio_irq_handlers(struct fsl_mc_device *dpio_dev, int cpu)
0,
dev_name(&dpio_dev->dev),
&dpio_dev->dev);
- if (error < 0) {
- dev_err(&dpio_dev->dev,
- "devm_request_irq() failed: %d\n",
- error);
+ if (error < 0)
return error;
- }
/* set the affinity hint */
if (irq_set_affinity_hint(irq->virq, cpumask_of(cpu)))
--
2.34.1
^ permalink raw reply related
* [PATCH 3/7] soc: mediatek: mtk-svs: Remove redundant dev_err_probe()
From: Pan Chuang @ 2026-07-20 9:59 UTC (permalink / raw)
To: Matthias Brugger, AngeloGioacchino Del Regno,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support
Cc: Pan Chuang
In-Reply-To: <20260720095920.542801-1-panchuang@vivo.com>
Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in
devm_request_*_irq()"), devm_request_threaded_irq() automatically logs
detailed error messages on failure. Remove the now-redundant
driver-specific dev_err_probe() calls.
Signed-off-by: Pan Chuang <panchuang@vivo.com>
---
drivers/soc/mediatek/mtk-svs.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/soc/mediatek/mtk-svs.c b/drivers/soc/mediatek/mtk-svs.c
index 99edecb204f2..39af0728ce61 100644
--- a/drivers/soc/mediatek/mtk-svs.c
+++ b/drivers/soc/mediatek/mtk-svs.c
@@ -2908,10 +2908,8 @@ static int svs_probe(struct platform_device *pdev)
ret = devm_request_threaded_irq(svsp->dev, svsp_irq, NULL, svs_isr,
IRQF_ONESHOT, svsp_data->name, svsp);
- if (ret) {
- dev_err_probe(svsp->dev, ret, "register irq(%d) failed\n", svsp_irq);
+ if (ret)
goto svs_probe_iounmap;
- }
ret = svs_start(svsp);
if (ret) {
--
2.34.1
^ permalink raw reply related
* [PATCH 6/7] soc: ti: wkup_m3_ipc: Remove redundant dev_err()
From: Pan Chuang @ 2026-07-20 9:59 UTC (permalink / raw)
To: Nishanth Menon, Santosh Shilimkar,
open list:TI KEYSTONE MULTICORE NAVIGATOR DRIVERS,
moderated list:TI KEYSTONE MULTICORE NAVIGATOR DRIVERS
Cc: Pan Chuang
In-Reply-To: <20260720095920.542801-1-panchuang@vivo.com>
Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in
devm_request_*_irq()"), devm_request_irq() automatically logs
detailed error messages on failure. Remove the now-redundant
driver-specific dev_err() calls.
Signed-off-by: Pan Chuang <panchuang@vivo.com>
---
drivers/soc/ti/wkup_m3_ipc.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/soc/ti/wkup_m3_ipc.c b/drivers/soc/ti/wkup_m3_ipc.c
index 5845fc652adc..f8640cdc2a21 100644
--- a/drivers/soc/ti/wkup_m3_ipc.c
+++ b/drivers/soc/ti/wkup_m3_ipc.c
@@ -630,10 +630,8 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq, wkup_m3_txev_handler,
0, "wkup_m3_txev", m3_ipc);
- if (ret) {
- dev_err(dev, "request_irq failed\n");
+ if (ret)
return ret;
- }
m3_ipc->mbox_client.dev = dev;
m3_ipc->mbox_client.tx_done = NULL;
--
2.34.1
^ permalink raw reply related
* [PATCH 7/7] soc: xilinx: Remove redundant dev_err()
From: Pan Chuang @ 2026-07-20 9:59 UTC (permalink / raw)
To: Michal Simek, Prasanna Kumar T S M, Pan Chuang, Marco Crivellari,
Jay Buddhabhatti, moderated list:ARM/ZYNQ ARCHITECTURE, open list
In-Reply-To: <20260720095920.542801-1-panchuang@vivo.com>
Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in
devm_request_*_irq()"), devm_request_threaded_irq() automatically logs
detailed error messages on failure. Remove the now-redundant
driver-specific dev_err() calls.
Signed-off-by: Pan Chuang <panchuang@vivo.com>
---
drivers/soc/xilinx/zynqmp_power.c | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c
index 370e61ac47d8..54c796afb89e 100644
--- a/drivers/soc/xilinx/zynqmp_power.c
+++ b/drivers/soc/xilinx/zynqmp_power.c
@@ -368,11 +368,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev)
IRQF_NO_SUSPEND | IRQF_ONESHOT,
dev_name(&pdev->dev),
&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "devm_request_threaded_irq '%d' failed with %d\n",
- irq, ret);
+ if (ret)
return ret;
- }
} else {
dev_err(&pdev->dev, "Required property not found in DT node\n");
return -ENOENT;
--
2.34.1
^ 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