[Patch v4 00/10] Add audio support for LPC32XX CPUs
This pach set is to bring back audio to machines with a LPC32XX CPU. The legacy LPC32XX SoC used to have audio spport in linux 2.6.27. The support was dropped due to lack of interest from mainaeners.
Piotr Wojtaszczyk (10): dt-bindings: dma: pl08x: Add dma-cells description dt-bindings: dma: Add lpc32xx DMA mux binding ASoC: dt-bindings: lpc32xx: Add lpc32xx i2s DT binding ARM: dts: lpc32xx: Add missing dma and i2s properties clk: lpc32xx: initialize regmap using parent syscon dmaengine: Add dma router for pl08x in LPC32XX SoC ARM: lpc32xx: Remove pl08x platform data in favor for device tree mtd: rawnand: lpx32xx: Request DMA channels using DT entries ASoC: fsl: Add i2s and pcm drivers for LPC32xx CPUs i2x: pnx: Use threaded irq to fix warning from del_timer_sync()
.../devicetree/bindings/dma/arm-pl08x.yaml | 7 + .../bindings/dma/nxp,lpc3220-dmamux.yaml | 56 +++ .../bindings/sound/nxp,lpc3220-i2s.yaml | 73 ++++ MAINTAINERS | 21 + arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi | 53 ++- arch/arm/mach-lpc32xx/phy3250.c | 54 --- drivers/clk/Kconfig | 1 + drivers/clk/nxp/clk-lpc32xx.c | 10 +- drivers/dma/Kconfig | 9 + drivers/dma/Makefile | 1 + drivers/dma/lpc32xx-dmamux.c | 195 +++++++++ drivers/i2c/busses/i2c-pnx.c | 4 +- drivers/mtd/nand/raw/lpc32xx_mlc.c | 10 +- drivers/mtd/nand/raw/lpc32xx_slc.c | 10 +- sound/soc/fsl/Kconfig | 7 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/lpc3xxx-i2s.c | 376 ++++++++++++++++++ sound/soc/fsl/lpc3xxx-i2s.h | 79 ++++ sound/soc/fsl/lpc3xxx-pcm.c | 73 ++++ 19 files changed, 954 insertions(+), 87 deletions(-) create mode 100644 Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml create mode 100644 Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml create mode 100644 drivers/dma/lpc32xx-dmamux.c create mode 100644 sound/soc/fsl/lpc3xxx-i2s.c create mode 100644 sound/soc/fsl/lpc3xxx-i2s.h create mode 100644 sound/soc/fsl/lpc3xxx-pcm.c
Recover dma-cells description from the legacy DT binding.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is new in v4
Documentation/devicetree/bindings/dma/arm-pl08x.yaml | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/dma/arm-pl08x.yaml b/Documentation/devicetree/bindings/dma/arm-pl08x.yaml index ab25ae63d2c3..191215d36c85 100644 --- a/Documentation/devicetree/bindings/dma/arm-pl08x.yaml +++ b/Documentation/devicetree/bindings/dma/arm-pl08x.yaml @@ -52,6 +52,13 @@ properties: clock-names: maxItems: 1
+ "#dma-cells": + const: 2 + description: | + First cell should contain the DMA request, + second cell should contain either 1 or 2 depending on + which AHB master that is used. + lli-bus-interface-ahb1: type: boolean description: if AHB master 1 is eligible for fetching LLIs
On 20/06/2024 19:56, Piotr Wojtaszczyk wrote:
Recover dma-cells description from the legacy DT binding.
Fixes: 6f64aa5746d2 ("dt-bindings: dma: convert arm-pl08x to yaml") Reviewed-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
Best regards, Krzysztof
LPC32XX SoCs use pl080 dma controller which have few request signals multiplexed between peripherals. This binding describes how devices can use the multiplexed request signals.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is new in v4
.../bindings/dma/nxp,lpc3220-dmamux.yaml | 56 +++++++++++++++++++ MAINTAINERS | 9 +++ 2 files changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml
diff --git a/Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml b/Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml new file mode 100644 index 000000000000..a5384b6c67fc --- /dev/null +++ b/Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml @@ -0,0 +1,56 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/nxp,lpc3220-dmamux.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: DMA multiplexer for LPC32XX SoC (DMA request router) + +maintainers: + - J.M.B. Downing jonathan.downing@nautel.com + - Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com + +allOf: + - $ref: dma-router.yaml# + +properties: + "#dma-cells": + const: 3 + description: | + First two cells same as for device pointed in dma-masters. + Third cell represents mux value for the request. + + compatible: + const: nxp,lpc3220-dmamux + + dma-masters: + description: phandle to a dma node compatible with arm,pl080 + + reg: + maxItems: 1 + +required: + - compatible + - reg + - dma-masters + +additionalProperties: false + +examples: + - | + syscon@40004000 { + compatible = "nxp,lpc3220-creg", "syscon", "simple-mfd"; + reg = <0x40004000 0x114>; + ranges = <0 0x40004000 0x114>; + #address-cells = <1>; + #size-cells = <1>; + + dma-router@7c { + compatible = "nxp,lpc3220-dmamux"; + reg = <0x7c 0x8>; + #dma-cells = <3>; + dma-masters = <&dma>; + }; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index aacccb376c28..f7adf9f66dfa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2396,6 +2396,15 @@ F: drivers/usb/host/ohci-nxp.c F: drivers/watchdog/pnx4008_wdt.c N: lpc32xx
+ARM/LPC32XX DMAMUX SUPPORT +M: J.M.B. Downing jonathan.downing@nautel.com +M: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com +R: Vladimir Zapolskiy vz@mleia.com +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml +N: lpc32xx + ARM/Marvell Dove/MV78xx0/Orion SOC support M: Andrew Lunn andrew@lunn.ch M: Sebastian Hesselbarth sebastian.hesselbarth@gmail.com
On Thu, 20 Jun 2024 19:56:33 +0200, Piotr Wojtaszczyk wrote:
LPC32XX SoCs use pl080 dma controller which have few request signals multiplexed between peripherals. This binding describes how devices can use the multiplexed request signals.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
Changes for v4:
- This patch is new in v4
.../bindings/dma/nxp,lpc3220-dmamux.yaml | 56 +++++++++++++++++++ MAINTAINERS | 9 +++ 2 files changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors: Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.example.dtb: /example-0/syscon@40004000: failed to match any schema with compatible: ['nxp,lpc3220-creg', 'syscon', 'simple-mfd']
doc reference errors (make refcheckdocs):
See https://patchwork.ozlabs.org/project/devicetree-bindings/patch/2024062017565...
The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema.
On 20/06/2024 19:56, Piotr Wojtaszczyk wrote:
LPC32XX SoCs use pl080 dma controller which have few request signals multiplexed between peripherals. This binding describes how devices can use the multiplexed request signals.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
+properties:
- "#dma-cells":
- const: 3
- description: |
First two cells same as for device pointed in dma-masters.
Third cell represents mux value for the request.
- compatible:
- const: nxp,lpc3220-dmamux
Please put compatible first in the list of properties (and follow the same order in "required"). It's the most important piece, so we want it to be the first to see. It also follows the convention of DTS, where compatible is expected to be first.
- dma-masters:
- description: phandle to a dma node compatible with arm,pl080
maxItems: 1
- reg:
- maxItems: 1
Keep reg after compatible.
+required:
- compatible
- reg
- dma-masters
+additionalProperties: false
+examples:
- |
- syscon@40004000 {
compatible = "nxp,lpc3220-creg", "syscon", "simple-mfd";
reg = <0x40004000 0x114>;
ranges = <0 0x40004000 0x114>;
#address-cells = <1>;
#size-cells = <1>;
Drop the node above (you will see Rob's warning). Alternatively, this schema could skip the example and the nxp,lpc3220-creg could have one complete example for entire device with children.
dma-router@7c {
compatible = "nxp,lpc3220-dmamux";
reg = <0x7c 0x8>;
#dma-cells = <3>;
dma-masters = <&dma>;
};
- };
+... diff --git a/MAINTAINERS b/MAINTAINERS index aacccb376c28..f7adf9f66dfa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2396,6 +2396,15 @@ F: drivers/usb/host/ohci-nxp.c F: drivers/watchdog/pnx4008_wdt.c N: lpc32xx
+ARM/LPC32XX DMAMUX SUPPORT
This should be just "LPC32XX DMAMUX SUPPORT"
+M: J.M.B. Downing jonathan.downing@nautel.com +M: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com +R: Vladimir Zapolskiy vz@mleia.com +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml +N: lpc32xx
I think this entry is only foor DMAMUX so the last "N:" is not appropriate.
You are welcomed to help maintaining the platform. Add yourself to appropriate place of LPC32xx ARM.
Best regards, Krzysztof
Add nxp,lpc3220-i2s DT binding documentation.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - Custom dma-vc-names property with standard dmas and dma-names - Added to MAINTAINERS
Changes for v3: - Added '$ref: dai-common.yaml#' and '#sound-dai-cells' - Dropped all clock-names, references - Dropped status property from the example - Added interrupts property - 'make dt_binding_check' pass
Changes for v2: - Added maintainers field - Dropped clock-names - Dropped unused unneded interrupts field
.../bindings/sound/nxp,lpc3220-i2s.yaml | 73 +++++++++++++++++++ MAINTAINERS | 10 +++ 2 files changed, 83 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml
diff --git a/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml b/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml new file mode 100644 index 000000000000..40a0877a8aba --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml @@ -0,0 +1,73 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nxp,lpc3220-i2s.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: NXP LPC32XX I2S Controller + +description: + The I2S controller in LPC32XX SoCs, ASoC DAI. + +maintainers: + - J.M.B. Downing jonathan.downing@nautel.com + - Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com + +allOf: + - $ref: dai-common.yaml# + +properties: + compatible: + enum: + - nxp,lpc3220-i2s + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: input clock of the peripheral. + + dmas: + items: + - description: RX DMA Channel + - description: TX DMA Channel + + dma-names: + items: + - const: rx + - const: tx + + "#sound-dai-cells": + const: 0 + +required: + - compatible + - reg + - interrupts + - clocks + - dmas + - dma-names + - '#sound-dai-cells' + +additionalProperties: false + +examples: + - | + #include <dt-bindings/clock/lpc32xx-clock.h> + #include <dt-bindings/interrupt-controller/irq.h> + + i2s@20094000 { + compatible = "nxp,lpc3220-i2s"; + reg = <0x20094000 0x1000>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_I2S0>; + dmas = <&dma 0 1>, <&dma 13 1>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; + }; + +... diff --git a/MAINTAINERS b/MAINTAINERS index f7adf9f66dfa..fadf1baafd89 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8918,6 +8918,16 @@ S: Maintained F: sound/soc/fsl/fsl* F: sound/soc/fsl/imx*
+FREESCALE SOC LPC32XX SOUND DRIVERS +M: J.M.B. Downing jonathan.downing@nautel.com +M: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com +R: Vladimir Zapolskiy vz@mleia.com +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linuxppc-dev@lists.ozlabs.org +S: Maintained +F: Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml +N: lpc32xx + FREESCALE SOC SOUND QMC DRIVER M: Herve Codina herve.codina@bootlin.com L: alsa-devel@alsa-project.org (moderated for non-subscribers)
On 20/06/2024 19:56, Piotr Wojtaszczyk wrote:
Add nxp,lpc3220-i2s DT binding documentation.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
Thanks for doing this. Appreciated.
+FREESCALE SOC LPC32XX SOUND DRIVERS +M: J.M.B. Downing jonathan.downing@nautel.com +M: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com +R: Vladimir Zapolskiy vz@mleia.com +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +L: linuxppc-dev@lists.ozlabs.org +S: Maintained +F: Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml +N: lpc32xx
Drop the last "N:".
Reviewed-by: Krzysztof Kozlowski krzysztof.kozlowski@linaro.org
Best regards, Krzysztof
Adds properties declared in the new DT bindings: - nxp,lpc3220-i2s.yaml - nxp,lpc3220-dmamux.yaml for dma router/mux and I2S interface.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is renamed from "ARM: dts: lpc32xx: Add missing properties for the i2s interfaces" to describe dma changes as well - Added dmas and dma-names properties in to all node which have dma request signals - Add bus properties to pl08x dma node since they are removed from platform data in phy3250.c - Put clock-controller@0 and dma-router@7c under the same syscon, simple-mfd device
Changes for v3: - Split previous commit for separate subsystems - Add properties to match dt binding
arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi | 53 +++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 5 deletions(-)
diff --git a/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi b/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi index 974410918f35..c58dc127e59f 100644 --- a/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi +++ b/arch/arm/boot/dts/nxp/lpc/lpc32xx.dtsi @@ -67,6 +67,8 @@ slc: flash@20020000 { reg = <0x20020000 0x1000>; clocks = <&clk LPC32XX_CLK_SLC>; status = "disabled"; + dmas = <&dma 1 1>; + dma-names = "rx-tx"; };
mlc: flash@200a8000 { @@ -75,6 +77,8 @@ mlc: flash@200a8000 { interrupts = <11 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk LPC32XX_CLK_MLC>; status = "disabled"; + dmas = <&dma 12 1>; + dma-names = "rx-tx"; };
dma: dma@31000000 { @@ -83,6 +87,13 @@ dma: dma@31000000 { interrupts = <28 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk LPC32XX_CLK_DMA>; clock-names = "apb_pclk"; + #dma-cells = <2>; + dma-channels = <8>; + dma-requests = <16>; + lli-bus-interface-ahb1; + mem-bus-interface-ahb1; + memcpy-burst-size = <256>; + memcpy-bus-width = <32>; };
usb { @@ -182,6 +193,8 @@ ssp0: spi@20084000 { clock-names = "apb_pclk"; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux 14 1 1>, <&dmamux 15 1 1>; + dma-names = "rx", "tx"; status = "disabled"; };
@@ -191,6 +204,8 @@ spi1: spi@20088000 { clocks = <&clk LPC32XX_CLK_SPI1>; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux 11 1 0>; + dma-names = "rx-tx"; status = "disabled"; };
@@ -206,6 +221,8 @@ ssp1: spi@2008c000 { clock-names = "apb_pclk"; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux 3 1 1>, <&dmamux 11 1 1>; + dma-names = "rx", "tx"; status = "disabled"; };
@@ -215,12 +232,19 @@ spi2: spi@20090000 { clocks = <&clk LPC32XX_CLK_SPI2>; #address-cells = <1>; #size-cells = <0>; + dmas = <&dmamux 3 1 0>; + dma-names = "rx-tx"; status = "disabled"; };
i2s0: i2s@20094000 { compatible = "nxp,lpc3220-i2s"; reg = <0x20094000 0x1000>; + interrupts = <22 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_I2S0>; + dmas = <&dma 0 1>, <&dma 13 1>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; status = "disabled"; };
@@ -231,12 +255,19 @@ sd: sd@20098000 { <13 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk LPC32XX_CLK_SD>; clock-names = "apb_pclk"; + dmas = <&dma 4 1>; + dma-names = "rx"; status = "disabled"; };
i2s1: i2s@2009c000 { compatible = "nxp,lpc3220-i2s"; reg = <0x2009c000 0x1000>; + interrupts = <23 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clk LPC32XX_CLK_I2S1>; + dmas = <&dma 2 1>, <&dmamux 10 1 1>; + dma-names = "rx", "tx"; + #sound-dai-cells = <0>; status = "disabled"; };
@@ -312,21 +343,27 @@ fab { compatible = "simple-bus"; ranges = <0x20000000 0x20000000 0x30000000>;
- /* System Control Block */ - scb { - compatible = "simple-bus"; - ranges = <0x0 0x40004000 0x00001000>; + syscon@40004000 { + compatible = "nxp,lpc3220-creg", "syscon", "simple-mfd"; + reg = <0x40004000 0x114>; #address-cells = <1>; #size-cells = <1>; + ranges = <0 0x40004000 0x114>;
clk: clock-controller@0 { compatible = "nxp,lpc3220-clk"; reg = <0x00 0x114>; #clock-cells = <1>; - clocks = <&xtal_32k>, <&xtal>; clock-names = "xtal_32k", "xtal"; }; + + dmamux: dma-router@7c { + compatible = "nxp,lpc3220-dmamux"; + reg = <0x7c 0x8>; + #dma-cells = <3>; + dma-masters = <&dma>; + }; };
mic: interrupt-controller@40008000 { @@ -362,6 +399,8 @@ uart1: serial@40014000 { compatible = "nxp,lpc3220-hsuart"; reg = <0x40014000 0x1000>; interrupts = <26 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dma 6 1>, <&dma 5 1>; + dma-names = "rx", "tx"; status = "disabled"; };
@@ -369,6 +408,8 @@ uart2: serial@40018000 { compatible = "nxp,lpc3220-hsuart"; reg = <0x40018000 0x1000>; interrupts = <25 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dma 8 1>, <&dma 7 1>; + dma-names = "rx", "tx"; status = "disabled"; };
@@ -376,6 +417,8 @@ uart7: serial@4001c000 { compatible = "nxp,lpc3220-hsuart"; reg = <0x4001c000 0x1000>; interrupts = <24 IRQ_TYPE_LEVEL_HIGH>; + dmas = <&dmamux 10 1 0>, <&dma 9 1>; + dma-names = "rx", "tx"; status = "disabled"; };
Adds properties declared in the new DT bindings:
Add? How do you think about to replace such an abbreviation?
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Docu...
Regards, Markus
On 20/06/2024 19:56, Piotr Wojtaszczyk wrote:
Adds properties declared in the new DT bindings:
- nxp,lpc3220-i2s.yaml
- nxp,lpc3220-dmamux.yaml
for dma router/mux and I2S interface.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
You are doing here multiple things at once. This should be
One patch is dma properties.
i2s0: i2s@20094000 { compatible = "nxp,lpc3220-i2s"; reg = <0x20094000 0x1000>;
interrupts = <22 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk LPC32XX_CLK_I2S0>;
dmas = <&dma 0 1>, <&dma 13 1>;
dma-names = "rx", "tx";
#sound-dai-cells = <0>;
Sound dai cells is another patch (or entire node is separate patch).
status = "disabled"; };
@@ -231,12 +255,19 @@ sd: sd@20098000 { <13 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clk LPC32XX_CLK_SD>; clock-names = "apb_pclk";
dmas = <&dma 4 1>;
dma-names = "rx"; status = "disabled"; }; i2s1: i2s@2009c000 { compatible = "nxp,lpc3220-i2s"; reg = <0x2009c000 0x1000>;
interrupts = <23 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk LPC32XX_CLK_I2S1>;
dmas = <&dma 2 1>, <&dmamux 10 1 1>;
dma-names = "rx", "tx";
#sound-dai-cells = <0>; status = "disabled"; };
@@ -312,21 +343,27 @@ fab { compatible = "simple-bus"; ranges = <0x20000000 0x20000000 0x30000000>;
/* System Control Block */
scb {
compatible = "simple-bus";
ranges = <0x0 0x40004000 0x00001000>;
syscon@40004000 {
compatible = "nxp,lpc3220-creg", "syscon", "simple-mfd";
reg = <0x40004000 0x114>;
This hunk is also separate patch.
Best regards, Krzysztof
This allows to share the regmap with other simple-mfd devices like nxp,lpc32xx-dmamux
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is new in v4
drivers/clk/Kconfig | 1 + drivers/clk/nxp/clk-lpc32xx.c | 10 ++-------- 2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 3e9099504fad..85ef57d5cccf 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -346,6 +346,7 @@ config COMMON_CLK_LOONGSON2 config COMMON_CLK_NXP def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX) select REGMAP_MMIO if ARCH_LPC32XX + select MFD_SYSCON if ARCH_LPC32XX select MFD_SYSCON if ARCH_LPC18XX help Support for clock providers on NXP platforms. diff --git a/drivers/clk/nxp/clk-lpc32xx.c b/drivers/clk/nxp/clk-lpc32xx.c index d0f870eff0d6..2a183a9ded93 100644 --- a/drivers/clk/nxp/clk-lpc32xx.c +++ b/drivers/clk/nxp/clk-lpc32xx.c @@ -7,6 +7,7 @@ #include <linux/clk-provider.h> #include <linux/io.h> #include <linux/of_address.h> +#include <linux/mfd/syscon.h> #include <linux/regmap.h>
#include <dt-bindings/clock/lpc32xx-clock.h> @@ -1511,17 +1512,10 @@ static void __init lpc32xx_clk_init(struct device_node *np) return; }
- base = of_iomap(np, 0); - if (!base) { - pr_err("failed to map system control block registers\n"); - return; - } - - clk_regmap = regmap_init_mmio(NULL, base, &lpc32xx_scb_regmap_config); + clk_regmap = syscon_node_to_regmap(np->parent); if (IS_ERR(clk_regmap)) { pr_err("failed to regmap system control block: %ld\n", PTR_ERR(clk_regmap)); - iounmap(base); return; }
This allows to share the regmap with other simple-mfd devices like nxp,lpc32xx-dmamux
Please choose an imperative wording for an improved change description. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Docu...
Regards, Markus
On 20/06/2024 19:56, Piotr Wojtaszczyk wrote:
- base = of_iomap(np, 0);
- if (!base) {
pr_err("failed to map system control block registers\n");
return;
- }
- clk_regmap = regmap_init_mmio(NULL, base, &lpc32xx_scb_regmap_config);
- clk_regmap = syscon_node_to_regmap(np->parent); if (IS_ERR(clk_regmap)) { pr_err("failed to regmap system control block: %ld\n", PTR_ERR(clk_regmap));
iounmap(base);
This looks backwards incompatible. You should keep the fallback way.
Best regards, Krzysztof
LPC32XX connects few of its peripherals to pl08x DMA thru a multiplexer, this driver allows to route a signal request line thru the multiplexer for given peripheral.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is new in v4
MAINTAINERS | 1 + drivers/dma/Kconfig | 9 ++ drivers/dma/Makefile | 1 + drivers/dma/lpc32xx-dmamux.c | 195 +++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 drivers/dma/lpc32xx-dmamux.c
diff --git a/MAINTAINERS b/MAINTAINERS index fadf1baafd89..5ffe988ee282 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2403,6 +2403,7 @@ R: Vladimir Zapolskiy vz@mleia.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml +F: drivers/dma/lpc32xx-dmamux.c N: lpc32xx
ARM/Marvell Dove/MV78xx0/Orion SOC support diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 002a5ec80620..aeace3d7e066 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -378,6 +378,15 @@ config LPC18XX_DMAMUX Enable support for DMA on NXP LPC18xx/43xx platforms with PL080 and multiplexed DMA request lines.
+config LPC32XX_DMAMUX + bool "NXP LPC32xx DMA MUX for PL080" + depends on ARCH_LPC32XX || COMPILE_TEST + depends on OF && AMBA_PL08X + select MFD_SYSCON + help + Support for PL080 multiplexed DMA request lines on + LPC32XX platrofm. + config LS2X_APB_DMA tristate "Loongson LS2X APB DMA support" depends on LOONGARCH || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 802ca916f05f..6f1350b62e7f 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-y += idxd/ obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o +obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o obj-$(CONFIG_MILBEAUT_XDMAC) += milbeaut-xdmac.o diff --git a/drivers/dma/lpc32xx-dmamux.c b/drivers/dma/lpc32xx-dmamux.c new file mode 100644 index 000000000000..4e6ce6026164 --- /dev/null +++ b/drivers/dma/lpc32xx-dmamux.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2024 Timesys Corporation piotr.wojtaszczyk@timesys.com +// +// Based on TI DMA Crossbar driver by: +// Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com +// Author: Peter Ujfalusi peter.ujfalusi@ti.com + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> + +#define LPC32XX_SSP_CLK_CTRL 0x78 +#define LPC32XX_I2S_CLK_CTRL 0x7c + +struct lpc32xx_dmamux { + int signal; + char *name_sel0; + char *name_sel1; + int muxval; + int muxreg; + int bit; + bool busy; +}; + +/* From LPC32x0 User manual "3.2.1 DMA request signals" */ +static struct lpc32xx_dmamux lpc32xx_muxes[] = { + { + .signal = 3, + .name_sel0 = "spi2-rx-tx", + .name_sel1 = "ssp1-rx", + .muxreg = LPC32XX_SSP_CLK_CTRL, + .bit = 5, + }, + { + .signal = 10, + .name_sel0 = "uart7-rx", + .name_sel1 = "i2s1-dma1", + .muxreg = LPC32XX_I2S_CLK_CTRL, + .bit = 4, + }, + { + .signal = 11, + .name_sel0 = "spi1-rx-tx", + .name_sel1 = "ssp1-tx", + .muxreg = LPC32XX_SSP_CLK_CTRL, + .bit = 4, + }, + { + .signal = 14, + .name_sel0 = "none", + .name_sel1 = "ssp0-rx", + .muxreg = LPC32XX_SSP_CLK_CTRL, + .bit = 3, + }, + { + .signal = 15, + .name_sel0 = "none", + .name_sel1 = "ssp0-tx", + .muxreg = LPC32XX_SSP_CLK_CTRL, + .bit = 2, + }, +}; + +struct lpc32xx_dmamux_data { + struct dma_router dmarouter; + struct regmap *reg; + spinlock_t lock; /* protects busy status flag */ +}; + +static void lpc32xx_dmamux_release(struct device *dev, void *route_data) +{ + struct lpc32xx_dmamux_data *dmamux = dev_get_drvdata(dev); + struct lpc32xx_dmamux *mux = route_data; + unsigned long flags; + + dev_dbg(dev, "releasing dma request signal %d routed to %s\n", + mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); + + guard(spinlock)(&dmamux->lock); + + mux->busy = false; +} + +static void *lpc32xx_dmamux_reserve(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); + struct device *dev = &pdev->dev; + struct lpc32xx_dmamux_data *dmamux = platform_get_drvdata(pdev); + unsigned long flags; + struct lpc32xx_dmamux *mux = NULL; + int i; + + if (dma_spec->args_count != 3) { + dev_err(&pdev->dev, "invalid number of dma mux args\n"); + return ERR_PTR(-EINVAL); + } + + for (i = 0; i < ARRAY_SIZE(lpc32xx_muxes); i++) { + if (lpc32xx_muxes[i].signal == dma_spec->args[0]) + mux = &lpc32xx_muxes[i]; + } + if (!mux) { + dev_err(&pdev->dev, "invalid mux request number: %d\n", + dma_spec->args[0]); + return ERR_PTR(-EINVAL); + } + + if (dma_spec->args[2] > 1) { + dev_err(&pdev->dev, "invalid dma mux value: %d\n", + dma_spec->args[1]); + return ERR_PTR(-EINVAL); + } + + /* The of_node_put() will be done in the core for the node */ + dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); + if (!dma_spec->np) { + dev_err(&pdev->dev, "can't get dma master\n"); + return ERR_PTR(-EINVAL); + } + + spin_lock_irqsave(&dmamux->lock, flags); + if (mux->busy) { + spin_unlock_irqrestore(&dmamux->lock, flags); + dev_err(dev, "dma request signal %d busy, routed to %s\n", + mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); + of_node_put(dma_spec->np); + return ERR_PTR(-EBUSY); + } + + mux->busy = true; + mux->muxval = dma_spec->args[2] ? BIT(mux->bit) : 0; + + regmap_update_bits(dmamux->reg, mux->muxreg, BIT(mux->bit), mux->muxval); + spin_unlock_irqrestore(&dmamux->lock, flags); + + dma_spec->args[2] = 0; + dma_spec->args_count = 2; + + dev_dbg(dev, "dma request signal %d routed to %s\n", + mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); + + return mux; +} + +static int lpc32xx_dmamux_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct lpc32xx_dmamux_data *dmamux; + int ret; + + dmamux = devm_kzalloc(&pdev->dev, sizeof(*dmamux), GFP_KERNEL); + if (!dmamux) + return -ENOMEM; + + dmamux->reg = syscon_node_to_regmap(np->parent); + if (IS_ERR(dmamux->reg)) { + dev_err(&pdev->dev, "syscon lookup failed\n"); + return PTR_ERR(dmamux->reg); + } + + spin_lock_init(&dmamux->lock); + platform_set_drvdata(pdev, dmamux); + dmamux->dmarouter.dev = &pdev->dev; + dmamux->dmarouter.route_free = lpc32xx_dmamux_release; + + return of_dma_router_register(np, lpc32xx_dmamux_reserve, + &dmamux->dmarouter); +} + +static const struct of_device_id lpc32xx_dmamux_match[] = { + { .compatible = "nxp,lpc3220-dmamux" }, + {}, +}; + +static struct platform_driver lpc32xx_dmamux_driver = { + .probe = lpc32xx_dmamux_probe, + .driver = { + .name = "lpc32xx-dmamux", + .of_match_table = lpc32xx_dmamux_match, + }, +}; + +static int __init lpc32xx_dmamux_init(void) +{ + return platform_driver_register(&lpc32xx_dmamux_driver); +} +arch_initcall(lpc32xx_dmamux_init);
…
this driver allows to route a signal request line thru the multiplexer for given peripheral.
Would you like to choose an imperative wording for an improved change description? https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Docu...
Regards, Markus
On 20/06/2024 19:56, Piotr Wojtaszczyk wrote:
LPC32XX connects few of its peripherals to pl08x DMA thru a multiplexer, this driver allows to route a signal request line thru the multiplexer for given peripheral.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
Changes for v4:
- This patch is new in v4
MAINTAINERS | 1 + drivers/dma/Kconfig | 9 ++ drivers/dma/Makefile | 1 + drivers/dma/lpc32xx-dmamux.c | 195 +++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 drivers/dma/lpc32xx-dmamux.c
diff --git a/MAINTAINERS b/MAINTAINERS index fadf1baafd89..5ffe988ee282 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2403,6 +2403,7 @@ R: Vladimir Zapolskiy vz@mleia.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml +F: drivers/dma/lpc32xx-dmamux.c N: lpc32xx
ARM/Marvell Dove/MV78xx0/Orion SOC support diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 002a5ec80620..aeace3d7e066 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -378,6 +378,15 @@ config LPC18XX_DMAMUX Enable support for DMA on NXP LPC18xx/43xx platforms with PL080 and multiplexed DMA request lines.
+config LPC32XX_DMAMUX
- bool "NXP LPC32xx DMA MUX for PL080"
- depends on ARCH_LPC32XX || COMPILE_TEST
- depends on OF && AMBA_PL08X
- select MFD_SYSCON
- help
Support for PL080 multiplexed DMA request lines on
LPC32XX platrofm.
config LS2X_APB_DMA tristate "Loongson LS2X APB DMA support" depends on LOONGARCH || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 802ca916f05f..6f1350b62e7f 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-y += idxd/ obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o +obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o obj-$(CONFIG_MILBEAUT_XDMAC) += milbeaut-xdmac.o diff --git a/drivers/dma/lpc32xx-dmamux.c b/drivers/dma/lpc32xx-dmamux.c new file mode 100644 index 000000000000..4e6ce6026164 --- /dev/null +++ b/drivers/dma/lpc32xx-dmamux.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2024 Timesys Corporation piotr.wojtaszczyk@timesys.com +// +// Based on TI DMA Crossbar driver by: +// Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com +// Author: Peter Ujfalusi peter.ujfalusi@ti.com
+#include <linux/err.h> +#include <linux/init.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spinlock.h>
+#define LPC32XX_SSP_CLK_CTRL 0x78 +#define LPC32XX_I2S_CLK_CTRL 0x7c
+struct lpc32xx_dmamux {
- int signal;
- char *name_sel0;
- char *name_sel1;
- int muxval;
- int muxreg;
- int bit;
- bool busy;
+};
+/* From LPC32x0 User manual "3.2.1 DMA request signals" */ +static struct lpc32xx_dmamux lpc32xx_muxes[] = {
- {
.signal = 3,
.name_sel0 = "spi2-rx-tx",
.name_sel1 = "ssp1-rx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 5,
- },
- {
.signal = 10,
.name_sel0 = "uart7-rx",
.name_sel1 = "i2s1-dma1",
.muxreg = LPC32XX_I2S_CLK_CTRL,
.bit = 4,
- },
- {
.signal = 11,
.name_sel0 = "spi1-rx-tx",
.name_sel1 = "ssp1-tx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 4,
- },
- {
.signal = 14,
.name_sel0 = "none",
.name_sel1 = "ssp0-rx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 3,
- },
- {
.signal = 15,
.name_sel0 = "none",
.name_sel1 = "ssp0-tx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 2,
- },
+};
+struct lpc32xx_dmamux_data {
- struct dma_router dmarouter;
- struct regmap *reg;
- spinlock_t lock; /* protects busy status flag */
+};
Please keep declarations of structures before definitions, so this should go before lpc32xx_muxes.
...
- dmamux->reg = syscon_node_to_regmap(np->parent);
- if (IS_ERR(dmamux->reg)) {
dev_err(&pdev->dev, "syscon lookup failed\n");
return PTR_ERR(dmamux->reg);
This should be rather: return dev_err_probe().
Best regards, Krzysztof
Hi Piotr,
Any reason why dmaengine parts cant be sent separately, why are they clubbed together, I dont see any obvious dependencies...
On 20-06-24, 19:56, Piotr Wojtaszczyk wrote:
LPC32XX connects few of its peripherals to pl08x DMA thru a multiplexer, this driver allows to route a signal request line thru the multiplexer for given peripheral.
What is the difference b/w this and lpc18xx driver, why not reuse that one?
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
Changes for v4:
- This patch is new in v4
MAINTAINERS | 1 + drivers/dma/Kconfig | 9 ++ drivers/dma/Makefile | 1 + drivers/dma/lpc32xx-dmamux.c | 195 +++++++++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+) create mode 100644 drivers/dma/lpc32xx-dmamux.c
diff --git a/MAINTAINERS b/MAINTAINERS index fadf1baafd89..5ffe988ee282 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2403,6 +2403,7 @@ R: Vladimir Zapolskiy vz@mleia.com L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/dma/nxp,lpc3220-dmamux.yaml +F: drivers/dma/lpc32xx-dmamux.c N: lpc32xx
ARM/Marvell Dove/MV78xx0/Orion SOC support diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 002a5ec80620..aeace3d7e066 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -378,6 +378,15 @@ config LPC18XX_DMAMUX Enable support for DMA on NXP LPC18xx/43xx platforms with PL080 and multiplexed DMA request lines.
+config LPC32XX_DMAMUX
- bool "NXP LPC32xx DMA MUX for PL080"
- depends on ARCH_LPC32XX || COMPILE_TEST
- depends on OF && AMBA_PL08X
- select MFD_SYSCON
- help
Support for PL080 multiplexed DMA request lines on
LPC32XX platrofm.
config LS2X_APB_DMA tristate "Loongson LS2X APB DMA support" depends on LOONGARCH || COMPILE_TEST diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 802ca916f05f..6f1350b62e7f 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_INTEL_IOATDMA) += ioat/ obj-y += idxd/ obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o +obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o obj-$(CONFIG_LS2X_APB_DMA) += ls2x-apb-dma.o obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o obj-$(CONFIG_MILBEAUT_XDMAC) += milbeaut-xdmac.o diff --git a/drivers/dma/lpc32xx-dmamux.c b/drivers/dma/lpc32xx-dmamux.c new file mode 100644 index 000000000000..4e6ce6026164 --- /dev/null +++ b/drivers/dma/lpc32xx-dmamux.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2024 Timesys Corporation piotr.wojtaszczyk@timesys.com +// +// Based on TI DMA Crossbar driver by: +// Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com +// Author: Peter Ujfalusi peter.ujfalusi@ti.com
+#include <linux/err.h> +#include <linux/init.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/spinlock.h>
+#define LPC32XX_SSP_CLK_CTRL 0x78 +#define LPC32XX_I2S_CLK_CTRL 0x7c
+struct lpc32xx_dmamux {
- int signal;
- char *name_sel0;
- char *name_sel1;
- int muxval;
- int muxreg;
- int bit;
- bool busy;
+};
+/* From LPC32x0 User manual "3.2.1 DMA request signals" */ +static struct lpc32xx_dmamux lpc32xx_muxes[] = {
- {
.signal = 3,
.name_sel0 = "spi2-rx-tx",
.name_sel1 = "ssp1-rx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 5,
- },
- {
.signal = 10,
.name_sel0 = "uart7-rx",
.name_sel1 = "i2s1-dma1",
.muxreg = LPC32XX_I2S_CLK_CTRL,
.bit = 4,
- },
- {
.signal = 11,
.name_sel0 = "spi1-rx-tx",
.name_sel1 = "ssp1-tx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 4,
- },
- {
.signal = 14,
.name_sel0 = "none",
.name_sel1 = "ssp0-rx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 3,
- },
- {
.signal = 15,
.name_sel0 = "none",
.name_sel1 = "ssp0-tx",
.muxreg = LPC32XX_SSP_CLK_CTRL,
.bit = 2,
- },
+};
+struct lpc32xx_dmamux_data {
- struct dma_router dmarouter;
- struct regmap *reg;
- spinlock_t lock; /* protects busy status flag */
+};
+static void lpc32xx_dmamux_release(struct device *dev, void *route_data) +{
- struct lpc32xx_dmamux_data *dmamux = dev_get_drvdata(dev);
- struct lpc32xx_dmamux *mux = route_data;
- unsigned long flags;
- dev_dbg(dev, "releasing dma request signal %d routed to %s\n",
mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1);
- guard(spinlock)(&dmamux->lock);
- mux->busy = false;
+}
+static void *lpc32xx_dmamux_reserve(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
+{
- struct platform_device *pdev = of_find_device_by_node(ofdma->of_node);
- struct device *dev = &pdev->dev;
- struct lpc32xx_dmamux_data *dmamux = platform_get_drvdata(pdev);
- unsigned long flags;
- struct lpc32xx_dmamux *mux = NULL;
- int i;
- if (dma_spec->args_count != 3) {
dev_err(&pdev->dev, "invalid number of dma mux args\n");
return ERR_PTR(-EINVAL);
- }
- for (i = 0; i < ARRAY_SIZE(lpc32xx_muxes); i++) {
if (lpc32xx_muxes[i].signal == dma_spec->args[0])
mux = &lpc32xx_muxes[i];
- }
- if (!mux) {
dev_err(&pdev->dev, "invalid mux request number: %d\n",
dma_spec->args[0]);
return ERR_PTR(-EINVAL);
- }
- if (dma_spec->args[2] > 1) {
dev_err(&pdev->dev, "invalid dma mux value: %d\n",
dma_spec->args[1]);
return ERR_PTR(-EINVAL);
- }
- /* The of_node_put() will be done in the core for the node */
- dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0);
- if (!dma_spec->np) {
dev_err(&pdev->dev, "can't get dma master\n");
return ERR_PTR(-EINVAL);
- }
- spin_lock_irqsave(&dmamux->lock, flags);
- if (mux->busy) {
spin_unlock_irqrestore(&dmamux->lock, flags);
dev_err(dev, "dma request signal %d busy, routed to %s\n",
mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1);
of_node_put(dma_spec->np);
return ERR_PTR(-EBUSY);
- }
- mux->busy = true;
- mux->muxval = dma_spec->args[2] ? BIT(mux->bit) : 0;
- regmap_update_bits(dmamux->reg, mux->muxreg, BIT(mux->bit), mux->muxval);
- spin_unlock_irqrestore(&dmamux->lock, flags);
- dma_spec->args[2] = 0;
- dma_spec->args_count = 2;
- dev_dbg(dev, "dma request signal %d routed to %s\n",
mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1);
- return mux;
+}
+static int lpc32xx_dmamux_probe(struct platform_device *pdev) +{
- struct device_node *np = pdev->dev.of_node;
- struct lpc32xx_dmamux_data *dmamux;
- int ret;
- dmamux = devm_kzalloc(&pdev->dev, sizeof(*dmamux), GFP_KERNEL);
- if (!dmamux)
return -ENOMEM;
- dmamux->reg = syscon_node_to_regmap(np->parent);
- if (IS_ERR(dmamux->reg)) {
dev_err(&pdev->dev, "syscon lookup failed\n");
return PTR_ERR(dmamux->reg);
- }
- spin_lock_init(&dmamux->lock);
- platform_set_drvdata(pdev, dmamux);
- dmamux->dmarouter.dev = &pdev->dev;
- dmamux->dmarouter.route_free = lpc32xx_dmamux_release;
- return of_dma_router_register(np, lpc32xx_dmamux_reserve,
&dmamux->dmarouter);
+}
+static const struct of_device_id lpc32xx_dmamux_match[] = {
- { .compatible = "nxp,lpc3220-dmamux" },
- {},
+};
+static struct platform_driver lpc32xx_dmamux_driver = {
- .probe = lpc32xx_dmamux_probe,
- .driver = {
.name = "lpc32xx-dmamux",
.of_match_table = lpc32xx_dmamux_match,
- },
+};
+static int __init lpc32xx_dmamux_init(void) +{
- return platform_driver_register(&lpc32xx_dmamux_driver);
+}
+arch_initcall(lpc32xx_dmamux_init);
2.25.1
On Mon, Jun 24, 2024 at 7:39 AM Vinod Koul vkoul@kernel.org wrote:
Any reason why dmaengine parts cant be sent separately, why are they clubbed together, I dont see any obvious dependencies...
The I2S driver depends on the dmaengine parts
On 20-06-24, 19:56, Piotr Wojtaszczyk wrote:
LPC32XX connects few of its peripherals to pl08x DMA thru a multiplexer, this driver allows to route a signal request line thru the multiplexer for given peripheral.
What is the difference b/w this and lpc18xx driver, why not reuse that one?
The lpc18xx used the same dma peripheral (pl08x) but the request signal multiplexer around pl08x is completely different - there are no common parts.
Hi Piotr,
kernel test robot noticed the following build warnings:
[auto build test WARNING on vkoul-dmaengine/next] [also build test WARNING on robh/for-next linus/master v6.10-rc6] [cannot apply to broonie-sound/for-next next-20240701] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Piotr-Wojtaszczyk/dt-bindings... base: https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next patch link: https://lore.kernel.org/r/20240620175657.358273-7-piotr.wojtaszczyk%40timesy... patch subject: [Patch v4 06/10] dmaengine: Add dma router for pl08x in LPC32XX SoC config: arm-allmodconfig compiler: arm-linux-gnueabi-gcc (GCC) 13.2.0 reproduce (this is a W=1 build):
If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202407021100.XiZcQQ0k-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/dma/lpc32xx-dmamux.c: In function 'lpc32xx_dmamux_release':
drivers/dma/lpc32xx-dmamux.c:81:23: warning: unused variable 'flags' [-Wunused-variable]
81 | unsigned long flags; | ^~~~~ drivers/dma/lpc32xx-dmamux.c: In function 'lpc32xx_dmamux_probe':
drivers/dma/lpc32xx-dmamux.c:157:13: warning: unused variable 'ret' [-Wunused-variable]
157 | int ret; | ^~~
vim +/flags +81 drivers/dma/lpc32xx-dmamux.c
76 77 static void lpc32xx_dmamux_release(struct device *dev, void *route_data) 78 { 79 struct lpc32xx_dmamux_data *dmamux = dev_get_drvdata(dev); 80 struct lpc32xx_dmamux *mux = route_data;
81 unsigned long flags;
82 83 dev_dbg(dev, "releasing dma request signal %d routed to %s\n", 84 mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); 85 86 guard(spinlock)(&dmamux->lock); 87 88 mux->busy = false; 89 } 90 91 static void *lpc32xx_dmamux_reserve(struct of_phandle_args *dma_spec, 92 struct of_dma *ofdma) 93 { 94 struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 95 struct device *dev = &pdev->dev; 96 struct lpc32xx_dmamux_data *dmamux = platform_get_drvdata(pdev); 97 unsigned long flags; 98 struct lpc32xx_dmamux *mux = NULL; 99 int i; 100 101 if (dma_spec->args_count != 3) { 102 dev_err(&pdev->dev, "invalid number of dma mux args\n"); 103 return ERR_PTR(-EINVAL); 104 } 105 106 for (i = 0; i < ARRAY_SIZE(lpc32xx_muxes); i++) { 107 if (lpc32xx_muxes[i].signal == dma_spec->args[0]) 108 mux = &lpc32xx_muxes[i]; 109 } 110 if (!mux) { 111 dev_err(&pdev->dev, "invalid mux request number: %d\n", 112 dma_spec->args[0]); 113 return ERR_PTR(-EINVAL); 114 } 115 116 if (dma_spec->args[2] > 1) { 117 dev_err(&pdev->dev, "invalid dma mux value: %d\n", 118 dma_spec->args[1]); 119 return ERR_PTR(-EINVAL); 120 } 121 122 /* The of_node_put() will be done in the core for the node */ 123 dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 124 if (!dma_spec->np) { 125 dev_err(&pdev->dev, "can't get dma master\n"); 126 return ERR_PTR(-EINVAL); 127 } 128 129 spin_lock_irqsave(&dmamux->lock, flags); 130 if (mux->busy) { 131 spin_unlock_irqrestore(&dmamux->lock, flags); 132 dev_err(dev, "dma request signal %d busy, routed to %s\n", 133 mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); 134 of_node_put(dma_spec->np); 135 return ERR_PTR(-EBUSY); 136 } 137 138 mux->busy = true; 139 mux->muxval = dma_spec->args[2] ? BIT(mux->bit) : 0; 140 141 regmap_update_bits(dmamux->reg, mux->muxreg, BIT(mux->bit), mux->muxval); 142 spin_unlock_irqrestore(&dmamux->lock, flags); 143 144 dma_spec->args[2] = 0; 145 dma_spec->args_count = 2; 146 147 dev_dbg(dev, "dma request signal %d routed to %s\n", 148 mux->signal, mux->muxval ? mux->name_sel1 : mux->name_sel1); 149 150 return mux; 151 } 152 153 static int lpc32xx_dmamux_probe(struct platform_device *pdev) 154 { 155 struct device_node *np = pdev->dev.of_node; 156 struct lpc32xx_dmamux_data *dmamux;
157 int ret;
158 159 dmamux = devm_kzalloc(&pdev->dev, sizeof(*dmamux), GFP_KERNEL); 160 if (!dmamux) 161 return -ENOMEM; 162 163 dmamux->reg = syscon_node_to_regmap(np->parent); 164 if (IS_ERR(dmamux->reg)) { 165 dev_err(&pdev->dev, "syscon lookup failed\n"); 166 return PTR_ERR(dmamux->reg); 167 } 168 169 spin_lock_init(&dmamux->lock); 170 platform_set_drvdata(pdev, dmamux); 171 dmamux->dmarouter.dev = &pdev->dev; 172 dmamux->dmarouter.route_free = lpc32xx_dmamux_release; 173 174 return of_dma_router_register(np, lpc32xx_dmamux_reserve, 175 &dmamux->dmarouter); 176 } 177
With the driver for nxp,lpc3220-dmamux we can remove the pl08x platform data and let pl08x driver to create peripheral channels from the DT properties.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is new in v4
arch/arm/mach-lpc32xx/phy3250.c | 54 --------------------------------- 1 file changed, 54 deletions(-)
diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c index 66701bf43248..0c7797a0e44e 100644 --- a/arch/arm/mach-lpc32xx/phy3250.c +++ b/arch/arm/mach-lpc32xx/phy3250.c @@ -16,64 +16,10 @@ #include <asm/mach/arch.h> #include "common.h"
-static struct pl08x_channel_data pl08x_slave_channels[] = { - { - .bus_id = "nand-slc", - .min_signal = 1, /* SLC NAND Flash */ - .max_signal = 1, - .periph_buses = PL08X_AHB1, - }, - { - .bus_id = "nand-mlc", - .min_signal = 12, /* MLC NAND Flash */ - .max_signal = 12, - .periph_buses = PL08X_AHB1, - }, -}; - -static int pl08x_get_signal(const struct pl08x_channel_data *cd) -{ - return cd->min_signal; -} - -static void pl08x_put_signal(const struct pl08x_channel_data *cd, int ch) -{ -} - -static struct pl08x_platform_data pl08x_pd = { - /* Some reasonable memcpy defaults */ - .memcpy_burst_size = PL08X_BURST_SZ_256, - .memcpy_bus_width = PL08X_BUS_WIDTH_32_BITS, - .slave_channels = &pl08x_slave_channels[0], - .num_slave_channels = ARRAY_SIZE(pl08x_slave_channels), - .get_xfer_signal = pl08x_get_signal, - .put_xfer_signal = pl08x_put_signal, - .lli_buses = PL08X_AHB1, - .mem_buses = PL08X_AHB1, -}; - -static struct lpc32xx_slc_platform_data lpc32xx_slc_data = { - .dma_filter = pl08x_filter_id, -}; - -static struct lpc32xx_mlc_platform_data lpc32xx_mlc_data = { - .dma_filter = pl08x_filter_id, -}; - -static const struct of_dev_auxdata lpc32xx_auxdata_lookup[] __initconst = { - OF_DEV_AUXDATA("arm,pl080", 0x31000000, "pl08xdmac", &pl08x_pd), - OF_DEV_AUXDATA("nxp,lpc3220-slc", 0x20020000, "20020000.flash", - &lpc32xx_slc_data), - OF_DEV_AUXDATA("nxp,lpc3220-mlc", 0x200a8000, "200a8000.flash", - &lpc32xx_mlc_data), - { } -}; - static void __init lpc3250_machine_init(void) { lpc32xx_serial_init();
- of_platform_default_populate(NULL, lpc32xx_auxdata_lookup, NULL); }
static const char *const lpc32xx_dt_compat[] __initconst = {
With the driver for nxp,lpc3220-dmamux we can remove the pl08x platform data and let pl08x driver to create peripheral channels from the DT properties.
Do you see opportunities to improve such a change description? https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Docu...
Regards, Markus
On 21/06/2024 07:56, Markus Elfring wrote:
With the driver for nxp,lpc3220-dmamux we can remove the pl08x platform data and let pl08x driver to create peripheral channels from the DT properties.
Do you see opportunities to improve such a change description? https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Docu...
<form letter> Feel free to ignore all comments from Markus, regardless whether the suggestion is reasonable or not. This person is banned from LKML and several maintainers ignore Markus' feedback, because it is just a waste of time. </form letter>
Best regards, Krzysztof
Move away from pl08x platform data towards device tree.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - This patch is new in v4
drivers/mtd/nand/raw/lpc32xx_mlc.c | 10 +--------- drivers/mtd/nand/raw/lpc32xx_slc.c | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-)
diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c index 677fcb03f9be..e504e3c5af11 100644 --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c @@ -574,15 +574,7 @@ static int lpc32xx_dma_setup(struct lpc32xx_nand_host *host) struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); dma_cap_mask_t mask;
- if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, - "nand-mlc"); + host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); if (!host->dma_chan) { dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); return -EBUSY; diff --git a/drivers/mtd/nand/raw/lpc32xx_slc.c b/drivers/mtd/nand/raw/lpc32xx_slc.c index 1c5fa855b9f2..f83a31411bde 100644 --- a/drivers/mtd/nand/raw/lpc32xx_slc.c +++ b/drivers/mtd/nand/raw/lpc32xx_slc.c @@ -721,15 +721,7 @@ static int lpc32xx_nand_dma_setup(struct lpc32xx_nand_host *host) struct mtd_info *mtd = nand_to_mtd(&host->nand_chip); dma_cap_mask_t mask;
- if (!host->pdata || !host->pdata->dma_filter) { - dev_err(mtd->dev.parent, "no DMA platform data\n"); - return -ENOENT; - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - host->dma_chan = dma_request_channel(mask, host->pdata->dma_filter, - "nand-slc"); + host->dma_chan = dma_request_chan(mtd->dev.parent, "rx-tx"); if (!host->dma_chan) { dev_err(mtd->dev.parent, "Failed to request DMA channel\n"); return -EBUSY;
Hi Piotr,
piotr.wojtaszczyk@timesys.com wrote on Thu, 20 Jun 2024 19:56:39 +0200:
Move away from pl08x platform data towards device tree.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
I don't see any change regarding the NAND controller node in the device tree, is there any dependency with other patches from the same patchset or may I apply this directly to nand/next?
Thanks, Miquèl
On Fri, Jun 21, 2024 at 10:30 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Piotr,
piotr.wojtaszczyk@timesys.com wrote on Thu, 20 Jun 2024 19:56:39 +0200:
Move away from pl08x platform data towards device tree.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
I don't see any change regarding the NAND controller node in the device tree, is there any dependency with other patches from the same patchset or may I apply this directly to nand/next?
Thanks, Miquèl
Yes, this patch depends on "[v4,04/10] ARM: dts: lpc32xx: Add missing dma and i2s properties" which will be splitted into two or more separate patches per request in the comments. I'd like to keep driver changes and corresponding changes in DTS in the same patch but I've made a separate patch for DTS per request from v2 of the patch set.
-- Piotr Wojtaszczyk Timesys
Hi Piotr,
piotr.wojtaszczyk@timesys.com wrote on Fri, 21 Jun 2024 14:44:21 +0200:
On Fri, Jun 21, 2024 at 10:30 AM Miquel Raynal miquel.raynal@bootlin.com wrote:
Hi Piotr,
piotr.wojtaszczyk@timesys.com wrote on Thu, 20 Jun 2024 19:56:39 +0200:
Move away from pl08x platform data towards device tree.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
I don't see any change regarding the NAND controller node in the device tree, is there any dependency with other patches from the same patchset or may I apply this directly to nand/next?
Thanks, Miquèl
Yes, this patch depends on "[v4,04/10] ARM: dts: lpc32xx: Add missing dma and i2s properties" which will be splitted into two or more separate patches per request in the comments. I'd like to keep driver changes and corresponding changes in DTS in the same patch but I've made a separate patch for DTS per request from v2 of the patch set.
These changes won't be applied to the same tries so they must be split.
So I will not take this patch for the next merge window and instead will take it for the one after, if the DT patches have been applied. Please ping me at -rc1 again if the DT patches have gone through.
Thanks, Miquèl
This driver was ported from an old version in linux 2.6.27 and adjusted for the new ASoC framework and DMA API.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- Changes for v4: - Add to MAINTAINERS - Use guard(mutex)(&i2s_info_p->lock) when possible - Request dma chennels using DT entries in devm_snd_dmaengine_pcm_register
Changes for v3: - Split previous commit for separate subsystems - Add support and jonathan.downing@nautel.com as a maintainer for the driver - Replaced `SND_SOC` config dependency with COMPILE_TEST - Moved `snd-soc-fsl-lpc3xxx-y` in Makefile up in the list to maintain alfabedical order - Changed comment to c++ format - replaced custom absd32() with standard abs() function - Added clock provider check in lpc3xxx_i2s_set_dai_fmt() - Removed empty lpc32xx_i2s_remove() function - Reworked i2s regs definitions to include LPC3XXX prefix - Replaced custom _BIT, _SBD with standard BIT and FIELD_PREP macros
Changes for v2: - Coding Style cleanup - Use dev_err_probe() for error handling in probe function - Removed unneded err_clk_disable label - Removed empty function - Droped of_match_ptr in lpc32xx_i2s_match DT match table - ASoC struct adjustmes for the latest 6.10-rc3 kernel
MAINTAINERS | 1 + sound/soc/fsl/Kconfig | 7 + sound/soc/fsl/Makefile | 2 + sound/soc/fsl/lpc3xxx-i2s.c | 376 ++++++++++++++++++++++++++++++++++++ sound/soc/fsl/lpc3xxx-i2s.h | 79 ++++++++ sound/soc/fsl/lpc3xxx-pcm.c | 73 +++++++ 6 files changed, 538 insertions(+) create mode 100644 sound/soc/fsl/lpc3xxx-i2s.c create mode 100644 sound/soc/fsl/lpc3xxx-i2s.h create mode 100644 sound/soc/fsl/lpc3xxx-pcm.c
diff --git a/MAINTAINERS b/MAINTAINERS index 5ffe988ee282..2705f48c967d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8927,6 +8927,7 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) L: linuxppc-dev@lists.ozlabs.org S: Maintained F: Documentation/devicetree/bindings/sound/nxp,lpc3220-i2s.yaml +F: sound/soc/fsl/lpc3xxx-* N: lpc32xx
FREESCALE SOC SOUND QMC DRIVER diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 270726c134b3..72f2d4d15696 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -130,6 +130,13 @@ config SND_SOC_FSL_RPMSG This option is only useful for out-of-tree drivers since in-tree drivers select it automatically.
+config SND_SOC_FSL_LPC3XXX + tristate "SoC Audio for NXP LPC32XX CPUs" + depends on ARCH_LPC32XX || COMPILE_TEST + select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for the LPC3XXX I2S interface. + config SND_SOC_IMX_PCM_DMA tristate select SND_SOC_GENERIC_DMAENGINE_PCM diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 2fe78eed3a48..2a61e2f96438 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o snd-soc-fsl-audmix-y := fsl_audmix.o snd-soc-fsl-asoc-card-y := fsl-asoc-card.o snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o +snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o snd-soc-fsl-sai-y := fsl_sai.o snd-soc-fsl-ssi-y := fsl_ssi.o snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o @@ -29,6 +30,7 @@ snd-soc-fsl-qmc-audio-y := fsl_qmc_audio.o obj-$(CONFIG_SND_SOC_FSL_AUDMIX) += snd-soc-fsl-audmix.o obj-$(CONFIG_SND_SOC_FSL_ASOC_CARD) += snd-soc-fsl-asoc-card.o obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o +obj-$(CONFIG_SND_SOC_FSL_LPC3XXX) += snd-soc-fsl-lpc3xxx.o obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o diff --git a/sound/soc/fsl/lpc3xxx-i2s.c b/sound/soc/fsl/lpc3xxx-i2s.c new file mode 100644 index 000000000000..7fdba451c643 --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-i2s.c @@ -0,0 +1,376 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Author: Kevin Wells kevin.wells@nxp.com +// +// Copyright (C) 2008 NXP Semiconductors +// Copyright 2023 Timesys Corporation piotr.wojtaszczyk@timesys.com + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> +#include <sound/initval.h> +#include <sound/soc.h> + +#include "lpc3xxx-i2s.h" + +#define I2S_PLAYBACK_FLAG 0x1 +#define I2S_CAPTURE_FLAG 0x2 + +#define LPC3XXX_I2S_RATES ( \ + SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) + +#define LPC3XXX_I2S_FORMATS ( \ + SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +static void __lpc3xxx_find_clkdiv(u32 *clkx, u32 *clky, int freq, int xbytes, u32 clkrate) +{ + u32 i2srate; + u32 idxx, idyy; + u32 savedbitclkrate, diff, trate, baseclk; + + /* Adjust rate for sample size (bits) and 2 channels and offset for + * divider in clock output + */ + i2srate = (freq / 100) * 2 * (8 * xbytes); + i2srate = i2srate << 1; + clkrate = clkrate / 100; + baseclk = clkrate; + *clkx = 1; + *clky = 1; + + /* Find the best divider */ + *clkx = *clky = 0; + savedbitclkrate = 0; + diff = ~0; + for (idxx = 1; idxx < 0xFF; idxx++) { + for (idyy = 1; idyy < 0xFF; idyy++) { + trate = (baseclk * idxx) / idyy; + if (abs(trate - i2srate) < diff) { + diff = abs(trate - i2srate); + savedbitclkrate = trate; + *clkx = idxx; + *clky = idyy; + } + } + } +} + +static int lpc3xxx_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + u32 flag; + int ret = 0; + + guard(mutex)(&i2s_info_p->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + flag = I2S_PLAYBACK_FLAG; + else + flag = I2S_CAPTURE_FLAG; + + if (flag & i2s_info_p->streams_in_use) { + dev_warn(dev, "I2S channel is busy\n"); + ret = -EBUSY; + return ret; + } + + if (i2s_info_p->streams_in_use == 0) { + ret = clk_prepare_enable(i2s_info_p->clk); + if (ret) { + dev_err(dev, "Can't enable clock, err=%d\n", ret); + return ret; + } + } + + i2s_info_p->streams_in_use |= flag; + return 0; +} + +static void lpc3xxx_i2s_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regs = i2s_info_p->regs; + const u32 stop_bits = (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP); + u32 flag; + + guard(mutex)(&i2s_info_p->lock); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + flag = I2S_PLAYBACK_FLAG; + regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, 0); + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, stop_bits, stop_bits); + } else { + flag = I2S_CAPTURE_FLAG; + regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, 0); + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, stop_bits, stop_bits); + } + i2s_info_p->streams_in_use &= ~flag; + + if (i2s_info_p->streams_in_use == 0) + clk_disable_unprepare(i2s_info_p->clk); +} + +static int lpc3xxx_i2s_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + + /* Will use in HW params later */ + i2s_info_p->freq = freq; + + return 0; +} + +static int lpc3xxx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + + if ((fmt & SND_SOC_DAIFMT_FORMAT_MASK) != SND_SOC_DAIFMT_I2S) { + dev_warn(dev, "unsupported bus format %d\n", fmt); + return -EINVAL; + } + + if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) { + dev_warn(dev, "unsupported clock provider %d\n", fmt); + return -EINVAL; + } + + return 0; +} + +static int lpc3xxx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct device *dev = i2s_info_p->dev; + struct regmap *regs = i2s_info_p->regs; + int xfersize; + u32 tmp, clkx, clky; + + tmp = LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP; + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + tmp |= LPC3XXX_I2S_WW8 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW8_HP); + xfersize = 1; + break; + + case SNDRV_PCM_FORMAT_S16_LE: + tmp |= LPC3XXX_I2S_WW16 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW16_HP); + xfersize = 2; + break; + + case SNDRV_PCM_FORMAT_S32_LE: + tmp |= LPC3XXX_I2S_WW32 | LPC3XXX_I2S_WS_HP(LPC3XXX_I2S_WW32_HP); + xfersize = 4; + break; + + default: + dev_warn(dev, "Unsupported audio data format %d\n", params_format(params)); + return -EINVAL; + } + + if (params_channels(params) == 1) + tmp |= LPC3XXX_I2S_MONO; + + __lpc3xxx_find_clkdiv(&clkx, &clky, i2s_info_p->freq, xfersize, i2s_info_p->clkrate); + + dev_dbg(dev, "Stream : %s\n", + substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture"); + dev_dbg(dev, "Desired clock rate : %d\n", i2s_info_p->freq); + dev_dbg(dev, "Base clock rate : %d\n", i2s_info_p->clkrate); + dev_dbg(dev, "Transfer size (bytes) : %d\n", xfersize); + dev_dbg(dev, "Clock divider (x) : %d\n", clkx); + dev_dbg(dev, "Clock divider (y) : %d\n", clky); + dev_dbg(dev, "Channels : %d\n", params_channels(params)); + dev_dbg(dev, "Data format : %s\n", "I2S"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_write(regs, LPC3XXX_REG_I2S_DMA1, + LPC3XXX_I2S_DMA1_TX_EN | LPC3XXX_I2S_DMA0_TX_DEPTH(4)); + regmap_write(regs, LPC3XXX_REG_I2S_TX_RATE, (clkx << 8) | clky); + regmap_write(regs, LPC3XXX_REG_I2S_DAO, tmp); + } else { + regmap_write(regs, LPC3XXX_REG_I2S_DMA0, + LPC3XXX_I2S_DMA0_RX_EN | LPC3XXX_I2S_DMA1_RX_DEPTH(4)); + regmap_write(regs, LPC3XXX_REG_I2S_RX_RATE, (clkx << 8) | clky); + regmap_write(regs, LPC3XXX_REG_I2S_DAI, tmp); + } + + return 0; +} + +static int lpc3xxx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(cpu_dai); + struct regmap *regs = i2s_info_p->regs; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, + LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); + else + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, + LPC3XXX_I2S_STOP, LPC3XXX_I2S_STOP); + break; + + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAO, + (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); + else + regmap_update_bits(regs, LPC3XXX_REG_I2S_DAI, + (LPC3XXX_I2S_RESET | LPC3XXX_I2S_STOP), 0); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int lpc3xxx_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct lpc3xxx_i2s_info *i2s_info_p = snd_soc_dai_get_drvdata(dai); + + snd_soc_dai_init_dma_data(dai, &i2s_info_p->playback_dma_config, + &i2s_info_p->capture_dma_config); + return 0; +} + +const struct snd_soc_dai_ops lpc3xxx_i2s_dai_ops = { + .probe = lpc3xxx_i2s_dai_probe, + .startup = lpc3xxx_i2s_startup, + .shutdown = lpc3xxx_i2s_shutdown, + .trigger = lpc3xxx_i2s_trigger, + .hw_params = lpc3xxx_i2s_hw_params, + .set_sysclk = lpc3xxx_i2s_set_dai_sysclk, + .set_fmt = lpc3xxx_i2s_set_dai_fmt, +}; + +struct snd_soc_dai_driver lpc3xxx_i2s_dai_driver = { + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = LPC3XXX_I2S_RATES, + .formats = LPC3XXX_I2S_FORMATS, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = LPC3XXX_I2S_RATES, + .formats = LPC3XXX_I2S_FORMATS, + }, + .ops = &lpc3xxx_i2s_dai_ops, + .symmetric_rate = 1, + .symmetric_channels = 1, + .symmetric_sample_bits = 1, +}; + +static const struct snd_soc_component_driver lpc32xx_i2s_component = { + .name = "lpc32xx-i2s", + .legacy_dai_naming = 1, +}; + +static const struct regmap_config lpc32xx_i2s_regconfig = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = LPC3XXX_REG_I2S_RX_RATE, +}; + +static int lpc32xx_i2s_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lpc3xxx_i2s_info *i2s_info_p; + struct resource *res; + void __iomem *iomem; + const char *filter_data; + int ret; + + i2s_info_p = devm_kzalloc(dev, sizeof(*i2s_info_p), GFP_KERNEL); + if (!i2s_info_p) + return -ENOMEM; + + platform_set_drvdata(pdev, i2s_info_p); + i2s_info_p->dev = dev; + + iomem = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(iomem)) + return dev_err_probe(dev, PTR_ERR(iomem), "Can't map registers\n"); + + i2s_info_p->regs = devm_regmap_init_mmio(dev, iomem, &lpc32xx_i2s_regconfig); + if (IS_ERR(i2s_info_p->regs)) + return dev_err_probe(dev, PTR_ERR(i2s_info_p->regs), + "failed to init register map: %d\n", ret); + + i2s_info_p->clk = devm_clk_get(dev, NULL); + if (IS_ERR(i2s_info_p->clk)) + return dev_err_probe(dev, PTR_ERR(i2s_info_p->clk), "Can't get clock\n"); + + i2s_info_p->clkrate = clk_get_rate(i2s_info_p->clk); + if (i2s_info_p->clkrate == 0) + return dev_err_probe(dev, -EINVAL, "Invalid returned clock rate\n"); + + mutex_init(&i2s_info_p->lock); + + ret = devm_snd_soc_register_component(dev, &lpc32xx_i2s_component, + &lpc3xxx_i2s_dai_driver, 1); + if (ret) + return dev_err_probe(dev, ret, "Can't register cpu_dai component\n"); + + i2s_info_p->playback_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_TX_FIFO); + i2s_info_p->playback_dma_config.maxburst = 4; + + i2s_info_p->capture_dma_config.addr = (dma_addr_t)(res->start + LPC3XXX_REG_I2S_RX_FIFO); + i2s_info_p->capture_dma_config.maxburst = 4; + + ret = lpc3xxx_pcm_register(pdev); + if (ret) + return dev_err_probe(dev, ret, "Can't register pcm component\n"); + + return 0; +} + +static const struct of_device_id lpc32xx_i2s_match[] = { + { .compatible = "nxp,lpc3220-i2s" }, + {}, +}; +MODULE_DEVICE_TABLE(of, lpc32xx_i2s_match); + +static struct platform_driver lpc32xx_i2s_driver = { + .probe = lpc32xx_i2s_probe, + .driver = { + .name = "lpc3xxx-i2s", + .of_match_table = lpc32xx_i2s_match, + }, +}; + +module_platform_driver(lpc32xx_i2s_driver); + +MODULE_AUTHOR("Kevin Wells kevin.wells@nxp.com"); +MODULE_AUTHOR("Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com"); +MODULE_DESCRIPTION("ASoC LPC3XXX I2S interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/lpc3xxx-i2s.h b/sound/soc/fsl/lpc3xxx-i2s.h new file mode 100644 index 000000000000..eec755448478 --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-i2s.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Author: Kevin Wells kevin.wells@nxp.com + * + * Copyright (C) 2008 NXP Semiconductors + * Copyright 2023 Timesys Corporation piotr.wojtaszczyk@timesys.com + */ + +#ifndef __SOUND_SOC_LPC3XXX_I2S_H +#define __SOUND_SOC_LPC3XXX_I2S_H + +#include <linux/types.h> +#include <linux/regmap.h> + +struct lpc3xxx_i2s_info { + struct device *dev; + struct clk *clk; + struct mutex lock; /* To serialize user-space access */ + struct regmap *regs; + u32 streams_in_use; + u32 clkrate; + int freq; + struct snd_dmaengine_dai_dma_data playback_dma_config; + struct snd_dmaengine_dai_dma_data capture_dma_config; +}; + +int lpc3xxx_pcm_register(struct platform_device *pdev); + +/* I2S controller register offsets */ +#define LPC3XXX_REG_I2S_DAO 0x00 +#define LPC3XXX_REG_I2S_DAI 0x04 +#define LPC3XXX_REG_I2S_TX_FIFO 0x08 +#define LPC3XXX_REG_I2S_RX_FIFO 0x0C +#define LPC3XXX_REG_I2S_STAT 0x10 +#define LPC3XXX_REG_I2S_DMA0 0x14 +#define LPC3XXX_REG_I2S_DMA1 0x18 +#define LPC3XXX_REG_I2S_IRQ 0x1C +#define LPC3XXX_REG_I2S_TX_RATE 0x20 +#define LPC3XXX_REG_I2S_RX_RATE 0x24 + +/* i2s_daO i2s_dai register definitions */ +#define LPC3XXX_I2S_WW8 FIELD_PREP(0x3, 0) /* Word width is 8bit */ +#define LPC3XXX_I2S_WW16 FIELD_PREP(0x3, 1) /* Word width is 16bit */ +#define LPC3XXX_I2S_WW32 FIELD_PREP(0x3, 3) /* Word width is 32bit */ +#define LPC3XXX_I2S_MONO BIT(2) /* Mono */ +#define LPC3XXX_I2S_STOP BIT(3) /* Stop, diables the access to FIFO, mutes the channel */ +#define LPC3XXX_I2S_RESET BIT(4) /* Reset the channel */ +#define LPC3XXX_I2S_WS_SEL BIT(5) /* Channel Master(0) or slave(1) mode select */ +#define LPC3XXX_I2S_WS_HP(s) FIELD_PREP(0x7FC0, s) /* Word select half period - 1 */ +#define LPC3XXX_I2S_MUTE BIT(15) /* Mute the channel, Transmit channel only */ + +#define LPC3XXX_I2S_WW32_HP 0x1f /* Word select half period for 32bit word width */ +#define LPC3XXX_I2S_WW16_HP 0x0f /* Word select half period for 16bit word width */ +#define LPC3XXX_I2S_WW8_HP 0x7 /* Word select half period for 8bit word width */ + +/* i2s_stat register definitions */ +#define LPC3XXX_I2S_IRQ_STAT BIT(0) +#define LPC3XXX_I2S_DMA0_REQ BIT(1) +#define LPC3XXX_I2S_DMA1_REQ BIT(2) + +/* i2s_dma0 Configuration register definitions */ +#define LPC3XXX_I2S_DMA0_RX_EN BIT(0) /* Enable RX DMA1 */ +#define LPC3XXX_I2S_DMA0_TX_EN BIT(1) /* Enable TX DMA1 */ +#define LPC3XXX_I2S_DMA0_RX_DEPTH(s) FIELD_PREP(0xF00, s) /* Set the DMA1 RX Request level */ +#define LPC3XXX_I2S_DMA0_TX_DEPTH(s) FIELD_PREP(0xF0000, s) /* Set the DMA1 TX Request level */ + +/* i2s_dma1 Configuration register definitions */ +#define LPC3XXX_I2S_DMA1_RX_EN BIT(0) /* Enable RX DMA1 */ +#define LPC3XXX_I2S_DMA1_TX_EN BIT(1) /* Enable TX DMA1 */ +#define LPC3XXX_I2S_DMA1_RX_DEPTH(s) FIELD_PREP(0x700, s) /* Set the DMA1 RX Request level */ +#define LPC3XXX_I2S_DMA1_TX_DEPTH(s) FIELD_PREP(0x70000, s) /* Set the DMA1 TX Request level */ + +/* i2s_irq register definitions */ +#define LPC3XXX_I2S_RX_IRQ_EN BIT(0) /* Enable RX IRQ */ +#define LPC3XXX_I2S_TX_IRQ_EN BIT(1) /* Enable TX IRQ */ +#define LPC3XXX_I2S_IRQ_RX_DEPTH(s) FIELD_PREP(0xFF00, s) /* valid values ar 0 to 7 */ +#define LPC3XXX_I2S_IRQ_TX_DEPTH(s) FIELD_PREP(0xFF0000, s) /* valid values ar 0 to 7 */ + +#endif diff --git a/sound/soc/fsl/lpc3xxx-pcm.c b/sound/soc/fsl/lpc3xxx-pcm.c new file mode 100644 index 000000000000..80f3780d1a0c --- /dev/null +++ b/sound/soc/fsl/lpc3xxx-pcm.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +// +// Author: Kevin Wells kevin.wells@nxp.com +// +// Copyright (C) 2008 NXP Semiconductors +// Copyright 2023 Timesys Corporation piotr.wojtaszczyk@timesys.com + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/amba/pl08x.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/dmaengine_pcm.h> +#include <sound/soc.h> + +#include "lpc3xxx-i2s.h" + +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static const struct snd_pcm_hardware lpc3xxx_pcm_hardware = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME), + .formats = STUB_FORMATS, + .period_bytes_min = 128, + .period_bytes_max = 2048, + .periods_min = 2, + .periods_max = 1024, + .buffer_bytes_max = 128 * 1024 +}; + +static const struct snd_dmaengine_pcm_config lpc3xxx_dmaengine_pcm_config = { + .pcm_hardware = &lpc3xxx_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = pl08x_filter_id, + .prealloc_buffer_size = 128 * 1024, +}; + +const struct snd_soc_component_driver lpc3xxx_soc_platform_driver = { + .name = "lpc32xx-pcm", +}; + +int lpc3xxx_pcm_register(struct platform_device *pdev) +{ + int ret; + int flags; + + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, &lpc3xxx_dmaengine_pcm_config, 0); + if (ret) { + dev_err(&pdev->dev, "failed to register dmaengine: %d\n", ret); + return ret; + } + + return devm_snd_soc_register_component(&pdev->dev, &lpc3xxx_soc_platform_driver, + NULL, 0); +} +EXPORT_SYMBOL(lpc3xxx_pcm_register);
When del_timer_sync() is called in an interrupt context it throws a warning because of potential deadlock. Threaded irq handler fixes the potential problem.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com --- drivers/i2c/busses/i2c-pnx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index a12525b3186b..b2ab6fb168bf 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -718,8 +718,8 @@ static int i2c_pnx_probe(struct platform_device *pdev) ret = alg_data->irq; goto out_clock; } - ret = devm_request_irq(&pdev->dev, alg_data->irq, i2c_pnx_interrupt, - 0, pdev->name, alg_data); + ret = devm_request_threaded_irq(&pdev->dev, alg_data->irq, NULL, i2c_pnx_interrupt, + IRQF_ONESHOT, pdev->name, alg_data); if (ret) goto out_clock;
Hi Piotr,
On Thu, Jun 20, 2024 at 07:56:41PM GMT, Piotr Wojtaszczyk wrote:
When del_timer_sync() is called in an interrupt context it throws a warning because of potential deadlock. Threaded irq handler fixes the potential problem.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
did you run into a lockdep splat?
Anything against using del_timer(), instead? Have you tried?
Thanks, Andi
Hi Andi,
On Fri, Jun 21, 2024 at 12:57 AM Andi Shyti andi.shyti@kernel.org wrote:
On Thu, Jun 20, 2024 at 07:56:41PM GMT, Piotr Wojtaszczyk wrote:
When del_timer_sync() is called in an interrupt context it throws a warning because of potential deadlock. Threaded irq handler fixes the potential problem.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
did you run into a lockdep splat?
Anything against using del_timer(), instead? Have you tried?
I didn't get a lockdep splat but console was flooded with warnings from https://github.com/torvalds/linux/blob/v6.10-rc4/kernel/time/timer.c#L1655 In the linux kernel v5.15 I didn't see these warnings.
I'm not a maintainer of the driver and I didn't do any research on what kind of impact would have using del_timer() instad. Maybe Vladimir Zapolskiy will know that.
Hi Piotr,
On Fri, Jun 21, 2024 at 02:08:03PM GMT, Piotr Wojtaszczyk wrote:
On Fri, Jun 21, 2024 at 12:57 AM Andi Shyti andi.shyti@kernel.org wrote:
On Thu, Jun 20, 2024 at 07:56:41PM GMT, Piotr Wojtaszczyk wrote:
When del_timer_sync() is called in an interrupt context it throws a warning because of potential deadlock. Threaded irq handler fixes the potential problem.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
did you run into a lockdep splat?
Anything against using del_timer(), instead? Have you tried?
I didn't get a lockdep splat but console was flooded with warnings from https://github.com/torvalds/linux/blob/v6.10-rc4/kernel/time/timer.c#L1655 In the linux kernel v5.15 I didn't see these warnings.
I'm not a maintainer of the driver and I didn't do any research on what kind of impact would have using del_timer() instad. Maybe Vladimir Zapolskiy will know that.
Your patch is definitely correct, no doubt about that.
And I don't have anything aginast changing irq handlers to threaded handlers. But I would be careful at doing that depending on the use of the controller and for accepting such change I would need an ack from someone who knows the device. Vladimir, perhaps?
There are cases where using threaded handlers are not totally right, for example when the controller is used at early boot for power management handling. I don't think it's the case for this driver, but I can't be 100% sure.
If you were able to see the flood of WARN_ON's, would be interesting to know how it behaves with del_timer(). Mind giving it a test?
Thanks, Andi
On Tue, Jun 25, 2024 at 11:12 PM Andi Shyti andi.shyti@kernel.org wrote:
Hi Piotr,
On Fri, Jun 21, 2024 at 02:08:03PM GMT, Piotr Wojtaszczyk wrote:
On Fri, Jun 21, 2024 at 12:57 AM Andi Shyti andi.shyti@kernel.org wrote:
On Thu, Jun 20, 2024 at 07:56:41PM GMT, Piotr Wojtaszczyk wrote:
When del_timer_sync() is called in an interrupt context it throws a warning because of potential deadlock. Threaded irq handler fixes the potential problem.
Signed-off-by: Piotr Wojtaszczyk piotr.wojtaszczyk@timesys.com
did you run into a lockdep splat?
Anything against using del_timer(), instead? Have you tried?
I didn't get a lockdep splat but console was flooded with warnings from https://github.com/torvalds/linux/blob/v6.10-rc4/kernel/time/timer.c#L1655 In the linux kernel v5.15 I didn't see these warnings.
I'm not a maintainer of the driver and I didn't do any research on what kind of impact would have using del_timer() instad. Maybe Vladimir Zapolskiy will know that.
Your patch is definitely correct, no doubt about that.
And I don't have anything aginast changing irq handlers to threaded handlers. But I would be careful at doing that depending on the use of the controller and for accepting such change I would need an ack from someone who knows the device. Vladimir, perhaps?
There are cases where using threaded handlers are not totally right, for example when the controller is used at early boot for power management handling. I don't think it's the case for this driver, but I can't be 100% sure.
If you were able to see the flood of WARN_ON's, would be interesting to know how it behaves with del_timer(). Mind giving it a test?
Thanks, Andi
I took some time to take a closer look at this and it turns out that the timer is used only to exit from the wait_for_completion(), after each del_timer_sync() there is a complete() call. So I will remove the timer all together and replace wait_for_completion() with wait_for_completion_timeout()
participants (8)
-
Andi Shyti
-
kernel test robot
-
Krzysztof Kozlowski
-
Markus Elfring
-
Miquel Raynal
-
Piotr Wojtaszczyk
-
Rob Herring (Arm)
-
Vinod Koul