* Re: [PATCH v14 01/18] irqchip/sifive-plic: Convert PLIC driver into a platform driver
From: Anup Patel @ 2024-04-03 14:16 UTC (permalink / raw)
To: Lad, Prabhakar
Cc: Geert Uytterhoeven, Palmer Dabbelt, Paul Walmsley,
Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Frank Rowand,
Conor Dooley, Marc Zyngier, Björn Töpel, Atish Patra,
Andrew Jones, Sunil V L, Saravana Kannan, Anup Patel, linux-riscv,
linux-arm-kernel, linux-kernel, devicetree
In-Reply-To: <CA+V-a8tGucbJ87hsMQDEgcor5BzDmB_WnRsEn6c9F_HzucWLXQ@mail.gmail.com>
On Wed, Apr 3, 2024 at 2:01 PM Lad, Prabhakar
<prabhakar.csengg@gmail.com> wrote:
>
> Hi Anup,
>
> On Thu, Feb 22, 2024 at 9:41 AM Anup Patel <apatel@ventanamicro.com> wrote:
> >
> > The PLIC driver does not require very early initialization so convert
> > it into a platform driver.
> >
> > After conversion, the PLIC driver is probed after CPUs are brought-up
> > so setup cpuhp state after context handler of all online CPUs are
> > initialized otherwise PLIC driver crashes for platforms with multiple
> > PLIC instances.
> >
> > Signed-off-by: Anup Patel <apatel@ventanamicro.com>
> > ---
> > drivers/irqchip/irq-sifive-plic.c | 101 ++++++++++++++++++------------
> > 1 file changed, 61 insertions(+), 40 deletions(-)
> >
> This patch seems to have broken things on RZ/Five SoC, after reverting
> this patch I get to boot it back again on v6.9-rc2. Looks like there
> is some probe order issue after switching to platform driver?
Yes, this is most likely related to probe ordering based on your DT.
Can you share the failing boot log and DT ?
Regards,
Anup
>
> Cheers,
> Prabhakar
>
> > diff --git a/drivers/irqchip/irq-sifive-plic.c b/drivers/irqchip/irq-sifive-plic.c
> > index 5b7bc4fd9517..7400a07fc479 100644
> > --- a/drivers/irqchip/irq-sifive-plic.c
> > +++ b/drivers/irqchip/irq-sifive-plic.c
> > @@ -64,6 +64,7 @@
> > #define PLIC_QUIRK_EDGE_INTERRUPT 0
> >
> > struct plic_priv {
> > + struct device *dev;
> > struct cpumask lmask;
> > struct irq_domain *irqdomain;
> > void __iomem *regs;
> > @@ -406,30 +407,50 @@ static int plic_starting_cpu(unsigned int cpu)
> > return 0;
> > }
> >
> > -static int __init __plic_init(struct device_node *node,
> > - struct device_node *parent,
> > - unsigned long plic_quirks)
> > +static const struct of_device_id plic_match[] = {
> > + { .compatible = "sifive,plic-1.0.0" },
> > + { .compatible = "riscv,plic0" },
> > + { .compatible = "andestech,nceplic100",
> > + .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
> > + { .compatible = "thead,c900-plic",
> > + .data = (const void *)BIT(PLIC_QUIRK_EDGE_INTERRUPT) },
> > + {}
> > +};
> > +
> > +static int plic_probe(struct platform_device *pdev)
> > {
> > int error = 0, nr_contexts, nr_handlers = 0, i;
> > - u32 nr_irqs;
> > - struct plic_priv *priv;
> > + struct device *dev = &pdev->dev;
> > + unsigned long plic_quirks = 0;
> > struct plic_handler *handler;
> > + struct plic_priv *priv;
> > + bool cpuhp_setup;
> > unsigned int cpu;
> > + u32 nr_irqs;
> > +
> > + if (is_of_node(dev->fwnode)) {
> > + const struct of_device_id *id;
> > +
> > + id = of_match_node(plic_match, to_of_node(dev->fwnode));
> > + if (id)
> > + plic_quirks = (unsigned long)id->data;
> > + }
> >
> > priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> > if (!priv)
> > return -ENOMEM;
> >
> > + priv->dev = dev;
> > priv->plic_quirks = plic_quirks;
> >
> > - priv->regs = of_iomap(node, 0);
> > + priv->regs = of_iomap(to_of_node(dev->fwnode), 0);
> > if (WARN_ON(!priv->regs)) {
> > error = -EIO;
> > goto out_free_priv;
> > }
> >
> > error = -EINVAL;
> > - of_property_read_u32(node, "riscv,ndev", &nr_irqs);
> > + of_property_read_u32(to_of_node(dev->fwnode), "riscv,ndev", &nr_irqs);
> > if (WARN_ON(!nr_irqs))
> > goto out_iounmap;
> >
> > @@ -439,13 +460,13 @@ static int __init __plic_init(struct device_node *node,
> > if (!priv->prio_save)
> > goto out_free_priority_reg;
> >
> > - nr_contexts = of_irq_count(node);
> > + nr_contexts = of_irq_count(to_of_node(dev->fwnode));
> > if (WARN_ON(!nr_contexts))
> > goto out_free_priority_reg;
> >
> > error = -ENOMEM;
> > - priv->irqdomain = irq_domain_add_linear(node, nr_irqs + 1,
> > - &plic_irqdomain_ops, priv);
> > + priv->irqdomain = irq_domain_add_linear(to_of_node(dev->fwnode), nr_irqs + 1,
> > + &plic_irqdomain_ops, priv);
> > if (WARN_ON(!priv->irqdomain))
> > goto out_free_priority_reg;
> >
> > @@ -455,7 +476,7 @@ static int __init __plic_init(struct device_node *node,
> > int cpu;
> > unsigned long hartid;
> >
> > - if (of_irq_parse_one(node, i, &parent)) {
> > + if (of_irq_parse_one(to_of_node(dev->fwnode), i, &parent)) {
> > pr_err("failed to parse parent for context %d.\n", i);
> > continue;
> > }
> > @@ -491,7 +512,7 @@ static int __init __plic_init(struct device_node *node,
> >
> > /* Find parent domain and register chained handler */
> > if (!plic_parent_irq && irq_find_host(parent.np)) {
> > - plic_parent_irq = irq_of_parse_and_map(node, i);
> > + plic_parent_irq = irq_of_parse_and_map(to_of_node(dev->fwnode), i);
> > if (plic_parent_irq)
> > irq_set_chained_handler(plic_parent_irq,
> > plic_handle_irq);
> > @@ -533,20 +554,29 @@ static int __init __plic_init(struct device_node *node,
> >
> > /*
> > * We can have multiple PLIC instances so setup cpuhp state
> > - * and register syscore operations only when context handler
> > - * for current/boot CPU is present.
> > + * and register syscore operations only once after context
> > + * handlers of all online CPUs are initialized.
> > */
> > - handler = this_cpu_ptr(&plic_handlers);
> > - if (handler->present && !plic_cpuhp_setup_done) {
> > - cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
> > - "irqchip/sifive/plic:starting",
> > - plic_starting_cpu, plic_dying_cpu);
> > - register_syscore_ops(&plic_irq_syscore_ops);
> > - plic_cpuhp_setup_done = true;
> > + if (!plic_cpuhp_setup_done) {
> > + cpuhp_setup = true;
> > + for_each_online_cpu(cpu) {
> > + handler = per_cpu_ptr(&plic_handlers, cpu);
> > + if (!handler->present) {
> > + cpuhp_setup = false;
> > + break;
> > + }
> > + }
> > + if (cpuhp_setup) {
> > + cpuhp_setup_state(CPUHP_AP_IRQ_SIFIVE_PLIC_STARTING,
> > + "irqchip/sifive/plic:starting",
> > + plic_starting_cpu, plic_dying_cpu);
> > + register_syscore_ops(&plic_irq_syscore_ops);
> > + plic_cpuhp_setup_done = true;
> > + }
> > }
> >
> > - pr_info("%pOFP: mapped %d interrupts with %d handlers for"
> > - " %d contexts.\n", node, nr_irqs, nr_handlers, nr_contexts);
> > + pr_info("%pOFP: mapped %d interrupts with %d handlers for %d contexts.\n",
> > + to_of_node(dev->fwnode), nr_irqs, nr_handlers, nr_contexts);
> > return 0;
> >
> > out_free_enable_reg:
> > @@ -563,20 +593,11 @@ static int __init __plic_init(struct device_node *node,
> > return error;
> > }
> >
> > -static int __init plic_init(struct device_node *node,
> > - struct device_node *parent)
> > -{
> > - return __plic_init(node, parent, 0);
> > -}
> > -
> > -IRQCHIP_DECLARE(sifive_plic, "sifive,plic-1.0.0", plic_init);
> > -IRQCHIP_DECLARE(riscv_plic0, "riscv,plic0", plic_init); /* for legacy systems */
> > -
> > -static int __init plic_edge_init(struct device_node *node,
> > - struct device_node *parent)
> > -{
> > - return __plic_init(node, parent, BIT(PLIC_QUIRK_EDGE_INTERRUPT));
> > -}
> > -
> > -IRQCHIP_DECLARE(andestech_nceplic100, "andestech,nceplic100", plic_edge_init);
> > -IRQCHIP_DECLARE(thead_c900_plic, "thead,c900-plic", plic_edge_init);
> > +static struct platform_driver plic_driver = {
> > + .driver = {
> > + .name = "riscv-plic",
> > + .of_match_table = plic_match,
> > + },
> > + .probe = plic_probe,
> > +};
> > +builtin_platform_driver(plic_driver);
> > --
> > 2.34.1
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel@lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* Re: [PATCH net-next v6 11/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs
From: Rob Herring @ 2024-04-03 14:31 UTC (permalink / raw)
To: Kory Maincent
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Jonathan Corbet, Luis Chamberlain, Russ Weight,
Greg Kroah-Hartman, Rafael J. Wysocki, Krzysztof Kozlowski,
Conor Dooley, Oleksij Rempel, Mark Brown, Frank Rowand,
Andrew Lunn, Heiner Kallweit, Russell King, Thomas Petazzoni,
netdev, linux-kernel, linux-doc, devicetree, Dent Project
In-Reply-To: <20240403111548.30e780b5@kmaincent-XPS-13-7390>
On Wed, Apr 03, 2024 at 11:15:48AM +0200, Kory Maincent wrote:
> On Tue, 2 Apr 2024 08:26:37 -0500
> Rob Herring <robh@kernel.org> wrote:
>
> > > + pairset-names:
> > > + $ref: /schemas/types.yaml#/definitions/string-array
> > > + description:
> > > + Names of the pairsets as per IEEE 802.3-2022, Section
> > > 145.2.4.
> > > + Valid values are "alternative-a" and "alternative-b". Each
> > > name
> >
> > Don't state constraints in prose which are defined as schema
> > constraints.
>
> Ok, I will remove the line.
>
> > > + pairsets:
> > > + $ref: /schemas/types.yaml#/definitions/phandle-array
> > > + description:
> > > + List of phandles, each pointing to the power supply for the
> > > + corresponding pairset named in 'pairset-names'. This property
> > > + aligns with IEEE 802.3-2022, Section 33.2.3 and 145.2.4.
> > > + PSE Pinout Alternatives (as per IEEE 802.3-2022 Table
> > > 145\u20133)
> > > +
> > > |-----------|---------------|---------------|---------------|---------------|
> > > + | Conductor | Alternative A | Alternative A | Alternative B
> > > | Alternative B |
> > > + | | (MDI-X) | (MDI) | (X)
> > > | (S) |
> > > +
> > > |-----------|---------------|---------------|---------------|---------------|
> > > + | 1 | Negative VPSE | Positive VPSE | \u2014
> > > | \u2014 |
> > > + | 2 | Negative VPSE | Positive VPSE | \u2014
> > > | \u2014 |
> > > + | 3 | Positive VPSE | Negative VPSE | \u2014
> > > | \u2014 |
> > > + | 4 | \u2014 | \u2014 |
> > > Negative VPSE | Positive VPSE |
> > > + | 5 | \u2014 | \u2014 |
> > > Negative VPSE | Positive VPSE |
> > > + | 6 | Positive VPSE | Negative VPSE | \u2014
> > > | \u2014 |
> > > + | 7 | \u2014 | \u2014 |
> > > Positive VPSE | Negative VPSE |
> > > + | 8 | \u2014 | \u2014 |
> > > Positive VPSE | Negative VPSE |
> > > + minItems: 1
> > > + maxItems: 2
> >
> > "pairsets" does not follow the normal design pattern of foos, foo-names,
> > and #foo-cells. You could add #foo-cells I suppose, but what would cells
> > convey? I don't think it's a good fit for what you need.
> >
> > The other oddity is the number of entries and the names are fixed. That
> > is usually defined per consumer.
>
> Theoretically if the RJ45 port binding was supported it would make more sense,
> but in reality it's not feasible as the PSE controller need this information
> in its init process.
> The PSE controller reset all its port to apply a configuration so we can't do
> it when the consumer (RJ45) probe. It would reset the other ports if one
> consumer is probed later in the process.
There is no reason other than convenience that all information some
driver needs has to be in one node or one hierarchy of nodes. You can
fetch anything from anywhere in the DT. It does feel like some of this
belongs in a connector node. We often haven't described connectors in DT
and stick connector properties in the controller node associated with
the connector. Then as things get more complicated, it becomes a mess.
> > As each entry is just a power rail, why can't the regulator binding be
> > used here?
>
> Olekisj already answered about it.
> PSE PI is like a regulator but with few different features and more information
> like the pinout and the polarity, so we could not really fully rely on the
> regulator binding style.
>
> > > +
> > > + polarity-supported:
> > > + $ref: /schemas/types.yaml#/definitions/string-array
> > > + description:
> > > + Polarity configuration supported by the PSE PI pairsets.
> > > + minItems: 1
> > > + maxItems: 4
> > > + items:
> > > + enum:
> > > + - MDI-X
> > > + - MDI
> > > + - X
> > > + - S
> > > +
> > > + vpwr-supply:
> > > + description: Regulator power supply for the PSE PI.
> >
> > I don't see this being used anywhere.
>
> Right, I forgot to add it to the PD692x0 and TPS23881 binding example!
But is this really common/generic? I would think input power rails would
be chip specific.
Rob
^ permalink raw reply
* Re: [PATCH] media: dt-bindings: ovti,ov2680: Document clock/data-lanes
From: Fabio Estevam @ 2024-04-03 14:32 UTC (permalink / raw)
To: Sakari Ailus
Cc: rmfrfs, robh, krzysztof.kozlowski+dt, conor+dt, linux-media,
devicetree, Fabio Estevam
In-Reply-To: <ZgvCJ5F6KlhVGRPC@kekkonen.localdomain>
Hi Sakari,
On Tue, Apr 2, 2024 at 5:30 AM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
> If it's a problem with the driver, you should fix the driver instead of
> working around it in DT. Just remove the check.
Makes sense. I will send a driver patch to remove the check.
I will wait for my ov2680 patches to be applied first and then I will
submit an additional one.
Thanks
^ permalink raw reply
* Re: [PATCH v2] arm64: dts: ti: k3-{am62p,j722s}: Disable ethernet by default
From: Andrew Davis @ 2024-04-03 14:34 UTC (permalink / raw)
To: Michael Walle, Francesco Dolcini, Nishanth Menon,
Vignesh Raghavendra, Tero Kristo, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20240403101545.3932437-1-mwalle@kernel.org>
On 4/3/24 5:15 AM, Michael Walle wrote:
> Device tree best practice is to disable any external interface in the
> dtsi and just enable them if needed in the device tree. Thus, disable
> the ethernet switch and its ports by default and just enable the ones
> used by the EVMs in their device trees.
>
> There is no functional change.
>
> Signed-off-by: Michael Walle <mwalle@kernel.org>
> ---
Acked-by: Andrew Davis <afd@ti.com>
> v2:
> - move the status propert into k3-am62p5-main.dtsi, thus also update
> the k3-am62p5-sk.dts
> - put "status" last
> ---
> arch/arm64/boot/dts/ti/k3-am62p-main.dtsi | 3 +++
> arch/arm64/boot/dts/ti/k3-am62p5-sk.dts | 3 +++
> arch/arm64/boot/dts/ti/k3-j722s-evm.dts | 5 +----
> 3 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/ti/k3-am62p-main.dtsi b/arch/arm64/boot/dts/ti/k3-am62p-main.dtsi
> index 7337a9e13535..88bc64111234 100644
> --- a/arch/arm64/boot/dts/ti/k3-am62p-main.dtsi
> +++ b/arch/arm64/boot/dts/ti/k3-am62p-main.dtsi
> @@ -673,6 +673,7 @@ cpsw3g: ethernet@8000000 {
> assigned-clock-parents = <&k3_clks 13 11>;
> clock-names = "fck";
> power-domains = <&k3_pds 13 TI_SCI_PD_EXCLUSIVE>;
> + status = "disabled";
>
> dmas = <&main_pktdma 0xc600 15>,
> <&main_pktdma 0xc601 15>,
> @@ -696,6 +697,7 @@ cpsw_port1: port@1 {
> label = "port1";
> phys = <&phy_gmii_sel 1>;
> mac-address = [00 00 00 00 00 00];
> + status = "disabled";
> };
>
> cpsw_port2: port@2 {
> @@ -704,6 +706,7 @@ cpsw_port2: port@2 {
> label = "port2";
> phys = <&phy_gmii_sel 2>;
> mac-address = [00 00 00 00 00 00];
> + status = "disabled";
> };
> };
>
> diff --git a/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts b/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts
> index 6694087b3665..6a9c99c5fb2a 100644
> --- a/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts
> +++ b/arch/arm64/boot/dts/ti/k3-am62p5-sk.dts
> @@ -431,16 +431,19 @@ &cpsw3g {
> pinctrl-names = "default";
> pinctrl-0 = <&main_rgmii1_pins_default>,
> <&main_rgmii2_pins_default>;
> + status = "okay";
> };
>
> &cpsw_port1 {
> phy-mode = "rgmii-rxid";
> phy-handle = <&cpsw3g_phy0>;
> + status = "okay";
> };
>
> &cpsw_port2 {
> phy-mode = "rgmii-rxid";
> phy-handle = <&cpsw3g_phy1>;
> + status = "okay";
> };
>
> &cpsw3g_mdio {
> diff --git a/arch/arm64/boot/dts/ti/k3-j722s-evm.dts b/arch/arm64/boot/dts/ti/k3-j722s-evm.dts
> index 6b148da2bcdc..8a38e5ae7d4f 100644
> --- a/arch/arm64/boot/dts/ti/k3-j722s-evm.dts
> +++ b/arch/arm64/boot/dts/ti/k3-j722s-evm.dts
> @@ -226,10 +226,7 @@ cpsw3g_phy0: ethernet-phy@0 {
> &cpsw_port1 {
> phy-mode = "rgmii-rxid";
> phy-handle = <&cpsw3g_phy0>;
> -};
> -
> -&cpsw_port2 {
> - status = "disabled";
> + status = "okay";
> };
>
> &main_gpio1 {
^ permalink raw reply
* Re: [PATCH v7 2/2] dmaengine: Loongson1: Add Loongson-1 APB DMA driver
From: Huacai Chen @ 2024-04-03 14:39 UTC (permalink / raw)
To: Keguang Zhang
Cc: Vinod Koul, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
linux-mips, dmaengine, devicetree, linux-kernel
In-Reply-To: <CAJhJPsULnEfTMFK5HS5TQZ_0XSs77Tw58Yfvw67BtTTHvjSLLw@mail.gmail.com>
On Wed, Apr 3, 2024 at 10:24 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
>
> On Tue, Apr 2, 2024 at 10:50 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> >
> > On Tue, Apr 2, 2024 at 6:51 PM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > >
> > > On Tue, Apr 2, 2024 at 5:04 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > >
> > > > On Tue, Apr 2, 2024 at 9:56 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > > > >
> > > > > On Mon, Apr 1, 2024 at 9:24 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > > > >
> > > > > > On Mon, Apr 1, 2024 at 7:10 PM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > > > > > >
> > > > > > > On Mon, Apr 1, 2024 at 5:06 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > > > > > >
> > > > > > > > On Mon, Apr 1, 2024 at 10:45 AM Keguang Zhang <keguang.zhang@gmail.com> wrote:
> > > > > > > > >
> > > > > > > > > Hi Huacai,
> > > > > > > > >
> > > > > > > > > On Sat, Mar 30, 2024 at 9:59 PM Huacai Chen <chenhuacai@kernel.org> wrote:
> > > > > > > > > >
> > > > > > > > > > Hi, Keguang,
> > > > > > > > > >
> > > > > > > > > > On Fri, Mar 29, 2024 at 7:28 PM Keguang Zhang via B4 Relay
> > > > > > > > > > <devnull+keguang.zhang.gmail.com@kernel.org> wrote:
> > > > > > > > > > >
> > > > > > > > > > > From: Keguang Zhang <keguang.zhang@gmail.com>
> > > > > > > > > > >
> > > > > > > > > > > This patch adds APB DMA driver for Loongson-1 SoCs.
> > > > > > > > > > >
> > > > > > > > > > > Signed-off-by: Keguang Zhang <keguang.zhang@gmail.com>
> > > > > > > > > > > ---
> > > > > > > > > > > Changes in v7:
> > > > > > > > > > > - Change the comptible to 'loongson,ls1*-apbdma'
> > > > > > > > > > > - Update Kconfig and Makefile accordingly
> > > > > > > > > > > - Rename the file to loongson1-apb-dma.c to keep the consistency
> > > > > > > > > > >
> > > > > > > > > > > Changes in v6:
> > > > > > > > > > > - Implement .device_prep_dma_cyclic for Loongson1 audio driver,
> > > > > > > > > > > - as well as .device_pause and .device_resume.
> > > > > > > > > > > - Set the limitation LS1X_DMA_MAX_DESC and put all descriptors
> > > > > > > > > > > - into one page to save memory
> > > > > > > > > > > - Move dma_pool_zalloc() into ls1x_dma_alloc_desc()
> > > > > > > > > > > - Drop dma_slave_config structure
> > > > > > > > > > > - Use .remove_new instead of .remove
> > > > > > > > > > > - Use KBUILD_MODNAME for the driver name
> > > > > > > > > > > - Improve the debug information
> > > > > > > > > > >
> > > > > > > > > > > Changes in v5:
> > > > > > > > > > > - Add DT support
> > > > > > > > > > > - Use DT data instead of platform data
> > > > > > > > > > > - Use chan_id of struct dma_chan instead of own id
> > > > > > > > > > > - Use of_dma_xlate_by_chan_id() instead of ls1x_dma_filter()
> > > > > > > > > > > - Update the author information to my official name
> > > > > > > > > > >
> > > > > > > > > > > Changes in v4:
> > > > > > > > > > > - Use dma_slave_map to find the proper channel.
> > > > > > > > > > > - Explicitly call devm_request_irq() and tasklet_kill().
> > > > > > > > > > > - Fix namespace issue.
> > > > > > > > > > > - Some minor fixes and cleanups.
> > > > > > > > > > >
> > > > > > > > > > > Changes in v3:
> > > > > > > > > > > - Rename ls1x_dma_filter_fn to ls1x_dma_filter.
> > > > > > > > > > >
> > > > > > > > > > > Changes in v2:
> > > > > > > > > > > - Change the config from 'DMA_LOONGSON1' to 'LOONGSON1_DMA',
> > > > > > > > > > > - and rearrange it in alphabetical order in Kconfig and Makefile.
> > > > > > > > > > > - Fix comment style.
> > > > > > > > > > > ---
> > > > > > > > > > > drivers/dma/Kconfig | 9 +
> > > > > > > > > > > drivers/dma/Makefile | 1 +
> > > > > > > > > > > drivers/dma/loongson1-apb-dma.c | 665 ++++++++++++++++++++++++++++++++++++++++
> > > > > > > > > > > 3 files changed, 675 insertions(+)
> > > > > > > > > > >
> > > > > > > > > > > diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> > > > > > > > > > > index 002a5ec80620..f7b06c4cdf3f 100644
> > > > > > > > > > > --- a/drivers/dma/Kconfig
> > > > > > > > > > > +++ b/drivers/dma/Kconfig
> > > > > > > > > > > @@ -369,6 +369,15 @@ config K3_DMA
> > > > > > > > > > > Support the DMA engine for Hisilicon K3 platform
> > > > > > > > > > > devices.
> > > > > > > > > > >
> > > > > > > > > > > +config LOONGSON1_APB_DMA
> > > > > > > > > > > + tristate "Loongson1 APB DMA support"
> > > > > > > > > > > + depends on MACH_LOONGSON32 || COMPILE_TEST
> > > > > > > > > > > + select DMA_ENGINE
> > > > > > > > > > > + select DMA_VIRTUAL_CHANNELS
> > > > > > > > > > > + help
> > > > > > > > > > > + This selects support for the APB DMA controller in Loongson1 SoCs,
> > > > > > > > > > > + which is required by Loongson1 NAND and audio support.
> > > > > > > > > > Why not rename to LS1X_APB_DMA and put it just before LS2X_APB_DMA
> > > > > > > > > > (and also the driver file name)?
> > > > > > > > > >
> > > > > > > > > So far all Kconfig entries of Loongson-1 drivers are named with the
> > > > > > > > > keyword "LOONGSON1".
> > > > > > > > > The same is true for these file names.
> > > > > > > > > Therefore, I need to keep the consistency.
> > > > > > > > But I see LS1X_IRQ in drivers/irqchip/Kconfig
> > > > > > > >
> > > > > > > Indeed, that's an exception, which was submitted by Jiaxun several years ago.
> > > > > > > Actually, most drivers of Loongson family use the keyword "LOONGSON"
> > > > > > > for Kconfig and "loongson" for filename.
> > > > > > > Thus I take this keywork as the naming convention.
> > > > > > But I think keeping consistency in a same subsystem is better than
> > > > > > keeping consistency in a same SoC (but cross subsystems).
> > > > > >
> > > > > In my opinion, "LS*X" is too short and may be confused with other SoCs.
> > > > > Meanwhile, there are only four drivers that use this keyword.
> > > > > config I2C_LS2X
> > > > > config LS2K_RESET
> > > > > config LS2X_APB_DMA
> > > > > config LS1X_IRQ
> > > > > Then, my suggestion is to change these "LS*X" to "LOONGSON*" to get a
> > > > > clear meaning.
> > > > We have made a naming conversion some years before with Jiaxun.
> > > > 1, Use "Loongson" for CPU in arch code;
> > > > 2, Use "LS7A" or something like this for bridges and devices.
> > > > 3, For drivers in SoC, if the driver is specific to Loongson-1, use
> > > > LS1X, if it is to Loongson-2, use LS2X, if it is shared by both
> > > > Loongson-1 and Loongson-2, use LOONGSON.
> > > >
> > > OK. But the doesn't the answer the question of confusion, such as
> > > "Freescale LS1021A".
> > > The same problem happens to the filenames.
> > > ./drivers/gpu/drm/nouveau/nvkm/nvfw/ls.c
> > > ./drivers/gpu/drm/nouveau/nvkm/subdev/acr/lsfw.c
> > > ./drivers/gpu/drm/amd/amdgpu/lsdma_v6_0.c
> > > ./drivers/gpu/drm/amd/amdgpu/lsdma_v7_0.c
> > > ./arch/powerpc/platforms/embedded6xx/ls_uart.c
> > > Regarding "LS*X" itself, it contains the wildcard character "X" which
> > > itself is confusing.
> > > Therefore, I don't think "LS*X" is clear enough.
> > >
> The confusion problem remains.
> Honestly, I don't think "LS" is a good short for "LOONGSON".
>
> > > On the other hand, I see "LOONGSON2_*" strings are still there.
> This question remains.
>
> > > In addition, some of "LOONGSON_" definitions are not applicable for
> > > Loongson-1 at all, which breaks your convention.
> > > config SND_SOC_LOONGSON_I2S_PCI /* Loongson-1 doesn't support I2S */
> > > config SND_SOC_LOONGSON_CARD
> > They are shared by LS2K and LS7A.
> >
> > > config DWMAC_LOONGSON1
> > > config DWMAC_LOONGSON /* This glue layer doesn't support Loongson-1 */
> > > config COMMON_CLK_LOONGSON2
> unaddressed
>
> > > config RTC_DRV_LOONGSON
> > RTC is shared by LS2K and LS7A.
> >
> > > config SPI_LOONGSON_CORE
> > > config SPI_LOONGSON_PCI /* N/A for Loongson-1 */
> > > config SPI_LOONGSON_PLATFORM
> > SPI is also shared by LS2K and LS7A.
> >
> > > config LOONGSON2_CPUFREQ
> unaddressed
>
> > > config DRM_LOONGSON /* N/A for Loongson-1 */
> > DRM is also shared by LS2K and LS7A.
> >
> > > config LOONGSON1_WDT
> > > config CLKSRC_LOONGSON1_PWM
> > > config LOONGSON_LIOINTC /* N/A for Loongson-1 */
> > > config LOONGSON_EIOINTC /* N/A for Loongson-1 */
> > > config LOONGSON_HTPIC /* N/A for Loongson-1 */
> > > config LOONGSON_HTVEC /* N/A for Loongson-1 */
> > > config LOONGSON_PCH_PIC /* N/A for Loongson-1 */
> > > config LOONGSON_PCH_MSI /* N/A for Loongson-1 */
> > > config LOONGSON_PCH_LPC /* N/A for Loongson-1 */
> > All interrupt controllers are shared by Loongson-2 and Loongson-3.
> >
> > > config PINCTRL_LOONGSON2
> unaddressed
> > > config LOONGSON2_THERMAL
> ditto
> > > config LOONGSON2_GUTS
> ditto
> > > config LOONGSON2_PM
> ditto
> > > config LOONGSON_LAPTOP /* N/A for Loongson-1 */
> > Laptop driver is shared by Loongson-2 and Loongson-3.
> >
> > > config GPIO_LOONGSON
> > > config GPIO_LOONGSON_64BIT -> N/A for Loongson-1
> > > config GPIO_LOONGSON1
> > GPIO driver is shared by LS2K and LS7A.
> >
> > > config PCI_LOONGSON
> > PCI driver is shared by Loongson-2 and Loongson-3.
>
> You said "if it is shared by both Loongson-1 and Loongson-2, use LOONGSON."
> Now the rule changes from "Loongson-1 and Loongson-2" to "Loongson-2
> and Loongson-3".
> Then, when shall we use "LOONGSON"?
"If it is shared Loongson-1 and Loongson-2" is an example, if you need
an exact description, then "If it is shared by more than one series,
such as Loongson-1 and Loongson-2".
>
> Here is the situation: only are four drivers use "LS*".
> config I2C_LS2X
> config LS2K_RESET
> config LS2X_APB_DMA
> config LS1X_IRQ
> My suggestion is to use the intuitive "LOONGSON*" for both CPU and
> drivers, which is easy to understand.
> And replace the confusing and unclear "LS*X" with "LOONGSON*".
> Use "LOONGSON" when the driver/feature is shared with Loongson-1,
> Loongson-2 and Loongson-3.
> >
> > >
> > > What's your plan about the above Kconfig entries?
> > Yes, there are exceptions indeed, but very rare. And some of the
> > exceptions are due to the limited spare time of Jiaxun and me. But in
> > this case, it is better to keep consistency in the DMA subsystem.
> >
> Sorry, I'm not persuaded.
> Please consider my proposal.
Renaming Kconfig is a bad idea, it breaks existing config files, so we
cannot do that. Part of your proposal is reasonable, for example,
Loongson-3's CPUFreq driver will be LOONGSON3_CPUFREQ, to keep
consistency in the same subsystem. But for the DMA subsystem, it also
should follow the existing style, use LS1X.
Huacai
> Thanks!
>
> > Huacai
> >
> > > Why can't we use LOONGSON1/LOONGSON2 for drivers?
> > >
> > >
> > > > Huacai
> > > >
> > > > >
> > > > > > Huacai
> > > > > >
> > > > > > >
> > > > > > > > Huacai
> > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > > Huacai
> > > > > > > > > >
> > > > > > > > > > > +
> > > > > > > > > > > config LPC18XX_DMAMUX
> > > > > > > > > > > bool "NXP LPC18xx/43xx DMA MUX for PL080"
> > > > > > > > > > > depends on ARCH_LPC18XX || COMPILE_TEST
> > > > > > > > > > > diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> > > > > > > > > > > index dfd40d14e408..b26f6677978a 100644
> > > > > > > > > > > --- a/drivers/dma/Makefile
> > > > > > > > > > > +++ b/drivers/dma/Makefile
> > > > > > > > > > > @@ -47,6 +47,7 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
> > > > > > > > > > > obj-$(CONFIG_INTEL_IOATDMA) += ioat/
> > > > > > > > > > > obj-y += idxd/
> > > > > > > > > > > obj-$(CONFIG_K3_DMA) += k3dma.o
> > > > > > > > > > > +obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
> > > > > > > > > > > obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
> > > > > > > > > > > obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o
> > > > > > > > > > > obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
> > > > > > > > > > > diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson1-apb-dma.c
> > > > > > > > > > > new file mode 100644
> > > > > > > > > > > index 000000000000..d474a2601e6e
> > > > > > > > > > > --- /dev/null
> > > > > > > > > > > +++ b/drivers/dma/loongson1-apb-dma.c
> > > > > > > > > > > @@ -0,0 +1,665 @@
> > > > > > > > > > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > > > > > > > > > +/*
> > > > > > > > > > > + * Driver for Loongson-1 APB DMA Controller
> > > > > > > > > > > + *
> > > > > > > > > > > + * Copyright (C) 2015-2024 Keguang Zhang <keguang.zhang@gmail.com>
> > > > > > > > > > > + */
> > > > > > > > > > > +
> > > > > > > > > > > +#include <linux/dmapool.h>
> > > > > > > > > > > +#include <linux/dma-mapping.h>
> > > > > > > > > > > +#include <linux/init.h>
> > > > > > > > > > > +#include <linux/interrupt.h>
> > > > > > > > > > > +#include <linux/iopoll.h>
> > > > > > > > > > > +#include <linux/module.h>
> > > > > > > > > > > +#include <linux/of.h>
> > > > > > > > > > > +#include <linux/of_dma.h>
> > > > > > > > > > > +#include <linux/platform_device.h>
> > > > > > > > > > > +#include <linux/slab.h>
> > > > > > > > > > > +
> > > > > > > > > > > +#include "dmaengine.h"
> > > > > > > > > > > +#include "virt-dma.h"
> > > > > > > > > > > +
> > > > > > > > > > > +/* Loongson-1 DMA Control Register */
> > > > > > > > > > > +#define DMA_CTRL 0x0
> > > > > > > > > > > +
> > > > > > > > > > > +/* DMA Control Register Bits */
> > > > > > > > > > > +#define DMA_STOP BIT(4)
> > > > > > > > > > > +#define DMA_START BIT(3)
> > > > > > > > > > > +#define DMA_ASK_VALID BIT(2)
> > > > > > > > > > > +
> > > > > > > > > > > +#define DMA_ADDR_MASK GENMASK(31, 6)
> > > > > > > > > > > +
> > > > > > > > > > > +/* DMA Next Field Bits */
> > > > > > > > > > > +#define DMA_NEXT_VALID BIT(0)
> > > > > > > > > > > +
> > > > > > > > > > > +/* DMA Command Field Bits */
> > > > > > > > > > > +#define DMA_RAM2DEV BIT(12)
> > > > > > > > > > > +#define DMA_INT BIT(1)
> > > > > > > > > > > +#define DMA_INT_MASK BIT(0)
> > > > > > > > > > > +
> > > > > > > > > > > +#define LS1X_DMA_MAX_CHANNELS 3
> > > > > > > > > > > +
> > > > > > > > > > > +/* Size of allocations for hardware descriptors */
> > > > > > > > > > > +#define LS1X_DMA_DESCS_SIZE PAGE_SIZE
> > > > > > > > > > > +#define LS1X_DMA_MAX_DESC \
> > > > > > > > > > > + (LS1X_DMA_DESCS_SIZE / sizeof(struct ls1x_dma_hwdesc))
> > > > > > > > > > > +
> > > > > > > > > > > +struct ls1x_dma_hwdesc {
> > > > > > > > > > > + u32 next; /* next descriptor address */
> > > > > > > > > > > + u32 saddr; /* memory DMA address */
> > > > > > > > > > > + u32 daddr; /* device DMA address */
> > > > > > > > > > > + u32 length;
> > > > > > > > > > > + u32 stride;
> > > > > > > > > > > + u32 cycles;
> > > > > > > > > > > + u32 cmd;
> > > > > > > > > > > + u32 stats;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct ls1x_dma_desc {
> > > > > > > > > > > + struct virt_dma_desc vdesc;
> > > > > > > > > > > + enum dma_transfer_direction dir;
> > > > > > > > > > > + enum dma_transaction_type type;
> > > > > > > > > > > + unsigned int bus_width;
> > > > > > > > > > > +
> > > > > > > > > > > + unsigned int nr_descs; /* number of descriptors */
> > > > > > > > > > > +
> > > > > > > > > > > + struct ls1x_dma_hwdesc *hwdesc;
> > > > > > > > > > > + dma_addr_t hwdesc_phys;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct ls1x_dma_chan {
> > > > > > > > > > > + struct virt_dma_chan vchan;
> > > > > > > > > > > + struct dma_pool *desc_pool;
> > > > > > > > > > > + phys_addr_t src_addr;
> > > > > > > > > > > + phys_addr_t dst_addr;
> > > > > > > > > > > + enum dma_slave_buswidth src_addr_width;
> > > > > > > > > > > + enum dma_slave_buswidth dst_addr_width;
> > > > > > > > > > > +
> > > > > > > > > > > + void __iomem *reg_base;
> > > > > > > > > > > + int irq;
> > > > > > > > > > > +
> > > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > > +
> > > > > > > > > > > + struct ls1x_dma_hwdesc *curr_hwdesc;
> > > > > > > > > > > + dma_addr_t curr_hwdesc_phys;
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +struct ls1x_dma {
> > > > > > > > > > > + struct dma_device ddev;
> > > > > > > > > > > + void __iomem *reg_base;
> > > > > > > > > > > +
> > > > > > > > > > > + unsigned int nr_chans;
> > > > > > > > > > > + struct ls1x_dma_chan chan[];
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +#define to_ls1x_dma_chan(dchan) \
> > > > > > > > > > > + container_of(dchan, struct ls1x_dma_chan, vchan.chan)
> > > > > > > > > > > +
> > > > > > > > > > > +#define to_ls1x_dma_desc(vd) \
> > > > > > > > > > > + container_of(vd, struct ls1x_dma_desc, vdesc)
> > > > > > > > > > > +
> > > > > > > > > > > +/* macros for registers read/write */
> > > > > > > > > > > +#define chan_readl(chan, off) \
> > > > > > > > > > > + readl((chan)->reg_base + (off))
> > > > > > > > > > > +
> > > > > > > > > > > +#define chan_writel(chan, off, val) \
> > > > > > > > > > > + writel((val), (chan)->reg_base + (off))
> > > > > > > > > > > +
> > > > > > > > > > > +static inline struct device *chan2dev(struct dma_chan *chan)
> > > > > > > > > > > +{
> > > > > > > > > > > + return &chan->dev->device;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static inline int ls1x_dma_query(struct ls1x_dma_chan *chan,
> > > > > > > > > > > + dma_addr_t *hwdesc_phys)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct dma_chan *dchan = &chan->vchan.chan;
> > > > > > > > > > > + int val, ret;
> > > > > > > > > > > +
> > > > > > > > > > > + val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > > > > > > > > + val |= DMA_ASK_VALID;
> > > > > > > > > > > + val |= dchan->chan_id;
> > > > > > > > > > > + chan_writel(chan, DMA_CTRL, val);
> > > > > > > > > > > + ret = readl_poll_timeout_atomic(chan->reg_base + DMA_CTRL, val,
> > > > > > > > > > > + !(val & DMA_ASK_VALID), 0, 3000);
> > > > > > > > > > > + if (ret)
> > > > > > > > > > > + dev_err(chan2dev(dchan), "failed to query DMA\n");
> > > > > > > > > > > +
> > > > > > > > > > > + return ret;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static inline int ls1x_dma_start(struct ls1x_dma_chan *chan,
> > > > > > > > > > > + dma_addr_t *hwdesc_phys)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct dma_chan *dchan = &chan->vchan.chan;
> > > > > > > > > > > + int val, ret;
> > > > > > > > > > > +
> > > > > > > > > > > + dev_dbg(chan2dev(dchan), "cookie=%d, starting hwdesc=%x\n",
> > > > > > > > > > > + dchan->cookie, *hwdesc_phys);
> > > > > > > > > > > +
> > > > > > > > > > > + val = *hwdesc_phys & DMA_ADDR_MASK;
> > > > > > > > > > > + val |= DMA_START;
> > > > > > > > > > > + val |= dchan->chan_id;
> > > > > > > > > > > + chan_writel(chan, DMA_CTRL, val);
> > > > > > > > > > > + ret = readl_poll_timeout(chan->reg_base + DMA_CTRL, val,
> > > > > > > > > > > + !(val & DMA_START), 0, 3000);
> > > > > > > > > > > + if (ret)
> > > > > > > > > > > + dev_err(chan2dev(dchan), "failed to start DMA\n");
> > > > > > > > > > > +
> > > > > > > > > > > + return ret;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static inline void ls1x_dma_stop(struct ls1x_dma_chan *chan)
> > > > > > > > > > > +{
> > > > > > > > > > > + chan_writel(chan, DMA_CTRL, chan_readl(chan, DMA_CTRL) | DMA_STOP);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static void ls1x_dma_free_chan_resources(struct dma_chan *dchan)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > +
> > > > > > > > > > > + dma_free_coherent(chan2dev(dchan), sizeof(struct ls1x_dma_hwdesc),
> > > > > > > > > > > + chan->curr_hwdesc, chan->curr_hwdesc_phys);
> > > > > > > > > > > + vchan_free_chan_resources(&chan->vchan);
> > > > > > > > > > > + dma_pool_destroy(chan->desc_pool);
> > > > > > > > > > > + chan->desc_pool = NULL;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_alloc_chan_resources(struct dma_chan *dchan)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > +
> > > > > > > > > > > + chan->desc_pool = dma_pool_create(dma_chan_name(dchan),
> > > > > > > > > > > + chan2dev(dchan),
> > > > > > > > > > > + sizeof(struct ls1x_dma_hwdesc),
> > > > > > > > > > > + __alignof__(struct ls1x_dma_hwdesc),
> > > > > > > > > > > + 0);
> > > > > > > > > > > + if (!chan->desc_pool)
> > > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > > +
> > > > > > > > > > > + /* allocate memory for querying current HW descriptor */
> > > > > > > > > > > + dma_set_coherent_mask(chan2dev(dchan), DMA_BIT_MASK(32));
> > > > > > > > > > > + chan->curr_hwdesc = dma_alloc_coherent(chan2dev(dchan),
> > > > > > > > > > > + sizeof(struct ls1x_dma_hwdesc),
> > > > > > > > > > > + &chan->curr_hwdesc_phys,
> > > > > > > > > > > + GFP_KERNEL);
> > > > > > > > > > > + if (!chan->curr_hwdesc)
> > > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > > +
> > > > > > > > > > > + return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static void ls1x_dma_free_desc(struct virt_dma_desc *vdesc)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_desc *desc = to_ls1x_dma_desc(vdesc);
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(vdesc->tx.chan);
> > > > > > > > > > > +
> > > > > > > > > > > + dma_pool_free(chan->desc_pool, desc->hwdesc, desc->hwdesc_phys);
> > > > > > > > > > > + chan->desc = NULL;
> > > > > > > > > > > + kfree(desc);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static struct ls1x_dma_desc *
> > > > > > > > > > > +ls1x_dma_alloc_desc(struct dma_chan *dchan, int sg_len,
> > > > > > > > > > > + enum dma_transfer_direction direction,
> > > > > > > > > > > + enum dma_transaction_type type)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > > +
> > > > > > > > > > > + if (sg_len > LS1X_DMA_MAX_DESC) {
> > > > > > > > > > > + dev_err(chan2dev(dchan), "sg_len %u exceeds limit %lu",
> > > > > > > > > > > + sg_len, LS1X_DMA_MAX_DESC);
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
> > > > > > > > > > > + if (!desc)
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > +
> > > > > > > > > > > + /* allocate HW descriptors */
> > > > > > > > > > > + desc->hwdesc = dma_pool_zalloc(chan->desc_pool, GFP_NOWAIT,
> > > > > > > > > > > + &desc->hwdesc_phys);
> > > > > > > > > > > + if (!desc->hwdesc) {
> > > > > > > > > > > + dev_err(chan2dev(dchan), "failed to alloc HW descriptors\n");
> > > > > > > > > > > + ls1x_dma_free_desc(&desc->vdesc);
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + desc->dir = direction;
> > > > > > > > > > > + desc->type = type;
> > > > > > > > > > > + desc->nr_descs = sg_len;
> > > > > > > > > > > +
> > > > > > > > > > > + return desc;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_setup_hwdescs(struct dma_chan *dchan,
> > > > > > > > > > > + struct ls1x_dma_desc *desc,
> > > > > > > > > > > + struct scatterlist *sgl, unsigned int sg_len)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + dma_addr_t next_hwdesc_phys = desc->hwdesc_phys;
> > > > > > > > > > > +
> > > > > > > > > > > + struct scatterlist *sg;
> > > > > > > > > > > + unsigned int dev_addr, cmd, i;
> > > > > > > > > > > +
> > > > > > > > > > > + switch (desc->dir) {
> > > > > > > > > > > + case DMA_MEM_TO_DEV:
> > > > > > > > > > > + dev_addr = chan->dst_addr;
> > > > > > > > > > > + desc->bus_width = chan->dst_addr_width;
> > > > > > > > > > > + cmd = DMA_RAM2DEV | DMA_INT;
> > > > > > > > > > > + break;
> > > > > > > > > > > + case DMA_DEV_TO_MEM:
> > > > > > > > > > > + dev_addr = chan->src_addr;
> > > > > > > > > > > + desc->bus_width = chan->src_addr_width;
> > > > > > > > > > > + cmd = DMA_INT;
> > > > > > > > > > > + break;
> > > > > > > > > > > + default:
> > > > > > > > > > > + dev_err(chan2dev(dchan), "unsupported DMA direction: %s\n",
> > > > > > > > > > > + dmaengine_get_direction_text(desc->dir));
> > > > > > > > > > > + return -EINVAL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + /* setup HW descriptors */
> > > > > > > > > > > + for_each_sg(sgl, sg, sg_len, i) {
> > > > > > > > > > > + dma_addr_t buf_addr = sg_dma_address(sg);
> > > > > > > > > > > + size_t buf_len = sg_dma_len(sg);
> > > > > > > > > > > + struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > > > > > > > > +
> > > > > > > > > > > + if (!is_dma_copy_aligned(dchan->device, buf_addr, 0, buf_len)) {
> > > > > > > > > > > + dev_err(chan2dev(dchan), "buffer is not aligned!\n");
> > > > > > > > > > > + return -EINVAL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + hwdesc->saddr = buf_addr;
> > > > > > > > > > > + hwdesc->daddr = dev_addr;
> > > > > > > > > > > + hwdesc->length = buf_len / desc->bus_width;
> > > > > > > > > > > + hwdesc->stride = 0;
> > > > > > > > > > > + hwdesc->cycles = 1;
> > > > > > > > > > > + hwdesc->cmd = cmd;
> > > > > > > > > > > +
> > > > > > > > > > > + if (i) {
> > > > > > > > > > > + next_hwdesc_phys += sizeof(*hwdesc);
> > > > > > > > > > > + desc->hwdesc[i - 1].next = next_hwdesc_phys
> > > > > > > > > > > + | DMA_NEXT_VALID;
> > > > > > > > > > > + }
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + if (desc->type == DMA_CYCLIC)
> > > > > > > > > > > + desc->hwdesc[i - 1].next = desc->hwdesc_phys | DMA_NEXT_VALID;
> > > > > > > > > > > +
> > > > > > > > > > > + for_each_sg(sgl, sg, sg_len, i) {
> > > > > > > > > > > + struct ls1x_dma_hwdesc *hwdesc = &desc->hwdesc[i];
> > > > > > > > > > > +
> > > > > > > > > > > + print_hex_dump_debug("HW DESC: ", DUMP_PREFIX_OFFSET, 16, 4,
> > > > > > > > > > > + hwdesc, sizeof(*hwdesc), false);
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static struct dma_async_tx_descriptor *
> > > > > > > > > > > +ls1x_dma_prep_slave_sg(struct dma_chan *dchan,
> > > > > > > > > > > + struct scatterlist *sgl, unsigned int sg_len,
> > > > > > > > > > > + enum dma_transfer_direction direction,
> > > > > > > > > > > + unsigned long flags, void *context)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > > +
> > > > > > > > > > > + dev_dbg(chan2dev(dchan), "sg_len=%u flags=0x%lx dir=%s\n",
> > > > > > > > > > > + sg_len, flags, dmaengine_get_direction_text(direction));
> > > > > > > > > > > +
> > > > > > > > > > > + desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_SLAVE);
> > > > > > > > > > > + if (!desc)
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > +
> > > > > > > > > > > + if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > > > > > > > > + ls1x_dma_free_desc(&desc->vdesc);
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static struct dma_async_tx_descriptor *
> > > > > > > > > > > +ls1x_dma_prep_dma_cyclic(struct dma_chan *dchan,
> > > > > > > > > > > + dma_addr_t buf_addr, size_t buf_len, size_t period_len,
> > > > > > > > > > > + enum dma_transfer_direction direction,
> > > > > > > > > > > + unsigned long flags)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + struct ls1x_dma_desc *desc;
> > > > > > > > > > > + struct scatterlist *sgl;
> > > > > > > > > > > + unsigned int sg_len;
> > > > > > > > > > > + unsigned int i;
> > > > > > > > > > > +
> > > > > > > > > > > + dev_dbg(chan2dev(dchan),
> > > > > > > > > > > + "buf_len=%d period_len=%zu flags=0x%lx dir=%s\n", buf_len,
> > > > > > > > > > > + period_len, flags, dmaengine_get_direction_text(direction));
> > > > > > > > > > > +
> > > > > > > > > > > + sg_len = buf_len / period_len;
> > > > > > > > > > > + desc = ls1x_dma_alloc_desc(dchan, sg_len, direction, DMA_CYCLIC);
> > > > > > > > > > > + if (!desc)
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > +
> > > > > > > > > > > + /* allocate the scatterlist */
> > > > > > > > > > > + sgl = kmalloc_array(sg_len, sizeof(*sgl), GFP_NOWAIT);
> > > > > > > > > > > + if (!sgl)
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > +
> > > > > > > > > > > + sg_init_table(sgl, sg_len);
> > > > > > > > > > > + for (i = 0; i < sg_len; ++i) {
> > > > > > > > > > > + sg_set_page(&sgl[i], pfn_to_page(PFN_DOWN(buf_addr)),
> > > > > > > > > > > + period_len, offset_in_page(buf_addr));
> > > > > > > > > > > + sg_dma_address(&sgl[i]) = buf_addr;
> > > > > > > > > > > + sg_dma_len(&sgl[i]) = period_len;
> > > > > > > > > > > + buf_addr += period_len;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + if (ls1x_dma_setup_hwdescs(dchan, desc, sgl, sg_len)) {
> > > > > > > > > > > + ls1x_dma_free_desc(&desc->vdesc);
> > > > > > > > > > > + return NULL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + kfree(sgl);
> > > > > > > > > > > +
> > > > > > > > > > > + return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_slave_config(struct dma_chan *dchan,
> > > > > > > > > > > + struct dma_slave_config *config)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > +
> > > > > > > > > > > + chan->src_addr = config->src_addr;
> > > > > > > > > > > + chan->src_addr_width = config->src_addr_width;
> > > > > > > > > > > + chan->dst_addr = config->dst_addr;
> > > > > > > > > > > + chan->dst_addr_width = config->dst_addr_width;
> > > > > > > > > > > +
> > > > > > > > > > > + return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_pause(struct dma_chan *dchan)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + unsigned long flags;
> > > > > > > > > > > + int ret;
> > > > > > > > > > > +
> > > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > > + ret = ls1x_dma_query(chan, &chan->curr_hwdesc_phys);
> > > > > > > > > > > + if (!ret)
> > > > > > > > > > > + ls1x_dma_stop(chan);
> > > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > > +
> > > > > > > > > > > + return ret;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_resume(struct dma_chan *dchan)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + unsigned long flags;
> > > > > > > > > > > + int ret;
> > > > > > > > > > > +
> > > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > > + ret = ls1x_dma_start(chan, &chan->curr_hwdesc_phys);
> > > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > > +
> > > > > > > > > > > + return ret;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_terminate_all(struct dma_chan *dchan)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + unsigned long flags;
> > > > > > > > > > > + LIST_HEAD(head);
> > > > > > > > > > > +
> > > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > > + ls1x_dma_stop(chan);
> > > > > > > > > > > + vchan_get_all_descriptors(&chan->vchan, &head);
> > > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > > +
> > > > > > > > > > > + vchan_dma_desc_free_list(&chan->vchan, &head);
> > > > > > > > > > > +
> > > > > > > > > > > + return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static enum dma_status ls1x_dma_tx_status(struct dma_chan *dchan,
> > > > > > > > > > > + dma_cookie_t cookie,
> > > > > > > > > > > + struct dma_tx_state *state)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + struct virt_dma_desc *vdesc;
> > > > > > > > > > > + enum dma_status status;
> > > > > > > > > > > + size_t bytes = 0;
> > > > > > > > > > > + unsigned long flags;
> > > > > > > > > > > +
> > > > > > > > > > > + status = dma_cookie_status(dchan, cookie, state);
> > > > > > > > > > > + if (status == DMA_COMPLETE)
> > > > > > > > > > > + return status;
> > > > > > > > > > > +
> > > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > > + vdesc = vchan_find_desc(&chan->vchan, cookie);
> > > > > > > > > > > + if (chan->desc && cookie == chan->desc->vdesc.tx.cookie) {
> > > > > > > > > > > + struct ls1x_dma_desc *desc = chan->desc;
> > > > > > > > > > > + int i;
> > > > > > > > > > > +
> > > > > > > > > > > + if (ls1x_dma_query(chan, &chan->curr_hwdesc_phys))
> > > > > > > > > > > + return status;
> > > > > > > > > > > +
> > > > > > > > > > > + /* locate the current HW descriptor */
> > > > > > > > > > > + for (i = 0; i < desc->nr_descs; i++)
> > > > > > > > > > > + if (desc->hwdesc[i].next == chan->curr_hwdesc->next)
> > > > > > > > > > > + break;
> > > > > > > > > > > +
> > > > > > > > > > > + /* count the residues */
> > > > > > > > > > > + for (; i < desc->nr_descs; i++)
> > > > > > > > > > > + bytes += desc->hwdesc[i].length * desc->bus_width;
> > > > > > > > > > > +
> > > > > > > > > > > + dma_set_residue(state, bytes);
> > > > > > > > > > > + }
> > > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > > +
> > > > > > > > > > > + return status;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static void ls1x_dma_issue_pending(struct dma_chan *dchan)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = to_ls1x_dma_chan(dchan);
> > > > > > > > > > > + struct virt_dma_desc *vdesc;
> > > > > > > > > > > + unsigned long flags;
> > > > > > > > > > > +
> > > > > > > > > > > + spin_lock_irqsave(&chan->vchan.lock, flags);
> > > > > > > > > > > + if (vchan_issue_pending(&chan->vchan) && !chan->desc) {
> > > > > > > > > > > + vdesc = vchan_next_desc(&chan->vchan);
> > > > > > > > > > > + if (!vdesc) {
> > > > > > > > > > > + chan->desc = NULL;
> > > > > > > > > > > + return;
> > > > > > > > > > > + }
> > > > > > > > > > > + chan->desc = to_ls1x_dma_desc(vdesc);
> > > > > > > > > > > + ls1x_dma_start(chan, &chan->desc->hwdesc_phys);
> > > > > > > > > > > + }
> > > > > > > > > > > + spin_unlock_irqrestore(&chan->vchan.lock, flags);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static irqreturn_t ls1x_dma_irq_handler(int irq, void *data)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma_chan *chan = data;
> > > > > > > > > > > + struct ls1x_dma_desc *desc = chan->desc;
> > > > > > > > > > > + struct dma_chan *dchan = &chan->vchan.chan;
> > > > > > > > > > > +
> > > > > > > > > > > + if (!desc) {
> > > > > > > > > > > + dev_warn(chan2dev(dchan),
> > > > > > > > > > > + "IRQ %d with no active descriptor on channel %d\n",
> > > > > > > > > > > + irq, dchan->chan_id);
> > > > > > > > > > > + return IRQ_NONE;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + dev_dbg(chan2dev(dchan), "DMA IRQ %d on channel %d\n", irq,
> > > > > > > > > > > + dchan->chan_id);
> > > > > > > > > > > +
> > > > > > > > > > > + spin_lock(&chan->vchan.lock);
> > > > > > > > > > > +
> > > > > > > > > > > + if (desc->type == DMA_CYCLIC) {
> > > > > > > > > > > + vchan_cyclic_callback(&desc->vdesc);
> > > > > > > > > > > + } else {
> > > > > > > > > > > + list_del(&desc->vdesc.node);
> > > > > > > > > > > + vchan_cookie_complete(&desc->vdesc);
> > > > > > > > > > > + chan->desc = NULL;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + spin_unlock(&chan->vchan.lock);
> > > > > > > > > > > + return IRQ_HANDLED;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_chan_probe(struct platform_device *pdev,
> > > > > > > > > > > + struct ls1x_dma *dma, int chan_id)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct device *dev = &pdev->dev;
> > > > > > > > > > > + struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > > > > > > > > + char pdev_irqname[4];
> > > > > > > > > > > + char *irqname;
> > > > > > > > > > > + int ret;
> > > > > > > > > > > +
> > > > > > > > > > > + sprintf(pdev_irqname, "ch%u", chan_id);
> > > > > > > > > > > + chan->irq = platform_get_irq_byname(pdev, pdev_irqname);
> > > > > > > > > > > + if (chan->irq < 0)
> > > > > > > > > > > + return -ENODEV;
> > > > > > > > > > > +
> > > > > > > > > > > + irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:%s",
> > > > > > > > > > > + dev_name(dev), pdev_irqname);
> > > > > > > > > > > + if (!irqname)
> > > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > > +
> > > > > > > > > > > + ret = devm_request_irq(dev, chan->irq, ls1x_dma_irq_handler,
> > > > > > > > > > > + IRQF_SHARED, irqname, chan);
> > > > > > > > > > > + if (ret)
> > > > > > > > > > > + return dev_err_probe(dev, ret,
> > > > > > > > > > > + "failed to request IRQ %u!\n", chan->irq);
> > > > > > > > > > > +
> > > > > > > > > > > + chan->reg_base = dma->reg_base;
> > > > > > > > > > > + chan->vchan.desc_free = ls1x_dma_free_desc;
> > > > > > > > > > > + vchan_init(&chan->vchan, &dma->ddev);
> > > > > > > > > > > + dev_info(dev, "%s (irq %d) initialized\n", pdev_irqname, chan->irq);
> > > > > > > > > > > +
> > > > > > > > > > > + return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static void ls1x_dma_chan_remove(struct ls1x_dma *dma, int chan_id)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct device *dev = dma->ddev.dev;
> > > > > > > > > > > + struct ls1x_dma_chan *chan = &dma->chan[chan_id];
> > > > > > > > > > > +
> > > > > > > > > > > + devm_free_irq(dev, chan->irq, chan);
> > > > > > > > > > > + list_del(&chan->vchan.chan.device_node);
> > > > > > > > > > > + tasklet_kill(&chan->vchan.task);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static int ls1x_dma_probe(struct platform_device *pdev)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct device *dev = &pdev->dev;
> > > > > > > > > > > + struct dma_device *ddev;
> > > > > > > > > > > + struct ls1x_dma *dma;
> > > > > > > > > > > + int nr_chans, ret, i;
> > > > > > > > > > > +
> > > > > > > > > > > + nr_chans = platform_irq_count(pdev);
> > > > > > > > > > > + if (nr_chans <= 0)
> > > > > > > > > > > + return nr_chans;
> > > > > > > > > > > + if (nr_chans > LS1X_DMA_MAX_CHANNELS)
> > > > > > > > > > > + return dev_err_probe(dev, -EINVAL,
> > > > > > > > > > > + "nr_chans=%d exceeds the maximum\n",
> > > > > > > > > > > + nr_chans);
> > > > > > > > > > > +
> > > > > > > > > > > + dma = devm_kzalloc(dev, struct_size(dma, chan, nr_chans), GFP_KERNEL);
> > > > > > > > > > > + if (!dma)
> > > > > > > > > > > + return -ENOMEM;
> > > > > > > > > > > +
> > > > > > > > > > > + /* initialize DMA device */
> > > > > > > > > > > + dma->reg_base = devm_platform_ioremap_resource(pdev, 0);
> > > > > > > > > > > + if (IS_ERR(dma->reg_base))
> > > > > > > > > > > + return PTR_ERR(dma->reg_base);
> > > > > > > > > > > +
> > > > > > > > > > > + ddev = &dma->ddev;
> > > > > > > > > > > + ddev->dev = dev;
> > > > > > > > > > > + ddev->copy_align = DMAENGINE_ALIGN_4_BYTES;
> > > > > > > > > > > + ddev->src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > > > > > > > > + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > > > > > > > > + ddev->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
> > > > > > > > > > > + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
> > > > > > > > > > > + ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
> > > > > > > > > > > + ddev->max_sg_burst = LS1X_DMA_MAX_DESC;
> > > > > > > > > > > + ddev->residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
> > > > > > > > > > > + ddev->device_alloc_chan_resources = ls1x_dma_alloc_chan_resources;
> > > > > > > > > > > + ddev->device_free_chan_resources = ls1x_dma_free_chan_resources;
> > > > > > > > > > > + ddev->device_prep_slave_sg = ls1x_dma_prep_slave_sg;
> > > > > > > > > > > + ddev->device_prep_dma_cyclic = ls1x_dma_prep_dma_cyclic;
> > > > > > > > > > > + ddev->device_config = ls1x_dma_slave_config;
> > > > > > > > > > > + ddev->device_pause = ls1x_dma_pause;
> > > > > > > > > > > + ddev->device_resume = ls1x_dma_resume;
> > > > > > > > > > > + ddev->device_terminate_all = ls1x_dma_terminate_all;
> > > > > > > > > > > + ddev->device_tx_status = ls1x_dma_tx_status;
> > > > > > > > > > > + ddev->device_issue_pending = ls1x_dma_issue_pending;
> > > > > > > > > > > +
> > > > > > > > > > > + dma_cap_set(DMA_SLAVE, ddev->cap_mask);
> > > > > > > > > > > + INIT_LIST_HEAD(&ddev->channels);
> > > > > > > > > > > +
> > > > > > > > > > > + /* initialize DMA channels */
> > > > > > > > > > > + for (i = 0; i < nr_chans; i++) {
> > > > > > > > > > > + ret = ls1x_dma_chan_probe(pdev, dma, i);
> > > > > > > > > > > + if (ret)
> > > > > > > > > > > + return ret;
> > > > > > > > > > > + }
> > > > > > > > > > > + dma->nr_chans = nr_chans;
> > > > > > > > > > > +
> > > > > > > > > > > + ret = dmaenginem_async_device_register(ddev);
> > > > > > > > > > > + if (ret) {
> > > > > > > > > > > + dev_err(dev, "failed to register DMA device! %d\n", ret);
> > > > > > > > > > > + return ret;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + ret =
> > > > > > > > > > > + of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id,
> > > > > > > > > > > + ddev);
> > > > > > > > > > > + if (ret) {
> > > > > > > > > > > + dev_err(dev, "failed to register DMA controller! %d\n", ret);
> > > > > > > > > > > + return ret;
> > > > > > > > > > > + }
> > > > > > > > > > > +
> > > > > > > > > > > + platform_set_drvdata(pdev, dma);
> > > > > > > > > > > + dev_info(dev, "Loongson1 DMA driver registered\n");
> > > > > > > > > > > +
> > > > > > > > > > > + return 0;
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static void ls1x_dma_remove(struct platform_device *pdev)
> > > > > > > > > > > +{
> > > > > > > > > > > + struct ls1x_dma *dma = platform_get_drvdata(pdev);
> > > > > > > > > > > + int i;
> > > > > > > > > > > +
> > > > > > > > > > > + of_dma_controller_free(pdev->dev.of_node);
> > > > > > > > > > > +
> > > > > > > > > > > + for (i = 0; i < dma->nr_chans; i++)
> > > > > > > > > > > + ls1x_dma_chan_remove(dma, i);
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static const struct of_device_id ls1x_dma_match[] = {
> > > > > > > > > > > + { .compatible = "loongson,ls1b-apbdma" },
> > > > > > > > > > > + { .compatible = "loongson,ls1c-apbdma" },
> > > > > > > > > > > + { /* sentinel */ }
> > > > > > > > > > > +};
> > > > > > > > > > > +MODULE_DEVICE_TABLE(of, ls1x_dma_match);
> > > > > > > > > > > +
> > > > > > > > > > > +static struct platform_driver ls1x_dma_driver = {
> > > > > > > > > > > + .probe = ls1x_dma_probe,
> > > > > > > > > > > + .remove_new = ls1x_dma_remove,
> > > > > > > > > > > + .driver = {
> > > > > > > > > > > + .name = KBUILD_MODNAME,
> > > > > > > > > > > + .of_match_table = ls1x_dma_match,
> > > > > > > > > > > + },
> > > > > > > > > > > +};
> > > > > > > > > > > +
> > > > > > > > > > > +module_platform_driver(ls1x_dma_driver);
> > > > > > > > > > > +
> > > > > > > > > > > +MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
> > > > > > > > > > > +MODULE_DESCRIPTION("Loongson-1 APB DMA Controller driver");
> > > > > > > > > > > +MODULE_LICENSE("GPL");
> > > > > > > > > > >
> > > > > > > > > > > --
> > > > > > > > > > > 2.40.1
> > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > --
> > > > > > > > > Best regards,
> > > > > > > > >
> > > > > > > > > Keguang Zhang
> > > > > > > > >
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > > > --
> > > > > > > Best regards,
> > > > > > >
> > > > > > > Keguang Zhang
> > > > >
> > > > >
> > > > >
> > > > > --
> > > > > Best regards,
> > > > >
> > > > > Keguang Zhang
> > >
> > >
> > >
> > > --
> > > Best regards,
> > >
> > > Keguang Zhang
>
>
>
> --
> Best regards,
>
> Keguang Zhang
^ permalink raw reply
* Re: [Patch v2 1/2] dt-bindings: make sid and broadcast reg optional
From: Sumit Gupta @ 2024-04-03 14:39 UTC (permalink / raw)
To: Krzysztof Kozlowski, Jon Hunter, robh, conor+dt, maz,
mark.rutland, treding
Cc: devicetree, linux-kernel, linux-tegra, amhetre, bbasu,
Sumit Gupta
In-Reply-To: <c51653d1-1a76-45de-93e0-ee5d341649e9@linaro.org>
>>
>> On 02/04/2024 14:26, Sumit Gupta wrote:
>>> MC SID and Broadbast channel register access is restricted for Guest VM.
>>> Make both the regions as optional for SoC's from Tegra186 onwards.
>>> Tegra MC driver will skip access to the restricted registers from Guest
>>> if the respective regions are not present in the memory-controller node
>>> of Guest DT.
>>>
>>> Signed-off-by: Sumit Gupta <sumitg@nvidia.com>
>>> ---
>>> .../memory-controllers/nvidia,tegra186-mc.yaml | 18 +++++++++---------
>>> 1 file changed, 9 insertions(+), 9 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml
>>> index 935d63d181d9..c52c259f7ec5 100644
>>> --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml
>>> +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra186-mc.yaml
>>> @@ -146,17 +146,17 @@ allOf:
>>> then:
>>> properties:
>>> reg:
>>> - maxItems: 6
>>> + maxItems: 4
>>
>> minItems?
>>
>
> If the intention was to make it variable, then yes, missing minItems.
> But more important: why patch was sent without any testing?
>
> Best regards,
> Krzysztof
>
I tested yaml file after doing the change for Tegra194 and Tegra234.
Changed the Tegra186 entry later and didn't verify that. My bad as
missed the obvious. Will correct the yaml file and send v3.
Will wait if any comments on 'Patch 2' before sending v3.
Best Regards,
Sumit Gupta
^ permalink raw reply
* Re: [PATCH net-next v6 11/17] dt-bindings: net: pse-pd: Add another way of describing several PSE PIs
From: Rob Herring @ 2024-04-03 14:44 UTC (permalink / raw)
To: Oleksij Rempel
Cc: Kory Maincent, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Jonathan Corbet, Luis Chamberlain, Russ Weight,
Greg Kroah-Hartman, Rafael J. Wysocki, Krzysztof Kozlowski,
Conor Dooley, Mark Brown, Frank Rowand, Andrew Lunn,
Heiner Kallweit, Russell King, Thomas Petazzoni, netdev,
linux-kernel, linux-doc, devicetree, Dent Project
In-Reply-To: <ZgworgDAXXOpf3QV@pengutronix.de>
On Tue, Apr 02, 2024 at 05:47:58PM +0200, Oleksij Rempel wrote:
> On Tue, Apr 02, 2024 at 08:26:37AM -0500, Rob Herring wrote:
> > > + pairsets:
> > > + $ref: /schemas/types.yaml#/definitions/phandle-array
> > > + description:
> > > + List of phandles, each pointing to the power supply for the
> > > + corresponding pairset named in 'pairset-names'. This property
> > > + aligns with IEEE 802.3-2022, Section 33.2.3 and 145.2.4.
> > > + PSE Pinout Alternatives (as per IEEE 802.3-2022 Table 145\u20133)
> > > + |-----------|---------------|---------------|---------------|---------------|
> > > + | Conductor | Alternative A | Alternative A | Alternative B | Alternative B |
> > > + | | (MDI-X) | (MDI) | (X) | (S) |
> > > + |-----------|---------------|---------------|---------------|---------------|
> > > + | 1 | Negative VPSE | Positive VPSE | \u2014 | \u2014 |
> > > + | 2 | Negative VPSE | Positive VPSE | \u2014 | \u2014 |
> > > + | 3 | Positive VPSE | Negative VPSE | \u2014 | \u2014 |
> > > + | 4 | \u2014 | \u2014 | Negative VPSE | Positive VPSE |
> > > + | 5 | \u2014 | \u2014 | Negative VPSE | Positive VPSE |
> > > + | 6 | Positive VPSE | Negative VPSE | \u2014 | \u2014 |
> > > + | 7 | \u2014 | \u2014 | Positive VPSE | Negative VPSE |
> > > + | 8 | \u2014 | \u2014 | Positive VPSE | Negative VPSE |
> > > + minItems: 1
> > > + maxItems: 2
> >
> > "pairsets" does not follow the normal design pattern of foos, foo-names,
> > and #foo-cells. You could add #foo-cells I suppose, but what would cells
> > convey? I don't think it's a good fit for what you need.
> >
> > The other oddity is the number of entries and the names are fixed. That
> > is usually defined per consumer.
> >
> > As each entry is just a power rail, why can't the regulator binding be
> > used here?
>
> I'm not against describing it consequent with regulator till the wire
> end, but right now I have no idea how it should be described by using
> regulator bindings. There are maximum 2 rails going in to PSE PI on one
> side and 4 rails with at least 5 combinations supported by standard on
> other side. Instead of inventing anything new, I suggested to describe
> supported output combinations by using IEEE 802.3 standard.
There's 4 combinations above, what's the 5th combination? SPE?
Seems to me you just describe the 2 rails going to the connector and
then describe all the variations the connector supports. The PSE
(h/w) has little to do with which variations are supported, right?
For example, MDI-X vs. MDI support is determined by the PHY, right? Or
it has to be supported by both the PHY and PSE?
Rob
^ permalink raw reply
* Re: [PATCH v6 05/16] dt-bindings: net: wireless: describe the ath12k PCI module
From: Bartosz Golaszewski @ 2024-04-03 14:56 UTC (permalink / raw)
To: Kalle Valo
Cc: Marcel Holtmann, Luiz Augusto von Dentz, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
Liam Girdwood, Mark Brown, Catalin Marinas, Will Deacon,
Bjorn Helgaas, Saravana Kannan, Geert Uytterhoeven, Arnd Bergmann,
Neil Armstrong, Marek Szyprowski, Alex Elder, Srini Kandagatla,
Greg Kroah-Hartman, Abel Vesa, Manivannan Sadhasivam,
Lukas Wunner, Dmitry Baryshkov, linux-bluetooth, netdev,
devicetree, linux-kernel, linux-wireless, linux-arm-msm,
linux-arm-kernel, linux-pci, linux-pm, Bartosz Golaszewski
In-Reply-To: <87msqm8l6q.fsf@kernel.org>
On Mon, Mar 25, 2024 at 3:01 PM Kalle Valo <kvalo@kernel.org> wrote:
>
> Bartosz Golaszewski <brgl@bgdev.pl> writes:
>
> > From: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
> >
> > +
> > +maintainers:
> > + - Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
>
> IMHO it would be better to have just driver maintainers listed here.
>
Why? What's wrong with having the author of the bindings in the Cc list?
> > +required:
> > + - compatible
> > + - reg
> > + - vddaon-supply
> > + - vddwlcx-supply
> > + - vddwlmx-supply
> > + - vddrfacmn-supply
> > + - vddrfa0p8-supply
> > + - vddrfa1p2-supply
> > + - vddrfa1p8-supply
> > + - vddpcie0p9-supply
> > + - vddpcie1p8-supply
>
> Same comment here as in patch 4. There are also ath12k PCI devices which
> don't need DT at all. I don't know if that should be reflected in the
> bindings doc but I want to point out this.
>
But DT bindings don't apply to devices that don't have DT nodes. This
isn't an issue at all.
Bart
^ permalink raw reply
* Re: [PATCH v7 1/5] dt-bindings: interconnect: Add Qualcomm IPQ9574 support
From: Krzysztof Kozlowski @ 2024-04-03 14:59 UTC (permalink / raw)
To: Varadarajan Narayanan, andersson, konrad.dybcio, mturquette,
sboyd, robh, krzk+dt, conor+dt, djakov, dmitry.baryshkov,
quic_anusha, linux-arm-msm, linux-clk, devicetree, linux-kernel,
linux-pm
In-Reply-To: <20240403104220.1092431-2-quic_varada@quicinc.com>
On 03/04/2024 12:42, Varadarajan Narayanan wrote:
> Add interconnect-cells to clock provider so that it can be
> used as icc provider.
>
> Add master/slave ids for Qualcomm IPQ9574 Network-On-Chip
> interfaces. This will be used by the gcc-ipq9574 driver
> that will for providing interconnect services using the
> icc-clk framework.
>
> Signed-off-by: Varadarajan Narayanan <quic_varada@quicinc.com>
> ---
> v7:
> Fix macro names to be consistent with other bindings
> v6:
> Removed Reviewed-by: Krzysztof Kozlowski
> Redefine the bindings such that driver and DT can share them
>
> v3:
> Squash Documentation/ and include/ changes into same patch
>
> qcom,ipq9574.h
> Move 'first id' to clock driver
>
> ---
> .../bindings/clock/qcom,ipq9574-gcc.yaml | 3 +
> .../dt-bindings/interconnect/qcom,ipq9574.h | 87 +++++++++++++++++++
> 2 files changed, 90 insertions(+)
> create mode 100644 include/dt-bindings/interconnect/qcom,ipq9574.h
>
> diff --git a/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml b/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml
> index 944a0ea79cd6..824781cbdf34 100644
> --- a/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml
> +++ b/Documentation/devicetree/bindings/clock/qcom,ipq9574-gcc.yaml
> @@ -33,6 +33,9 @@ properties:
> - description: PCIE30 PHY3 pipe clock source
> - description: USB3 PHY pipe clock source
>
> + '#interconnect-cells':
> + const: 1
> +
> required:
> - compatible
> - clocks
> diff --git a/include/dt-bindings/interconnect/qcom,ipq9574.h b/include/dt-bindings/interconnect/qcom,ipq9574.h
> new file mode 100644
> index 000000000000..0b076b0cf880
> --- /dev/null
> +++ b/include/dt-bindings/interconnect/qcom,ipq9574.h
> @@ -0,0 +1,87 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +#ifndef INTERCONNECT_QCOM_IPQ9574_H
> +#define INTERCONNECT_QCOM_IPQ9574_H
> +
> +#define ICC_ANOC_PCIE0 0
> +#define ICC_SNOC_PCIE0 1
> +#define ICC_ANOC_PCIE1 2
> +#define ICC_SNOC_PCIE1 3
> +#define ICC_ANOC_PCIE2 4
> +#define ICC_SNOC_PCIE2 5
> +#define ICC_ANOC_PCIE3 6
> +#define ICC_SNOC_PCIE3 7
> +#define ICC_SNOC_USB 8
> +#define ICC_ANOC_USB_AXI 9
> +#define ICC_NSSNOC_NSSCC 10
> +#define ICC_NSSNOC_SNOC_0 11
> +#define ICC_NSSNOC_SNOC_1 12
> +#define ICC_NSSNOC_PCNOC_1 13
> +#define ICC_NSSNOC_QOSGEN_REF 14
> +#define ICC_NSSNOC_TIMEOUT_REF 15
> +#define ICC_NSSNOC_XO_DCD 16
> +#define ICC_NSSNOC_ATB 17
> +#define ICC_MEM_NOC_NSSNOC 18
> +#define ICC_NSSNOC_MEMNOC 19
> +#define ICC_NSSNOC_MEM_NOC_1 20
> +
> +#define ICC_NSSNOC_PPE 0
> +#define ICC_NSSNOC_PPE_CFG 1
> +#define ICC_NSSNOC_NSS_CSR 2
> +#define ICC_NSSNOC_IMEM_QSB 3
> +#define ICC_NSSNOC_IMEM_AHB 4
> +
> +#define MASTER_ANOC_PCIE0 (ICC_ANOC_PCIE0 * 2)
> +#define SLAVE_ANOC_PCIE0 ((ICC_ANOC_PCIE0 * 2) + 1)
Which existing Qualcomm platform has such code?
This is the third time I am asking for consistent headers. Open
existing, recently added headers and look how it is done there. Why?
Because I am against such calculations and see no reason for them.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v3 03/25] media: i2c: imx258: Disable digital cropping on binned modes
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
The binned modes set DIG_CROP_X_OFFSET and DIG_CROP_IMAGE_WIDTH
to less than the full image, even though the image being captured
is meant to be a scaled version of the full array size.
Reduce X_OFFSET to 0, and increase IMAGE_WIDTH to the full array.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 4a7048d834c6..0ae4371940ca 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -340,11 +340,11 @@ static const struct imx258_reg mode_2104_1560_regs[] = {
{ 0x0404, 0x00 },
{ 0x0405, 0x20 },
{ 0x0408, 0x00 },
- { 0x0409, 0x02 },
+ { 0x0409, 0x00 },
{ 0x040A, 0x00 },
{ 0x040B, 0x00 },
{ 0x040C, 0x10 },
- { 0x040D, 0x6A },
+ { 0x040D, 0x70 },
{ 0x040E, 0x06 },
{ 0x040F, 0x18 },
{ 0x3038, 0x00 },
@@ -459,11 +459,11 @@ static const struct imx258_reg mode_1048_780_regs[] = {
{ 0x0404, 0x00 },
{ 0x0405, 0x40 },
{ 0x0408, 0x00 },
- { 0x0409, 0x06 },
+ { 0x0409, 0x00 },
{ 0x040A, 0x00 },
{ 0x040B, 0x00 },
{ 0x040C, 0x10 },
- { 0x040D, 0x64 },
+ { 0x040D, 0x70 },
{ 0x040E, 0x03 },
{ 0x040F, 0x0C },
{ 0x3038, 0x00 },
--
2.42.0
^ permalink raw reply related
* [PATCH v3 04/25] media: i2c: imx258: Remove redundant I2C writes.
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Registers 0x0202 and 0x0203 are written via the control handler
for V4L2_CID_EXPOSURE, so are not needed from the mode lists.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 6 ------
1 file changed, 6 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 0ae4371940ca..df7ed4716762 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -237,8 +237,6 @@ static const struct imx258_reg mode_4208x3120_regs[] = {
{ 0x034E, 0x0C },
{ 0x034F, 0x30 },
{ 0x0350, 0x01 },
- { 0x0202, 0x0C },
- { 0x0203, 0x46 },
{ 0x0204, 0x00 },
{ 0x0205, 0x00 },
{ 0x020E, 0x01 },
@@ -356,8 +354,6 @@ static const struct imx258_reg mode_2104_1560_regs[] = {
{ 0x034E, 0x06 },
{ 0x034F, 0x18 },
{ 0x0350, 0x01 },
- { 0x0202, 0x06 },
- { 0x0203, 0x2E },
{ 0x0204, 0x00 },
{ 0x0205, 0x00 },
{ 0x020E, 0x01 },
@@ -475,8 +471,6 @@ static const struct imx258_reg mode_1048_780_regs[] = {
{ 0x034E, 0x03 },
{ 0x034F, 0x0C },
{ 0x0350, 0x01 },
- { 0x0202, 0x03 },
- { 0x0203, 0x42 },
{ 0x0204, 0x00 },
{ 0x0205, 0x00 },
{ 0x020E, 0x01 },
--
2.42.0
^ permalink raw reply related
* [PATCH v3 05/25] media: i2c: imx258: Add regulator control
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
The device tree bindings define the relevant regulators for the
sensor, so update the driver to request the regulators and control
them at the appropriate times.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 42 +++++++++++++++++++++++++++++++++++++-
1 file changed, 41 insertions(+), 1 deletion(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index df7ed4716762..495eaada2945 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -7,6 +7,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fwnode.h>
@@ -507,6 +508,16 @@ static const char * const imx258_test_pattern_menu[] = {
"Pseudorandom Sequence (PN9)",
};
+/* regulator supplies */
+static const char * const imx258_supply_name[] = {
+ /* Supplies can be enabled in any order */
+ "vana", /* Analog (2.8V) supply */
+ "vdig", /* Digital Core (1.2V) supply */
+ "vif", /* IF (1.8V) supply */
+};
+
+#define IMX258_NUM_SUPPLIES ARRAY_SIZE(imx258_supply_name)
+
/* Configurations for supported link frequencies */
#define IMX258_LINK_FREQ_634MHZ 633600000ULL
#define IMX258_LINK_FREQ_320MHZ 320000000ULL
@@ -611,6 +622,7 @@ struct imx258 {
struct mutex mutex;
struct clk *clk;
+ struct regulator_bulk_data supplies[IMX258_NUM_SUPPLIES];
};
static inline struct imx258 *to_imx258(struct v4l2_subdev *_sd)
@@ -995,9 +1007,19 @@ static int imx258_power_on(struct device *dev)
struct imx258 *imx258 = to_imx258(sd);
int ret;
+ ret = regulator_bulk_enable(IMX258_NUM_SUPPLIES,
+ imx258->supplies);
+ if (ret) {
+ dev_err(dev, "%s: failed to enable regulators\n",
+ __func__);
+ return ret;
+ }
+
ret = clk_prepare_enable(imx258->clk);
- if (ret)
+ if (ret) {
dev_err(dev, "failed to enable clock\n");
+ regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies);
+ }
return ret;
}
@@ -1008,6 +1030,7 @@ static int imx258_power_off(struct device *dev)
struct imx258 *imx258 = to_imx258(sd);
clk_disable_unprepare(imx258->clk);
+ regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies);
return 0;
}
@@ -1220,6 +1243,18 @@ static void imx258_free_controls(struct imx258 *imx258)
mutex_destroy(&imx258->mutex);
}
+static int imx258_get_regulators(struct imx258 *imx258,
+ struct i2c_client *client)
+{
+ unsigned int i;
+
+ for (i = 0; i < IMX258_NUM_SUPPLIES; i++)
+ imx258->supplies[i].supply = imx258_supply_name[i];
+
+ return devm_regulator_bulk_get(&client->dev,
+ IMX258_NUM_SUPPLIES, imx258->supplies);
+}
+
static int imx258_probe(struct i2c_client *client)
{
struct imx258 *imx258;
@@ -1230,6 +1265,11 @@ static int imx258_probe(struct i2c_client *client)
if (!imx258)
return -ENOMEM;
+ ret = imx258_get_regulators(imx258, client);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to get regulators\n");
+
imx258->clk = devm_clk_get_optional(&client->dev, NULL);
if (IS_ERR(imx258->clk))
return dev_err_probe(&client->dev, PTR_ERR(imx258->clk),
--
2.42.0
^ permalink raw reply related
* [PATCH v3 09/25] media: i2c: imx258: Add support for running on 2 CSI data lanes
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Extends the driver to also support 2 data lanes.
Frame rates are obviously more restricted on 2 lanes, but some
hardware simply hasn't wired more up.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 214 ++++++++++++++++++++++++++++++++-----
1 file changed, 190 insertions(+), 24 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index e4b1b3cbbde5..8f792f0e0738 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -86,12 +86,18 @@ struct imx258_reg_list {
const struct imx258_reg *regs;
};
+enum {
+ IMX258_2_LANE_MODE,
+ IMX258_4_LANE_MODE,
+ IMX258_LANE_CONFIGS,
+};
+
/* Link frequency config */
struct imx258_link_freq_config {
u32 pixels_per_line;
/* PLL registers for this link frequency */
- struct imx258_reg_list reg_list;
+ struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS];
};
/* Mode : resolution and related config&values */
@@ -111,8 +117,34 @@ struct imx258_mode {
struct imx258_reg_list reg_list;
};
-/* 4208x3120 needs 1267Mbps/lane, 4 lanes */
-static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
+/*
+ * 4208x3120 @ 30 fps needs 1267Mbps/lane, 4 lanes.
+ * To avoid further computation of clock settings, adopt the same per
+ * lane data rate when using 2 lanes, thus allowing a maximum of 15fps.
+ */
+static const struct imx258_reg mipi_1267mbps_19_2mhz_2l[] = {
+ { 0x0136, 0x13 },
+ { 0x0137, 0x33 },
+ { 0x0301, 0x0A },
+ { 0x0303, 0x02 },
+ { 0x0305, 0x03 },
+ { 0x0306, 0x00 },
+ { 0x0307, 0xC6 },
+ { 0x0309, 0x0A },
+ { 0x030B, 0x01 },
+ { 0x030D, 0x02 },
+ { 0x030E, 0x00 },
+ { 0x030F, 0xD8 },
+ { 0x0310, 0x00 },
+
+ { 0x0114, 0x01 },
+ { 0x0820, 0x09 },
+ { 0x0821, 0xa6 },
+ { 0x0822, 0x66 },
+ { 0x0823, 0x66 },
+};
+
+static const struct imx258_reg mipi_1267mbps_19_2mhz_4l[] = {
{ 0x0136, 0x13 },
{ 0x0137, 0x33 },
{ 0x0301, 0x05 },
@@ -126,16 +158,18 @@ static const struct imx258_reg mipi_1267mbps_19_2mhz[] = {
{ 0x030E, 0x00 },
{ 0x030F, 0xD8 },
{ 0x0310, 0x00 },
+
+ { 0x0114, 0x03 },
{ 0x0820, 0x13 },
{ 0x0821, 0x4C },
{ 0x0822, 0xCC },
{ 0x0823, 0xCC },
};
-static const struct imx258_reg mipi_1272mbps_24mhz[] = {
+static const struct imx258_reg mipi_1272mbps_24mhz_2l[] = {
{ 0x0136, 0x18 },
{ 0x0137, 0x00 },
- { 0x0301, 0x05 },
+ { 0x0301, 0x0a },
{ 0x0303, 0x02 },
{ 0x0305, 0x04 },
{ 0x0306, 0x00 },
@@ -146,13 +180,59 @@ static const struct imx258_reg mipi_1272mbps_24mhz[] = {
{ 0x030E, 0x00 },
{ 0x030F, 0xD8 },
{ 0x0310, 0x00 },
+
+ { 0x0114, 0x01 },
{ 0x0820, 0x13 },
{ 0x0821, 0x4C },
{ 0x0822, 0xCC },
{ 0x0823, 0xCC },
};
-static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
+static const struct imx258_reg mipi_1272mbps_24mhz_4l[] = {
+ { 0x0136, 0x18 },
+ { 0x0137, 0x00 },
+ { 0x0301, 0x05 },
+ { 0x0303, 0x02 },
+ { 0x0305, 0x04 },
+ { 0x0306, 0x00 },
+ { 0x0307, 0xD4 },
+ { 0x0309, 0x0A },
+ { 0x030B, 0x01 },
+ { 0x030D, 0x02 },
+ { 0x030E, 0x00 },
+ { 0x030F, 0xD8 },
+ { 0x0310, 0x00 },
+
+ { 0x0114, 0x03 },
+ { 0x0820, 0x13 },
+ { 0x0821, 0xE0 },
+ { 0x0822, 0x00 },
+ { 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mipi_640mbps_19_2mhz_2l[] = {
+ { 0x0136, 0x13 },
+ { 0x0137, 0x33 },
+ { 0x0301, 0x05 },
+ { 0x0303, 0x02 },
+ { 0x0305, 0x03 },
+ { 0x0306, 0x00 },
+ { 0x0307, 0x64 },
+ { 0x0309, 0x0A },
+ { 0x030B, 0x01 },
+ { 0x030D, 0x02 },
+ { 0x030E, 0x00 },
+ { 0x030F, 0xD8 },
+ { 0x0310, 0x00 },
+
+ { 0x0114, 0x01 },
+ { 0x0820, 0x05 },
+ { 0x0821, 0x00 },
+ { 0x0822, 0x00 },
+ { 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = {
{ 0x0136, 0x13 },
{ 0x0137, 0x33 },
{ 0x0301, 0x05 },
@@ -166,13 +246,37 @@ static const struct imx258_reg mipi_640mbps_19_2mhz[] = {
{ 0x030E, 0x00 },
{ 0x030F, 0xD8 },
{ 0x0310, 0x00 },
+
+ { 0x0114, 0x03 },
+ { 0x0820, 0x0A },
+ { 0x0821, 0x00 },
+ { 0x0822, 0x00 },
+ { 0x0823, 0x00 },
+};
+
+static const struct imx258_reg mipi_642mbps_24mhz_2l[] = {
+ { 0x0136, 0x18 },
+ { 0x0137, 0x00 },
+ { 0x0301, 0x0A },
+ { 0x0303, 0x02 },
+ { 0x0305, 0x04 },
+ { 0x0306, 0x00 },
+ { 0x0307, 0x6B },
+ { 0x0309, 0x0A },
+ { 0x030B, 0x01 },
+ { 0x030D, 0x02 },
+ { 0x030E, 0x00 },
+ { 0x030F, 0xD8 },
+ { 0x0310, 0x00 },
+
+ { 0x0114, 0x01 },
{ 0x0820, 0x0A },
{ 0x0821, 0x00 },
{ 0x0822, 0x00 },
{ 0x0823, 0x00 },
};
-static const struct imx258_reg mipi_642mbps_24mhz[] = {
+static const struct imx258_reg mipi_642mbps_24mhz_4l[] = {
{ 0x0136, 0x18 },
{ 0x0137, 0x00 },
{ 0x0301, 0x05 },
@@ -186,6 +290,8 @@ static const struct imx258_reg mipi_642mbps_24mhz[] = {
{ 0x030E, 0x00 },
{ 0x030F, 0xD8 },
{ 0x0310, 0x00 },
+
+ { 0x0114, 0x03 },
{ 0x0820, 0x0A },
{ 0x0821, 0x00 },
{ 0x0822, 0x00 },
@@ -240,7 +346,6 @@ static const struct imx258_reg mode_common_regs[] = {
{ 0x5F05, 0xED },
{ 0x0112, 0x0A },
{ 0x0113, 0x0A },
- { 0x0114, 0x03 },
{ 0x0342, 0x14 },
{ 0x0343, 0xE8 },
{ 0x0344, 0x00 },
@@ -359,11 +464,13 @@ enum {
/*
* pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
- * data rate => double data rate; number of lanes => 4; bits per pixel => 10
+ * data rate => double data rate;
+ * number of lanes => (configurable 2 or 4);
+ * bits per pixel => 10
*/
-static u64 link_freq_to_pixel_rate(u64 f)
+static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes)
{
- f *= 2 * 4;
+ f *= 2 * nlanes;
do_div(f, 10);
return f;
@@ -386,15 +493,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
[IMX258_LINK_FREQ_1267MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
.reg_list = {
- .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz),
- .regs = mipi_1267mbps_19_2mhz,
+ [IMX258_2_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l),
+ .regs = mipi_1267mbps_19_2mhz_2l,
+ },
+ [IMX258_4_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l),
+ .regs = mipi_1267mbps_19_2mhz_4l,
+ },
}
},
[IMX258_LINK_FREQ_640MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
.reg_list = {
- .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz),
- .regs = mipi_640mbps_19_2mhz,
+ [IMX258_2_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l),
+ .regs = mipi_640mbps_19_2mhz_2l,
+ },
+ [IMX258_4_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l),
+ .regs = mipi_640mbps_19_2mhz_4l,
+ },
}
},
};
@@ -403,15 +522,27 @@ static const struct imx258_link_freq_config link_freq_configs_24[] = {
[IMX258_LINK_FREQ_1267MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
.reg_list = {
- .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz),
- .regs = mipi_1272mbps_24mhz,
+ [IMX258_2_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l),
+ .regs = mipi_1272mbps_24mhz_2l,
+ },
+ [IMX258_4_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l),
+ .regs = mipi_1272mbps_24mhz_4l,
+ },
}
},
[IMX258_LINK_FREQ_640MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
.reg_list = {
- .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz),
- .regs = mipi_642mbps_24mhz,
+ [IMX258_2_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l),
+ .regs = mipi_642mbps_24mhz_2l,
+ },
+ [IMX258_4_LANE_MODE] = {
+ .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l),
+ .regs = mipi_642mbps_24mhz_4l,
+ },
}
},
};
@@ -470,6 +601,7 @@ struct imx258 {
const struct imx258_link_freq_config *link_freq_configs;
const s64 *link_freq_menu_items;
+ unsigned int nlanes;
/*
* Mutex for serialized access:
@@ -775,7 +907,7 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
__v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
- pixel_rate = link_freq_to_pixel_rate(link_freq);
+ pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
__v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
/* Update limits and set FPS to default */
vblank_def = imx258->cur_mode->vts_def -
@@ -804,11 +936,13 @@ static int imx258_start_streaming(struct imx258 *imx258)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
const struct imx258_reg_list *reg_list;
+ const struct imx258_link_freq_config *link_freq_cfg;
int ret, link_freq_index;
/* Setup PLL */
link_freq_index = imx258->cur_mode->link_freq_index;
- reg_list = &imx258->link_freq_configs[link_freq_index].reg_list;
+ link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
+ reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1];
ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
if (ret) {
dev_err(&client->dev, "%s failed to set plls\n", __func__);
@@ -1026,9 +1160,11 @@ static int imx258_init_controls(struct imx258 *imx258)
vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
pixel_rate_max =
- link_freq_to_pixel_rate(imx258->link_freq_menu_items[0]);
+ link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
+ imx258->nlanes);
pixel_rate_min =
- link_freq_to_pixel_rate(imx258->link_freq_menu_items[1]);
+ link_freq_to_pixel_rate(imx258->link_freq_menu_items[1],
+ imx258->nlanes);
/* By default, PIXEL_RATE is read only */
imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
V4L2_CID_PIXEL_RATE,
@@ -1125,6 +1261,10 @@ static int imx258_get_regulators(struct imx258 *imx258,
static int imx258_probe(struct i2c_client *client)
{
struct imx258 *imx258;
+ struct fwnode_handle *endpoint;
+ struct v4l2_fwnode_endpoint ep = {
+ .bus_type = V4L2_MBUS_CSI2_DPHY
+ };
int ret;
u32 val = 0;
@@ -1165,13 +1305,35 @@ static int imx258_probe(struct i2c_client *client)
return -EINVAL;
}
+ endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+ if (!endpoint) {
+ dev_err(&client->dev, "Endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &ep);
+ fwnode_handle_put(endpoint);
+ if (ret) {
+ dev_err(&client->dev, "Parsing endpoint node failed\n");
+ return ret;
+ }
+
+ /* Get number of data lanes */
+ imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
+ if (imx258->nlanes != 2 && imx258->nlanes != 4) {
+ dev_err(&client->dev, "Invalid data lanes: %u\n",
+ imx258->nlanes);
+ ret = -EINVAL;
+ goto error_endpoint_free;
+ }
+
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
/* Will be powered off via pm_runtime_idle */
ret = imx258_power_on(&client->dev);
if (ret)
- return ret;
+ goto error_endpoint_free;
/* Check module identity */
ret = imx258_identify_module(imx258);
@@ -1204,6 +1366,7 @@ static int imx258_probe(struct i2c_client *client)
pm_runtime_set_active(&client->dev);
pm_runtime_enable(&client->dev);
pm_runtime_idle(&client->dev);
+ v4l2_fwnode_endpoint_free(&ep);
return 0;
@@ -1216,6 +1379,9 @@ static int imx258_probe(struct i2c_client *client)
error_identify:
imx258_power_off(&client->dev);
+error_endpoint_free:
+ v4l2_fwnode_endpoint_free(&ep);
+
return ret;
}
--
2.42.0
^ permalink raw reply related
* [PATCH v3 10/25] media: i2c: imx258: Follow normal V4L2 behaviours for clipping exposure
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
V4L2 sensor drivers are expected are expected to clip the supported
exposure range based on the VBLANK configured.
IMX258 wasn't doing that as register 0x350 (FRM_LENGTH_CTL)
switches it to a mode where frame length tracks coarse exposure time.
Disable this mode and clip the range for V4L2_CID_EXPOSURE appropriately
based on V4L2_CID_VBLANK.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 25 +++++++++++++++++++++++--
1 file changed, 23 insertions(+), 2 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 8f792f0e0738..ebc404b548b3 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -37,10 +37,11 @@
/* Exposure control */
#define IMX258_REG_EXPOSURE 0x0202
+#define IMX258_EXPOSURE_OFFSET 10
#define IMX258_EXPOSURE_MIN 4
#define IMX258_EXPOSURE_STEP 1
#define IMX258_EXPOSURE_DEFAULT 0x640
-#define IMX258_EXPOSURE_MAX 65535
+#define IMX258_EXPOSURE_MAX (IMX258_VTS_MAX - IMX258_EXPOSURE_OFFSET)
/* Analog gain control */
#define IMX258_REG_ANALOG_GAIN 0x0204
@@ -371,7 +372,7 @@ static const struct imx258_reg mode_common_regs[] = {
{ 0x303A, 0x00 },
{ 0x303B, 0x10 },
{ 0x300D, 0x00 },
- { 0x0350, 0x01 },
+ { 0x0350, 0x00 },
{ 0x0204, 0x00 },
{ 0x0205, 0x00 },
{ 0x020E, 0x01 },
@@ -734,6 +735,19 @@ static int imx258_update_digital_gain(struct imx258 *imx258, u32 len, u32 val)
return 0;
}
+static void imx258_adjust_exposure_range(struct imx258 *imx258)
+{
+ int exposure_max, exposure_def;
+
+ /* Honour the VBLANK limits when setting exposure. */
+ exposure_max = imx258->cur_mode->height + imx258->vblank->val -
+ IMX258_EXPOSURE_OFFSET;
+ exposure_def = min(exposure_max, imx258->exposure->val);
+ __v4l2_ctrl_modify_range(imx258->exposure, imx258->exposure->minimum,
+ exposure_max, imx258->exposure->step,
+ exposure_def);
+}
+
static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
{
struct imx258 *imx258 =
@@ -741,6 +755,13 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
int ret = 0;
+ /*
+ * The VBLANK control may change the limits of usable exposure, so check
+ * and adjust if necessary.
+ */
+ if (ctrl->id == V4L2_CID_VBLANK)
+ imx258_adjust_exposure_range(imx258);
+
/*
* Applying V4L2 control value only happens
* when power is up for streaming
--
2.42.0
^ permalink raw reply related
* [PATCH v3 12/25] media: i2c: imx258: Allow configuration of clock lane behaviour
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
The sensor supports the clock lane either remaining in HS mode
during frame blanking, or dropping to LP11.
Add configuration of the mode via V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 805308b4a4c6..692a5aeb7941 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -72,6 +72,8 @@
/* Test Pattern Control */
#define IMX258_REG_TEST_PATTERN 0x0600
+#define IMX258_CLK_BLANK_STOP 0x4040
+
/* Orientation */
#define REG_MIRROR_FLIP_CONTROL 0x0101
#define REG_CONFIG_MIRROR_FLIP 0x03
@@ -632,6 +634,7 @@ struct imx258 {
const struct imx258_link_freq_config *link_freq_configs;
const s64 *link_freq_menu_items;
unsigned int nlanes;
+ unsigned int csi2_flags;
/*
* Mutex for serialized access:
@@ -1066,6 +1069,15 @@ static int imx258_start_streaming(struct imx258 *imx258)
return ret;
}
+ ret = imx258_write_reg(imx258, IMX258_CLK_BLANK_STOP,
+ IMX258_REG_VALUE_08BIT,
+ imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ?
+ 1 : 0);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set clock lane mode\n", __func__);
+ return ret;
+ }
+
/* Apply default values of current mode */
reg_list = &imx258->cur_mode->reg_list;
ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
@@ -1438,6 +1450,8 @@ static int imx258_probe(struct i2c_client *client)
goto error_endpoint_free;
}
+ imx258->csi2_flags = ep.bus.mipi_csi2.flags;
+
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
--
2.42.0
^ permalink raw reply related
* [PATCH v3 13/25] media: i2c: imx258: Correct max FRM_LENGTH_LINES value
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
The data sheet states that the maximum value for registers
0x0340/0x0341 FRM_LENGTH_LINES is 65525(decimal), not the
0xFFFF defined in this driver. Correct this limit.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 692a5aeb7941..5a5bf2b25bdf 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -28,7 +28,7 @@
#define IMX258_VTS_30FPS 0x0c50
#define IMX258_VTS_30FPS_2K 0x0638
#define IMX258_VTS_30FPS_VGA 0x034c
-#define IMX258_VTS_MAX 0xffff
+#define IMX258_VTS_MAX 65525
#define IMX258_REG_VTS 0x0340
--
2.42.0
^ permalink raw reply related
* [PATCH v3 14/25] media: i2c: imx258: Issue reset before starting streaming
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Whilst not documented, register 0x0103 bit 0 is the soft
reset for the sensor, so send it before trying to configure
the sensor.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 5a5bf2b25bdf..c242e318be11 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -20,6 +20,8 @@
#define IMX258_MODE_STANDBY 0x00
#define IMX258_MODE_STREAMING 0x01
+#define IMX258_REG_RESET 0x0103
+
/* Chip ID */
#define IMX258_REG_CHIP_ID 0x0016
#define IMX258_CHIP_ID 0x0258
@@ -1052,6 +1054,16 @@ static int imx258_start_streaming(struct imx258 *imx258)
const struct imx258_link_freq_config *link_freq_cfg;
int ret, link_freq_index;
+ ret = imx258_write_reg(imx258, IMX258_REG_RESET, IMX258_REG_VALUE_08BIT,
+ 0x01);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to reset sensor\n", __func__);
+ return ret;
+ }
+
+ /* 12ms is required from poweron to standby */
+ fsleep(12000);
+
/* Setup PLL */
link_freq_index = imx258->cur_mode->link_freq_index;
link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
--
2.42.0
^ permalink raw reply related
* [PATCH v3 11/25] media: i2c: imx258: Add get_selection for pixel array information
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Libcamera requires the cropping information for each mode, so
add this information to the driver.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 90 ++++++++++++++++++++++++++++++++++++++
1 file changed, 90 insertions(+)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index ebc404b548b3..805308b4a4c6 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -77,6 +77,14 @@
#define REG_CONFIG_MIRROR_FLIP 0x03
#define REG_CONFIG_FLIP_TEST_PATTERN 0x02
+/* IMX258 native and active pixel array size. */
+#define IMX258_NATIVE_WIDTH 4224U
+#define IMX258_NATIVE_HEIGHT 3192U
+#define IMX258_PIXEL_ARRAY_LEFT 8U
+#define IMX258_PIXEL_ARRAY_TOP 16U
+#define IMX258_PIXEL_ARRAY_WIDTH 4208U
+#define IMX258_PIXEL_ARRAY_HEIGHT 3120U
+
struct imx258_reg {
u16 address;
u8 val;
@@ -116,6 +124,9 @@ struct imx258_mode {
u32 link_freq_index;
/* Default register values */
struct imx258_reg_list reg_list;
+
+ /* Analog crop rectangle. */
+ struct v4l2_rect crop;
};
/*
@@ -560,6 +571,12 @@ static const struct imx258_mode supported_modes[] = {
.regs = mode_4208x3120_regs,
},
.link_freq_index = IMX258_LINK_FREQ_1267MBPS,
+ .crop = {
+ .left = IMX258_PIXEL_ARRAY_LEFT,
+ .top = IMX258_PIXEL_ARRAY_TOP,
+ .width = 4208,
+ .height = 3120,
+ },
},
{
.width = 2104,
@@ -571,6 +588,12 @@ static const struct imx258_mode supported_modes[] = {
.regs = mode_2104_1560_regs,
},
.link_freq_index = IMX258_LINK_FREQ_640MBPS,
+ .crop = {
+ .left = IMX258_PIXEL_ARRAY_LEFT,
+ .top = IMX258_PIXEL_ARRAY_TOP,
+ .width = 4208,
+ .height = 3120,
+ },
},
{
.width = 1048,
@@ -582,6 +605,12 @@ static const struct imx258_mode supported_modes[] = {
.regs = mode_1048_780_regs,
},
.link_freq_index = IMX258_LINK_FREQ_640MBPS,
+ .crop = {
+ .left = IMX258_PIXEL_ARRAY_LEFT,
+ .top = IMX258_PIXEL_ARRAY_TOP,
+ .width = 4208,
+ .height = 3120,
+ },
},
};
@@ -698,6 +727,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
struct v4l2_mbus_framefmt *try_fmt =
v4l2_subdev_state_get_format(fh->state, 0);
+ struct v4l2_rect *try_crop;
/* Initialize try_fmt */
try_fmt->width = supported_modes[0].width;
@@ -705,6 +735,13 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
try_fmt->field = V4L2_FIELD_NONE;
+ /* Initialize try_crop */
+ try_crop = v4l2_subdev_state_get_crop(fh->state, 0);
+ try_crop->left = IMX258_PIXEL_ARRAY_LEFT;
+ try_crop->top = IMX258_PIXEL_ARRAY_TOP;
+ try_crop->width = IMX258_PIXEL_ARRAY_WIDTH;
+ try_crop->height = IMX258_PIXEL_ARRAY_HEIGHT;
+
return 0;
}
@@ -952,6 +989,58 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
return 0;
}
+static const struct v4l2_rect *
+__imx258_get_pad_crop(struct imx258 *imx258,
+ struct v4l2_subdev_state *sd_state,
+ unsigned int pad, enum v4l2_subdev_format_whence which)
+{
+ switch (which) {
+ case V4L2_SUBDEV_FORMAT_TRY:
+ return v4l2_subdev_state_get_crop(sd_state, pad);
+ case V4L2_SUBDEV_FORMAT_ACTIVE:
+ return &imx258->cur_mode->crop;
+ }
+
+ return NULL;
+}
+
+static int imx258_get_selection(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_selection *sel)
+{
+ switch (sel->target) {
+ case V4L2_SEL_TGT_CROP: {
+ struct imx258 *imx258 = to_imx258(sd);
+
+ mutex_lock(&imx258->mutex);
+ sel->r = *__imx258_get_pad_crop(imx258, sd_state, sel->pad,
+ sel->which);
+ mutex_unlock(&imx258->mutex);
+
+ return 0;
+ }
+
+ case V4L2_SEL_TGT_NATIVE_SIZE:
+ sel->r.left = 0;
+ sel->r.top = 0;
+ sel->r.width = IMX258_NATIVE_WIDTH;
+ sel->r.height = IMX258_NATIVE_HEIGHT;
+
+ return 0;
+
+ case V4L2_SEL_TGT_CROP_DEFAULT:
+ case V4L2_SEL_TGT_CROP_BOUNDS:
+ sel->r.left = IMX258_PIXEL_ARRAY_LEFT;
+ sel->r.top = IMX258_PIXEL_ARRAY_TOP;
+ sel->r.width = IMX258_PIXEL_ARRAY_WIDTH;
+ sel->r.height = IMX258_PIXEL_ARRAY_HEIGHT;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
/* Start streaming */
static int imx258_start_streaming(struct imx258 *imx258)
{
@@ -1128,6 +1217,7 @@ static const struct v4l2_subdev_pad_ops imx258_pad_ops = {
.get_fmt = imx258_get_pad_format,
.set_fmt = imx258_set_pad_format,
.enum_frame_size = imx258_enum_frame_size,
+ .get_selection = imx258_get_selection,
};
static const struct v4l2_subdev_ops imx258_subdev_ops = {
--
2.42.0
^ permalink raw reply related
* [PATCH v3 15/25] media: i2c: imx258: Set pixel_rate range to the same as the value
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
With a read only control there is limited point in advertising
a minimum and maximum for the control, so change to set the
value, min, and max all to the selected pixel rate.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index c242e318be11..7d434bc12c6a 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -971,7 +971,8 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
- __v4l2_ctrl_s_ctrl_int64(imx258->pixel_rate, pixel_rate);
+ __v4l2_ctrl_modify_range(imx258->pixel_rate, pixel_rate,
+ pixel_rate, 1, pixel_rate);
/* Update limits and set FPS to default */
vblank_def = imx258->cur_mode->vts_def -
imx258->cur_mode->height;
@@ -1262,8 +1263,7 @@ static int imx258_init_controls(struct imx258 *imx258)
struct v4l2_ctrl *vflip, *hflip;
s64 vblank_def;
s64 vblank_min;
- s64 pixel_rate_min;
- s64 pixel_rate_max;
+ s64 pixel_rate;
int ret;
ctrl_hdlr = &imx258->ctrl_handler;
@@ -1294,18 +1294,13 @@ static int imx258_init_controls(struct imx258 *imx258)
if (vflip)
vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- pixel_rate_max =
- link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
- imx258->nlanes);
- pixel_rate_min =
- link_freq_to_pixel_rate(imx258->link_freq_menu_items[1],
- imx258->nlanes);
+ pixel_rate = link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
+ imx258->nlanes);
/* By default, PIXEL_RATE is read only */
imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
V4L2_CID_PIXEL_RATE,
- pixel_rate_min, pixel_rate_max,
- 1, pixel_rate_max);
-
+ pixel_rate, pixel_rate,
+ 1, pixel_rate);
vblank_def = imx258->cur_mode->vts_def - imx258->cur_mode->height;
vblank_min = imx258->cur_mode->vts_min - imx258->cur_mode->height;
--
2.42.0
^ permalink raw reply related
* [PATCH v3 18/25] dt-bindings: media: imx258: Add alternate compatible strings
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
There are a number of variants of the imx258 modules that can not
be differentiated at runtime, so add compatible strings for the
PDAF variant.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
.../devicetree/bindings/media/i2c/sony,imx258.yaml | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
index bee61a443b23..c978abc0cdb3 100644
--- a/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
@@ -13,11 +13,16 @@ description: |-
IMX258 is a diagonal 5.867mm (Type 1/3.06) 13 Mega-pixel CMOS active pixel
type stacked image sensor with a square pixel array of size 4208 x 3120. It
is programmable through I2C interface. Image data is sent through MIPI
- CSI-2.
+ CSI-2. The sensor exists in two different models, a standard variant
+ (IMX258) and a variant with phase detection autofocus (IMX258-PDAF).
+ The camera module does not expose the model through registers, so the
+ exact model needs to be specified.
properties:
compatible:
- const: sony,imx258
+ enum:
+ - sony,imx258
+ - sony,imx258-pdaf
assigned-clocks: true
assigned-clock-parents: true
--
2.42.0
^ permalink raw reply related
* [PATCH v3 19/25] media: i2c: imx258: Change register settings for variants of the sensor
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
Sony have advised that there are variants of the IMX258 sensor which
require slightly different register configuration to the mainline
imx258 driver defaults.
There is no available run-time detection for the variant, so add
configuration via the DT compatible string.
The Vision Components imx258 module supports PDAF, so add the
register differences for that variant
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 48 ++++++++++++++++++++++++++++++++++----
1 file changed, 44 insertions(+), 4 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 775d957c9b87..fa48da212037 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -6,6 +6,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/v4l2-ctrls.h>
@@ -321,8 +322,6 @@ static const struct imx258_reg mipi_642mbps_24mhz_4l[] = {
static const struct imx258_reg mode_common_regs[] = {
{ 0x3051, 0x00 },
- { 0x3052, 0x00 },
- { 0x4E21, 0x14 },
{ 0x6B11, 0xCF },
{ 0x7FF0, 0x08 },
{ 0x7FF1, 0x0F },
@@ -345,7 +344,6 @@ static const struct imx258_reg mode_common_regs[] = {
{ 0x7FA8, 0x03 },
{ 0x7FA9, 0xFE },
{ 0x7B24, 0x81 },
- { 0x7B25, 0x00 },
{ 0x6564, 0x07 },
{ 0x6B0D, 0x41 },
{ 0x653D, 0x04 },
@@ -460,6 +458,33 @@ static const struct imx258_reg mode_1048_780_regs[] = {
{ 0x034F, 0x0C },
};
+struct imx258_variant_cfg {
+ const struct imx258_reg *regs;
+ unsigned int num_regs;
+};
+
+static const struct imx258_reg imx258_cfg_regs[] = {
+ { 0x3052, 0x00 },
+ { 0x4E21, 0x14 },
+ { 0x7B25, 0x00 },
+};
+
+static const struct imx258_variant_cfg imx258_cfg = {
+ .regs = imx258_cfg_regs,
+ .num_regs = ARRAY_SIZE(imx258_cfg_regs),
+};
+
+static const struct imx258_reg imx258_pdaf_cfg_regs[] = {
+ { 0x3052, 0x01 },
+ { 0x4E21, 0x10 },
+ { 0x7B25, 0x01 },
+};
+
+static const struct imx258_variant_cfg imx258_pdaf_cfg = {
+ .regs = imx258_pdaf_cfg_regs,
+ .num_regs = ARRAY_SIZE(imx258_pdaf_cfg_regs),
+};
+
static const char * const imx258_test_pattern_menu[] = {
"Disabled",
"Solid Colour",
@@ -637,6 +662,8 @@ struct imx258 {
struct v4l2_subdev sd;
struct media_pad pad;
+ const struct imx258_variant_cfg *variant_cfg;
+
struct v4l2_ctrl_handler ctrl_handler;
/* V4L2 Controls */
struct v4l2_ctrl *link_freq;
@@ -1104,6 +1131,14 @@ static int imx258_start_streaming(struct imx258 *imx258)
return ret;
}
+ ret = imx258_write_regs(imx258, imx258->variant_cfg->regs,
+ imx258->variant_cfg->num_regs);
+ if (ret) {
+ dev_err(&client->dev, "%s failed to set variant config\n",
+ __func__);
+ return ret;
+ }
+
ret = imx258_write_reg(imx258, IMX258_CLK_BLANK_STOP,
IMX258_REG_VALUE_08BIT,
imx258->csi2_flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK ?
@@ -1492,6 +1527,10 @@ static int imx258_probe(struct i2c_client *client)
imx258->csi2_flags = ep.bus.mipi_csi2.flags;
+ imx258->variant_cfg = of_device_get_match_data(&client->dev);
+ if (!imx258->variant_cfg)
+ imx258->variant_cfg = &imx258_cfg;
+
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
@@ -1579,7 +1618,8 @@ MODULE_DEVICE_TABLE(acpi, imx258_acpi_ids);
#endif
static const struct of_device_id imx258_dt_ids[] = {
- { .compatible = "sony,imx258" },
+ { .compatible = "sony,imx258", .data = &imx258_cfg },
+ { .compatible = "sony,imx258-pdaf", .data = &imx258_pdaf_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx258_dt_ids);
--
2.42.0
^ permalink raw reply related
* [PATCH v3 16/25] media: i2c: imx258: Support faster pixel rate on binned modes
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
With the binned modes, there is little point in faithfully
reproducing the horizontal line length of 5352 pixels on the CSI2
bus, and the FIFO between the pixel array and MIPI serialiser
allows us to remove that dependency.
Allow the pixel array to run with the normal settings, with the MIPI
serialiser at half the rate. This requires some additional
information for the link frequency to pixel rate function that
needs to be added to the configuration tables.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 109 ++++++++++++++++++++++++-------------
1 file changed, 71 insertions(+), 38 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 7d434bc12c6a..775d957c9b87 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -99,6 +99,11 @@ struct imx258_reg_list {
const struct imx258_reg *regs;
};
+struct imx258_link_cfg {
+ unsigned int lf_to_pix_rate_factor;
+ struct imx258_reg_list reg_list;
+};
+
enum {
IMX258_2_LANE_MODE,
IMX258_4_LANE_MODE,
@@ -109,8 +114,8 @@ enum {
struct imx258_link_freq_config {
u32 pixels_per_line;
- /* PLL registers for this link frequency */
- struct imx258_reg_list reg_list[IMX258_LANE_CONFIGS];
+ /* Configuration for this link frequency / num lanes selection */
+ struct imx258_link_cfg link_cfg[IMX258_LANE_CONFIGS];
};
/* Mode : resolution and related config&values */
@@ -273,7 +278,7 @@ static const struct imx258_reg mipi_640mbps_19_2mhz_4l[] = {
static const struct imx258_reg mipi_642mbps_24mhz_2l[] = {
{ 0x0136, 0x18 },
{ 0x0137, 0x00 },
- { 0x0301, 0x0A },
+ { 0x0301, 0x05 },
{ 0x0303, 0x02 },
{ 0x0305, 0x04 },
{ 0x0306, 0x00 },
@@ -479,14 +484,22 @@ enum {
};
/*
- * pixel_rate = link_freq * data-rate * nr_of_lanes / bits_per_sample
- * data rate => double data rate;
- * number of lanes => (configurable 2 or 4);
- * bits per pixel => 10
+ * Pixel rate does not necessarily relate to link frequency on this sensor as
+ * there is a FIFO between the pixel array pipeline and the MIPI serializer.
+ * The recommendation from Sony is that the pixel array is always run with a
+ * line length of 5352 pixels, which means that there is a large amount of
+ * blanking time for the 1048x780 mode. There is no need to replicate this
+ * blanking on the CSI2 bus, and the configuration of register 0x0301 allows the
+ * divider to be altered.
+ *
+ * The actual factor between link frequency and pixel rate is in the
+ * imx258_link_cfg, so use this to convert between the two.
+ * bits per pixel being 10, and D-PHY being DDR is assumed by this function, so
+ * the value is only the combination of number of lanes and pixel clock divider.
*/
-static u64 link_freq_to_pixel_rate(u64 f, unsigned int nlanes)
+static u64 link_freq_to_pixel_rate(u64 f, const struct imx258_link_cfg *link_cfg)
{
- f *= 2 * nlanes;
+ f *= 2 * link_cfg->lf_to_pix_rate_factor;
do_div(f, 10);
return f;
@@ -504,31 +517,33 @@ static const s64 link_freq_menu_items_24[] = {
321000000ULL,
};
+#define REGS(_list) { .num_of_regs = ARRAY_SIZE(_list), .regs = _list, }
+
/* Link frequency configs */
static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
[IMX258_LINK_FREQ_1267MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
- .reg_list = {
+ .link_cfg = {
[IMX258_2_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_2l),
- .regs = mipi_1267mbps_19_2mhz_2l,
+ .lf_to_pix_rate_factor = 2 * 2,
+ .reg_list = REGS(mipi_1267mbps_19_2mhz_2l),
},
[IMX258_4_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_1267mbps_19_2mhz_4l),
- .regs = mipi_1267mbps_19_2mhz_4l,
+ .lf_to_pix_rate_factor = 4,
+ .reg_list = REGS(mipi_1267mbps_19_2mhz_4l),
},
}
},
[IMX258_LINK_FREQ_640MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
- .reg_list = {
+ .link_cfg = {
[IMX258_2_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_2l),
- .regs = mipi_640mbps_19_2mhz_2l,
+ .lf_to_pix_rate_factor = 2,
+ .reg_list = REGS(mipi_640mbps_19_2mhz_2l),
},
[IMX258_4_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_640mbps_19_2mhz_4l),
- .regs = mipi_640mbps_19_2mhz_4l,
+ .lf_to_pix_rate_factor = 4,
+ .reg_list = REGS(mipi_640mbps_19_2mhz_4l),
},
}
},
@@ -537,27 +552,27 @@ static const struct imx258_link_freq_config link_freq_configs_19_2[] = {
static const struct imx258_link_freq_config link_freq_configs_24[] = {
[IMX258_LINK_FREQ_1267MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
- .reg_list = {
+ .link_cfg = {
[IMX258_2_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_2l),
- .regs = mipi_1272mbps_24mhz_2l,
+ .lf_to_pix_rate_factor = 2,
+ .reg_list = REGS(mipi_1272mbps_24mhz_2l),
},
[IMX258_4_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_1272mbps_24mhz_4l),
- .regs = mipi_1272mbps_24mhz_4l,
+ .lf_to_pix_rate_factor = 4,
+ .reg_list = REGS(mipi_1272mbps_24mhz_4l),
},
}
},
[IMX258_LINK_FREQ_640MBPS] = {
.pixels_per_line = IMX258_PPL_DEFAULT,
- .reg_list = {
+ .link_cfg = {
[IMX258_2_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_2l),
- .regs = mipi_642mbps_24mhz_2l,
+ .lf_to_pix_rate_factor = 2 * 2,
+ .reg_list = REGS(mipi_642mbps_24mhz_2l),
},
[IMX258_4_LANE_MODE] = {
- .num_of_regs = ARRAY_SIZE(mipi_642mbps_24mhz_4l),
- .regs = mipi_642mbps_24mhz_4l,
+ .lf_to_pix_rate_factor = 4,
+ .reg_list = REGS(mipi_642mbps_24mhz_4l),
},
}
},
@@ -635,7 +650,7 @@ struct imx258 {
const struct imx258_link_freq_config *link_freq_configs;
const s64 *link_freq_menu_items;
- unsigned int nlanes;
+ unsigned int lane_mode_idx;
unsigned int csi2_flags;
/*
@@ -945,8 +960,10 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
struct v4l2_subdev_format *fmt)
{
struct imx258 *imx258 = to_imx258(sd);
- const struct imx258_mode *mode;
+ const struct imx258_link_freq_config *link_freq_cfgs;
+ const struct imx258_link_cfg *link_cfg;
struct v4l2_mbus_framefmt *framefmt;
+ const struct imx258_mode *mode;
s32 vblank_def;
s32 vblank_min;
s64 h_blank;
@@ -970,7 +987,11 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
__v4l2_ctrl_s_ctrl(imx258->link_freq, mode->link_freq_index);
link_freq = imx258->link_freq_menu_items[mode->link_freq_index];
- pixel_rate = link_freq_to_pixel_rate(link_freq, imx258->nlanes);
+ link_freq_cfgs =
+ &imx258->link_freq_configs[mode->link_freq_index];
+
+ link_cfg = &link_freq_cfgs->link_cfg[imx258->lane_mode_idx];
+ pixel_rate = link_freq_to_pixel_rate(link_freq, link_cfg);
__v4l2_ctrl_modify_range(imx258->pixel_rate, pixel_rate,
pixel_rate, 1, pixel_rate);
/* Update limits and set FPS to default */
@@ -1068,7 +1089,8 @@ static int imx258_start_streaming(struct imx258 *imx258)
/* Setup PLL */
link_freq_index = imx258->cur_mode->link_freq_index;
link_freq_cfg = &imx258->link_freq_configs[link_freq_index];
- reg_list = &link_freq_cfg->reg_list[imx258->nlanes == 2 ? 0 : 1];
+
+ reg_list = &link_freq_cfg->link_cfg[imx258->lane_mode_idx].reg_list;
ret = imx258_write_regs(imx258, reg_list->regs, reg_list->num_of_regs);
if (ret) {
dev_err(&client->dev, "%s failed to set plls\n", __func__);
@@ -1258,9 +1280,11 @@ static const struct v4l2_subdev_internal_ops imx258_internal_ops = {
static int imx258_init_controls(struct imx258 *imx258)
{
struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
+ const struct imx258_link_freq_config *link_freq_cfgs;
struct v4l2_fwnode_device_properties props;
- struct v4l2_ctrl_handler *ctrl_hdlr;
struct v4l2_ctrl *vflip, *hflip;
+ struct v4l2_ctrl_handler *ctrl_hdlr;
+ const struct imx258_link_cfg *link_cfg;
s64 vblank_def;
s64 vblank_min;
s64 pixel_rate;
@@ -1294,8 +1318,11 @@ static int imx258_init_controls(struct imx258 *imx258)
if (vflip)
vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ link_freq_cfgs = &imx258->link_freq_configs[0];
+ link_cfg = link_freq_cfgs[imx258->lane_mode_idx].link_cfg;
pixel_rate = link_freq_to_pixel_rate(imx258->link_freq_menu_items[0],
- imx258->nlanes);
+ link_cfg);
+
/* By default, PIXEL_RATE is read only */
imx258->pixel_rate = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
V4L2_CID_PIXEL_RATE,
@@ -1449,10 +1476,16 @@ static int imx258_probe(struct i2c_client *client)
}
/* Get number of data lanes */
- imx258->nlanes = ep.bus.mipi_csi2.num_data_lanes;
- if (imx258->nlanes != 2 && imx258->nlanes != 4) {
+ switch (ep.bus.mipi_csi2.num_data_lanes) {
+ case 2:
+ imx258->lane_mode_idx = IMX258_2_LANE_MODE;
+ break;
+ case 4:
+ imx258->lane_mode_idx = IMX258_4_LANE_MODE;
+ break;
+ default:
dev_err(&client->dev, "Invalid data lanes: %u\n",
- imx258->nlanes);
+ ep.bus.mipi_csi2.num_data_lanes);
ret = -EINVAL;
goto error_endpoint_free;
}
--
2.42.0
^ permalink raw reply related
* [PATCH v3 20/25] media: i2c: imx258: Make HFLIP and VFLIP controls writable
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
The sensor supports H & V flips, but the controls were READ_ONLY.
Note that the Bayer order changes with these flips, therefore
they set the V4L2_CTRL_FLAG_MODIFY_LAYOUT property.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 100 ++++++++++++++++++++++++-------------
1 file changed, 65 insertions(+), 35 deletions(-)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index fa48da212037..e2ecf6109516 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -79,8 +79,8 @@
/* Orientation */
#define REG_MIRROR_FLIP_CONTROL 0x0101
-#define REG_CONFIG_MIRROR_FLIP 0x03
-#define REG_CONFIG_FLIP_TEST_PATTERN 0x02
+#define REG_CONFIG_MIRROR_HFLIP 0x01
+#define REG_CONFIG_MIRROR_VFLIP 0x02
/* IMX258 native and active pixel array size. */
#define IMX258_NATIVE_WIDTH 4224U
@@ -485,6 +485,23 @@ static const struct imx258_variant_cfg imx258_pdaf_cfg = {
.num_regs = ARRAY_SIZE(imx258_pdaf_cfg_regs),
};
+/*
+ * The supported formats.
+ * This table MUST contain 4 entries per format, to cover the various flip
+ * combinations in the order
+ * - no flip
+ * - h flip
+ * - v flip
+ * - h&v flips
+ */
+static const u32 codes[] = {
+ /* 10-bit modes. */
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_1X10
+};
+
static const char * const imx258_test_pattern_menu[] = {
"Disabled",
"Solid Colour",
@@ -671,6 +688,8 @@ struct imx258 {
struct v4l2_ctrl *vblank;
struct v4l2_ctrl *hblank;
struct v4l2_ctrl *exposure;
+ struct v4l2_ctrl *hflip;
+ struct v4l2_ctrl *vflip;
/* Current mode */
const struct imx258_mode *cur_mode;
@@ -769,9 +788,23 @@ static int imx258_write_regs(struct imx258 *imx258,
return 0;
}
+/* Get bayer order based on flip setting. */
+static u32 imx258_get_format_code(const struct imx258 *imx258)
+{
+ unsigned int i;
+
+ lockdep_assert_held(&imx258->mutex);
+
+ i = (imx258->vflip->val ? 2 : 0) |
+ (imx258->hflip->val ? 1 : 0);
+
+ return codes[i];
+}
+
/* Open sub-device */
static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
{
+ struct imx258 *imx258 = to_imx258(sd);
struct v4l2_mbus_framefmt *try_fmt =
v4l2_subdev_state_get_format(fh->state, 0);
struct v4l2_rect *try_crop;
@@ -779,7 +812,7 @@ static int imx258_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
/* Initialize try_fmt */
try_fmt->width = supported_modes[0].width;
try_fmt->height = supported_modes[0].height;
- try_fmt->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ try_fmt->code = imx258_get_format_code(imx258);
try_fmt->field = V4L2_FIELD_NONE;
/* Initialize try_crop */
@@ -872,10 +905,6 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
ret = imx258_write_reg(imx258, IMX258_REG_TEST_PATTERN,
IMX258_REG_VALUE_16BIT,
ctrl->val);
- ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
- IMX258_REG_VALUE_08BIT,
- !ctrl->val ? REG_CONFIG_MIRROR_FLIP :
- REG_CONFIG_FLIP_TEST_PATTERN);
break;
case V4L2_CID_WIDE_DYNAMIC_RANGE:
if (!ctrl->val) {
@@ -898,6 +927,15 @@ static int imx258_set_ctrl(struct v4l2_ctrl *ctrl)
IMX258_REG_VALUE_16BIT,
imx258->cur_mode->height + ctrl->val);
break;
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HFLIP:
+ ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
+ IMX258_REG_VALUE_08BIT,
+ (imx258->hflip->val ?
+ REG_CONFIG_MIRROR_HFLIP : 0) |
+ (imx258->vflip->val ?
+ REG_CONFIG_MIRROR_VFLIP : 0));
+ break;
default:
dev_info(&client->dev,
"ctrl(id:0x%x,val:0x%x) is not handled\n",
@@ -919,11 +957,13 @@ static int imx258_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
- /* Only one bayer order(GRBG) is supported */
+ struct imx258 *imx258 = to_imx258(sd);
+
+ /* Only one bayer format (10 bit) is supported */
if (code->index > 0)
return -EINVAL;
- code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ code->code = imx258_get_format_code(imx258);
return 0;
}
@@ -932,10 +972,11 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
+ struct imx258 *imx258 = to_imx258(sd);
if (fse->index >= ARRAY_SIZE(supported_modes))
return -EINVAL;
- if (fse->code != MEDIA_BUS_FMT_SBGGR10_1X10)
+ if (fse->code != imx258_get_format_code(imx258))
return -EINVAL;
fse->min_width = supported_modes[fse->index].width;
@@ -946,12 +987,13 @@ static int imx258_enum_frame_size(struct v4l2_subdev *sd,
return 0;
}
-static void imx258_update_pad_format(const struct imx258_mode *mode,
+static void imx258_update_pad_format(struct imx258 *imx258,
+ const struct imx258_mode *mode,
struct v4l2_subdev_format *fmt)
{
fmt->format.width = mode->width;
fmt->format.height = mode->height;
- fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.code = imx258_get_format_code(imx258);
fmt->format.field = V4L2_FIELD_NONE;
}
@@ -963,7 +1005,7 @@ static int __imx258_get_pad_format(struct imx258 *imx258,
fmt->format = *v4l2_subdev_state_get_format(sd_state,
fmt->pad);
else
- imx258_update_pad_format(imx258->cur_mode, fmt);
+ imx258_update_pad_format(imx258, imx258->cur_mode, fmt);
return 0;
}
@@ -999,13 +1041,12 @@ static int imx258_set_pad_format(struct v4l2_subdev *sd,
mutex_lock(&imx258->mutex);
- /* Only one raw bayer(GBRG) order is supported */
- fmt->format.code = MEDIA_BUS_FMT_SBGGR10_1X10;
+ fmt->format.code = imx258_get_format_code(imx258);
mode = v4l2_find_nearest_size(supported_modes,
ARRAY_SIZE(supported_modes), width, height,
fmt->format.width, fmt->format.height);
- imx258_update_pad_format(mode, fmt);
+ imx258_update_pad_format(imx258, mode, fmt);
if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) {
framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
*framefmt = fmt->format;
@@ -1156,15 +1197,6 @@ static int imx258_start_streaming(struct imx258 *imx258)
return ret;
}
- /* Set Orientation be 180 degree */
- ret = imx258_write_reg(imx258, REG_MIRROR_FLIP_CONTROL,
- IMX258_REG_VALUE_08BIT, REG_CONFIG_MIRROR_FLIP);
- if (ret) {
- dev_err(&client->dev, "%s failed to set orientation\n",
- __func__);
- return ret;
- }
-
/* Apply customized values from user */
ret = __v4l2_ctrl_handler_setup(imx258->sd.ctrl_handler);
if (ret)
@@ -1317,7 +1349,6 @@ static int imx258_init_controls(struct imx258 *imx258)
struct i2c_client *client = v4l2_get_subdevdata(&imx258->sd);
const struct imx258_link_freq_config *link_freq_cfgs;
struct v4l2_fwnode_device_properties props;
- struct v4l2_ctrl *vflip, *hflip;
struct v4l2_ctrl_handler *ctrl_hdlr;
const struct imx258_link_cfg *link_cfg;
s64 vblank_def;
@@ -1342,16 +1373,15 @@ static int imx258_init_controls(struct imx258 *imx258)
if (imx258->link_freq)
imx258->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY;
- /* The driver only supports one bayer order and flips by default. */
- hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
- V4L2_CID_HFLIP, 1, 1, 1, 1);
- if (hflip)
- hflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ imx258->hflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
+ V4L2_CID_HFLIP, 0, 1, 1, 1);
+ if (imx258->hflip)
+ imx258->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
- vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
- V4L2_CID_VFLIP, 1, 1, 1, 1);
- if (vflip)
- vflip->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ imx258->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx258_ctrl_ops,
+ V4L2_CID_VFLIP, 0, 1, 1, 1);
+ if (imx258->vflip)
+ imx258->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
link_freq_cfgs = &imx258->link_freq_configs[0];
link_cfg = link_freq_cfgs[imx258->lane_mode_idx].link_cfg;
--
2.42.0
^ permalink raw reply related
* [PATCH v3 17/25] dt-bindings: media: imx258: Rename to include vendor prefix
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Conor Dooley, Luis Garcia
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Dave Stevenson <dave.stevenson@raspberrypi.com>
imx258.yaml doesn't include the vendor prefix of sony, so
rename to add it.
Update the id entry and MAINTAINERS to match.
Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
.../bindings/media/i2c/{imx258.yaml => sony,imx258.yaml} | 2 +-
MAINTAINERS | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
rename Documentation/devicetree/bindings/media/i2c/{imx258.yaml => sony,imx258.yaml} (97%)
diff --git a/Documentation/devicetree/bindings/media/i2c/imx258.yaml b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
similarity index 97%
rename from Documentation/devicetree/bindings/media/i2c/imx258.yaml
rename to Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
index 80d24220baa0..bee61a443b23 100644
--- a/Documentation/devicetree/bindings/media/i2c/imx258.yaml
+++ b/Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
@@ -1,7 +1,7 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
-$id: http://devicetree.org/schemas/media/i2c/imx258.yaml#
+$id: http://devicetree.org/schemas/media/i2c/sony,imx258.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Sony IMX258 13 Mpixel CMOS Digital Image Sensor
diff --git a/MAINTAINERS b/MAINTAINERS
index 8ceb49f1b630..a8c3a531ad39 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20489,7 +20489,7 @@ M: Sakari Ailus <sakari.ailus@linux.intel.com>
L: linux-media@vger.kernel.org
S: Maintained
T: git git://linuxtv.org/media_tree.git
-F: Documentation/devicetree/bindings/media/i2c/imx258.yaml
+F: Documentation/devicetree/bindings/media/i2c/sony,imx258.yaml
F: drivers/media/i2c/imx258.c
SONY IMX274 SENSOR DRIVER
--
2.42.0
^ permalink raw reply related
* [PATCH v3 23/25] drivers: media: i2c: imx258: Add support for powerdown gpio
From: git @ 2024-04-03 15:03 UTC (permalink / raw)
To: linux-media
Cc: dave.stevenson, jacopo.mondi, mchehab, robh,
krzysztof.kozlowski+dt, conor+dt, shawnguo, s.hauer, kernel,
festevam, sakari.ailus, devicetree, imx, linux-arm-kernel,
linux-kernel, pavel, phone-devel, Luis Garcia, Ondrej Jirman
In-Reply-To: <20240403150355.189229-1-git@luigi311.com>
From: Luis Garcia <git@luigi311.com>
On some boards powerdown signal needs to be deasserted for this
sensor to be enabled.
Signed-off-by: Ondrej Jirman <megi@xff.cz>
Signed-off-by: Luis Garcia <git@luigi311.com>
---
drivers/media/i2c/imx258.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/drivers/media/i2c/imx258.c b/drivers/media/i2c/imx258.c
index 30352c33f63c..163f04f6f954 100644
--- a/drivers/media/i2c/imx258.c
+++ b/drivers/media/i2c/imx258.c
@@ -679,6 +679,8 @@ struct imx258 {
unsigned int lane_mode_idx;
unsigned int csi2_flags;
+ struct gpio_desc *powerdown_gpio;
+
/*
* Mutex for serialized access:
* Protect sensor module set pad format and start/stop streaming safely.
@@ -1213,6 +1215,8 @@ static int imx258_power_on(struct device *dev)
struct imx258 *imx258 = to_imx258(sd);
int ret;
+ gpiod_set_value_cansleep(imx258->powerdown_gpio, 0);
+
ret = regulator_bulk_enable(IMX258_NUM_SUPPLIES,
imx258->supplies);
if (ret) {
@@ -1224,6 +1228,7 @@ static int imx258_power_on(struct device *dev)
ret = clk_prepare_enable(imx258->clk);
if (ret) {
dev_err(dev, "failed to enable clock\n");
+ gpiod_set_value_cansleep(imx258->powerdown_gpio, 1);
regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies);
}
@@ -1238,6 +1243,8 @@ static int imx258_power_off(struct device *dev)
clk_disable_unprepare(imx258->clk);
regulator_bulk_disable(IMX258_NUM_SUPPLIES, imx258->supplies);
+ gpiod_set_value_cansleep(imx258->powerdown_gpio, 1);
+
return 0;
}
@@ -1541,6 +1548,12 @@ static int imx258_probe(struct i2c_client *client)
if (!imx258->variant_cfg)
imx258->variant_cfg = &imx258_cfg;
+ /* request optional power down pin */
+ imx258->powerdown_gpio = devm_gpiod_get_optional(&client->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(imx258->powerdown_gpio))
+ return PTR_ERR(imx258->powerdown_gpio);
+
/* Initialize subdev */
v4l2_i2c_subdev_init(&imx258->sd, client, &imx258_subdev_ops);
--
2.42.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox