Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git

This commit is contained in:
Stephen Rothwell 2025-01-13 12:32:52 +11:00
commit d761877d8f
157 changed files with 21396 additions and 1114 deletions

View File

@ -36,12 +36,14 @@ unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@34 {
compatible = "adi,ssm2518";
reg = <0x34>;
gpios = <&gpio 5 0>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@34 {
compatible = "adi,ssm2518";
reg = <0x34>;
gpios = <&gpio 5 GPIO_ACTIVE_HIGH>;
};
};

View File

@ -23,6 +23,7 @@ properties:
- allwinner,sun8i-h3-codec
- allwinner,sun8i-v3s-codec
- allwinner,sun50i-h616-codec
- allwinner,suniv-f1c100s-codec
reg:
maxItems: 1
@ -77,6 +78,7 @@ properties:
- MIC1
- MIC2
- MIC3
- MIC
# Microphone Biases from the SoC
- HBIAS
@ -87,6 +89,8 @@ properties:
- Headset Mic
- Line In
- Line Out
- Right FM In
- Left FM In
- Mic
- Speaker
@ -270,6 +274,33 @@ allOf:
- const: rx
- const: tx
- if:
properties:
compatible:
enum:
- allwinner,suniv-f1c100s-codec
then:
properties:
allwinner,audio-routing:
items:
enum:
- HP
- HPCOM
- LINEIN
- LINEOUT
- MIC
- HBIAS
- MBIAS
- Headphone
- Headset Mic
- Line In
- Line Out
- Right FM In
- Left FM In
- Mic
- Speaker
unevaluatedProperties: false
examples:

View File

@ -18,6 +18,7 @@ properties:
compatible:
enum:
- awinic,aw88081
- awinic,aw88083
- awinic,aw88261
- awinic,aw88395
- awinic,aw88399
@ -58,6 +59,7 @@ allOf:
contains:
enum:
- awinic,aw88081
- awinic,aw88083
- awinic,aw88261
then:
properties:

View File

@ -53,10 +53,10 @@ unevaluatedProperties: false
examples:
- |
codec {
compatible = "everest,es7134";
#sound-dai-cells = <0>;
VDD-supply = <&vdd_supply>;
};
codec {
compatible = "everest,es7134";
#sound-dai-cells = <0>;
VDD-supply = <&vdd_supply>;
};
...

View File

@ -54,14 +54,15 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
codec {
compatible = "everest,es7241";
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
VDDP-supply = <&vddp_supply>;
VDDA-supply = <&vdda_supply>;
VDDD-supply = <&vddd_supply>;
};
#include <dt-bindings/gpio/gpio.h>
codec {
compatible = "everest,es7241";
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
VDDP-supply = <&vddp_supply>;
VDDA-supply = <&vdda_supply>;
VDDD-supply = <&vddd_supply>;
};
...

View File

@ -87,20 +87,20 @@ examples:
#include <dt-bindings/clock/imx8mn-clock.h>
easrc: easrc@300c0000 {
compatible = "fsl,imx8mn-easrc";
reg = <0x300c0000 0x10000>;
interrupts = <0x0 122 0x4>;
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
clock-names = "mem";
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
dma-names = "ctx0_rx", "ctx0_tx",
"ctx1_rx", "ctx1_tx",
"ctx2_rx", "ctx2_tx",
"ctx3_rx", "ctx3_tx";
firmware-name = "imx/easrc/easrc-imx8mn.bin";
fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
compatible = "fsl,imx8mn-easrc";
reg = <0x300c0000 0x10000>;
interrupts = <0x0 122 0x4>;
clocks = <&clk IMX8MN_CLK_ASRC_ROOT>;
clock-names = "mem";
dmas = <&sdma2 16 23 0> , <&sdma2 17 23 0>,
<&sdma2 18 23 0> , <&sdma2 19 23 0>,
<&sdma2 20 23 0> , <&sdma2 21 23 0>,
<&sdma2 22 23 0> , <&sdma2 23 23 0>;
dma-names = "ctx0_rx", "ctx0_tx",
"ctx1_rx", "ctx1_tx",
"ctx2_rx", "ctx2_tx",
"ctx3_rx", "ctx3_tx";
firmware-name = "imx/easrc/easrc-imx8mn.bin";
fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
};

View File

@ -140,21 +140,21 @@ examples:
#include <dt-bindings/reset/imx8mp-reset.h>
xcvr: xcvr@30cc0000 {
compatible = "fsl,imx8mp-xcvr";
reg = <0x30cc0000 0x800>,
<0x30cc0800 0x400>,
<0x30cc0c00 0x080>,
<0x30cc0e00 0x080>;
reg-names = "ram", "regs", "rxfifo", "txfifo";
interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_IPG>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_PHY>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT>;
clock-names = "ipg", "phy", "spba", "pll_ipg";
dmas = <&sdma2 30 2 0>, <&sdma2 31 2 0>;
dma-names = "rx", "tx";
resets = <&audiomix_reset 0>;
compatible = "fsl,imx8mp-xcvr";
reg = <0x30cc0000 0x800>,
<0x30cc0800 0x400>,
<0x30cc0c00 0x080>,
<0x30cc0e00 0x080>;
reg-names = "ram", "regs", "rxfifo", "txfifo";
interrupts = <GIC_SPI 128 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 129 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 146 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_IPG>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_EARC_PHY>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_SPBA2_ROOT>,
<&audiomix_clk IMX8MP_CLK_AUDIOMIX_AUDPLL_ROOT>;
clock-names = "ipg", "phy", "spba", "pll_ipg";
dmas = <&sdma2 30 2 0>, <&sdma2 31 2 0>;
dma-names = "rx", "tx";
resets = <&audiomix_reset 0>;
};

View File

@ -72,19 +72,19 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#define KEEM_BAY_PSS_AUX_I2S3
#define KEEM_BAY_PSS_I2S3
i2s3: i2s@20140000 {
compatible = "intel,keembay-i2s";
#sound-dai-cells = <0>;
reg = <0x20140000 0x200>, /* I2S registers */
<0x202a00a4 0x4>; /* I2S gen configuration */
reg-names = "i2s-regs", "i2s_gen_cfg";
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "osc", "apb_clk";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_I2S3>, <&scmi_clk KEEM_BAY_PSS_I2S3>;
dmas = <&axi_dma0 29>, <&axi_dma0 33>;
dma-names = "tx", "rx";
};
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
#define KEEM_BAY_PSS_AUX_I2S3
#define KEEM_BAY_PSS_I2S3
i2s@20140000 {
compatible = "intel,keembay-i2s";
#sound-dai-cells = <0>;
reg = <0x20140000 0x200>, /* I2S registers */
<0x202a00a4 0x4>; /* I2S gen configuration */
reg-names = "i2s-regs", "i2s_gen_cfg";
interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>;
clock-names = "osc", "apb_clk";
clocks = <&scmi_clk KEEM_BAY_PSS_AUX_I2S3>, <&scmi_clk KEEM_BAY_PSS_I2S3>;
dmas = <&axi_dma0 29>, <&axi_dma0 33>;
dma-names = "tx", "rx";
};

View File

@ -14,11 +14,15 @@ allOf:
properties:
compatible:
enum:
- mediatek,mt8188-es8326
- mediatek,mt8188-mt6359-evb
- mediatek,mt8188-nau8825
- mediatek,mt8188-rt5682s
oneOf:
- enum:
- mediatek,mt8188-es8326
- mediatek,mt8188-mt6359-evb
- mediatek,mt8188-nau8825
- mediatek,mt8188-rt5682s
- items:
- const: mediatek,mt8390-mt6359-evk
- const: mediatek,mt8188-mt6359-evb
audio-routing:
description:
@ -56,6 +60,8 @@ patternProperties:
- ETDM2_OUT_BE
- ETDM3_OUT_BE
- PCM1_BE
- DL_SRC_BE
- UL_SRC_BE
codec:
description: Holds subnode which indicates codec dai.

View File

@ -55,16 +55,18 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@2a {
compatible = "neofidelity,ntp8918";
#sound-dai-cells = <0>;
reg = <0x2a>;
clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>;
clock-names = "wck", "scl", "bck";
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
audio-codec@2a {
compatible = "neofidelity,ntp8918";
#sound-dai-cells = <0>;
reg = <0x2a>;
clocks = <&clkc 150>, <&clkc 151>, <&clkc 152>;
clock-names = "wck", "scl", "bck";
reset-gpios = <&gpio 5 GPIO_ACTIVE_LOW>;
};
};
};

View File

@ -0,0 +1,156 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/realtek,rt5682.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Realtek rt5682 and rt5682i codecs
maintainers:
- Bard Liao <bardliao@realtek.com>
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- realtek,rt5682
- realtek,rt5682i
reg:
maxItems: 1
description: I2C address of the device.
interrupts:
maxItems: 1
description: The CODEC's interrupt output.
realtek,dmic1-data-pin:
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # dmic1 data is not used
- 1 # using GPIO2 pin as dmic1 data pin
- 2 # using GPIO5 pin as dmic1 data pin
description:
Specify which GPIO pin be used as DMIC1 data pin.
realtek,dmic1-clk-pin:
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # using GPIO1 pin as dmic1 clock pin
- 1 # using GPIO3 pin as dmic1 clock pin
description:
Specify which GPIO pin be used as DMIC1 clk pin.
realtek,jd-src:
$ref: /schemas/types.yaml#/definitions/uint32
enum:
- 0 # No JD is used
- 1 # using JD1 as JD source
description:
Specify which JD source be used.
realtek,ldo1-en-gpios:
description:
The GPIO that controls the CODEC's LDO1_EN pin.
realtek,btndet-delay:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The debounce delay for push button.
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
If absent, the default is 16.
realtek,dmic-clk-rate-hz:
description:
Set the clock rate (hz) for the requirement of the particular DMIC.
realtek,dmic-delay-ms:
description:
Set the delay time (ms) for the requirement of the particular DMIC.
realtek,dmic-clk-driving-high:
type: boolean
description:
Set the high driving of the DMIC clock out.
clocks:
items:
- description: phandle and clock specifier for codec MCLK.
clock-names:
items:
- const: mclk
"#clock-cells":
const: 1
clock-output-names:
minItems: 2
maxItems: 2
description: Name given for DAI word clock and bit clock outputs.
"#sound-dai-cells":
const: 1
AVDD-supply:
description: Regulator supplying analog power through the AVDD pin.
MICVDD-supply:
description: Regulator supplying power for the microphone bias through
the MICVDD pin.
VBAT-supply:
description: Regulator supplying battery power through the VBAT pin.
DBVDD-supply:
description: Regulator supplying I/O power through the DBVDD pin.
LDO1-IN-supply:
description: Regulator supplying power to the digital core and charge
pump through the LDO1_IN pin.
required:
- compatible
- reg
- AVDD-supply
- VBAT-supply
- MICVDD-supply
- DBVDD-supply
- LDO1-IN-supply
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@1a {
compatible = "realtek,rt5682";
reg = <0x1a>;
interrupts = <6 IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio 2 GPIO_ACTIVE_HIGH>;
realtek,dmic1-data-pin = <1>;
realtek,dmic1-clk-pin = <1>;
realtek,jd-src = <1>;
#clock-cells = <1>;
clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk";
clocks = <&osc>;
clock-names = "mclk";
AVDD-supply = <&avdd_reg>;
VBAT-supply = <&vbat_reg>;
MICVDD-supply = <&micvdd_reg>;
DBVDD-supply = <&dbvdd_reg>;
LDO1-IN-supply = <&ldo1_in_reg>;
};
};

View File

@ -112,12 +112,6 @@ properties:
description: List of necessary clock names.
# details are defined below
post-init-providers:
description: At least if rsnd is using DPCM connection on Audio-Graph-Card2,
fw_devlink might doesn't have enough information to break the cycle. rsnd
driver will not be probed in such case. Same problem might occur with
Multi-CPU/Codec or Codec2Codec.
# ports is below
port:
$ref: audio-graph-port.yaml#/definitions/port-base

View File

@ -19,6 +19,7 @@ properties:
- renesas,r9a07g043-ssi # RZ/G2UL and RZ/Five
- renesas,r9a07g044-ssi # RZ/G2{L,LC}
- renesas,r9a07g054-ssi # RZ/V2L
- renesas,r9a08g045-ssi # RZ/G3S
- const: renesas,rz-ssi
reg:
@ -57,24 +58,6 @@ properties:
dmas:
minItems: 1
maxItems: 2
description:
The first cell represents a phandle to dmac.
The second cell specifies the encoded MID/RID values of the SSI port
connected to the DMA client and the slave channel configuration
parameters.
bits[0:9] - Specifies MID/RID value of a SSI channel as below
MID/RID value of SSI rx0 = 0x256
MID/RID value of SSI tx0 = 0x255
MID/RID value of SSI rx1 = 0x25a
MID/RID value of SSI tx1 = 0x259
MID/RID value of SSI rt2 = 0x25f
MID/RID value of SSI rx3 = 0x262
MID/RID value of SSI tx3 = 0x261
bit[10] - HIEN = 1, Detects a request in response to the rising edge
of the signal
bit[11] - LVL = 0, Detects based on the edge
bits[12:14] - AM = 2, Bus cycle mode
bit[15] - TM = 0, Single transfer mode
dma-names:
oneOf:

View File

@ -1,98 +0,0 @@
RT5682 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "realtek,rt5682" or "realtek,rt5682i"
- reg : The I2C address of the device.
- AVDD-supply: phandle to the regulator supplying analog power through the
AVDD pin
- MICVDD-supply: phandle to the regulator supplying power for the microphone
bias through the MICVDD pin. Either MICVDD or VBAT should be present.
- VBAT-supply: phandle to the regulator supplying battery power through the
VBAT pin. Either MICVDD or VBAT should be present.
- DBVDD-supply: phandle to the regulator supplying I/O power through the DBVDD
pin.
- LDO1-IN-supply: phandle to the regulator supplying power to the digital core
and charge pump through the LDO1_IN pin.
Optional properties:
- interrupts : The CODEC's interrupt output.
- realtek,dmic1-data-pin
0: dmic1 is not used
1: using GPIO2 pin as dmic1 data pin
2: using GPIO5 pin as dmic1 data pin
- realtek,dmic1-clk-pin
0: using GPIO1 pin as dmic1 clock pin
1: using GPIO3 pin as dmic1 clock pin
- realtek,jd-src
0: No JD is used
1: using JD1 as JD source
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
- realtek,btndet-delay
The debounce delay for push button.
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
If absent, the default is 16.
- #clock-cells : Should be set to '<1>', wclk and bclk sources provided.
- clock-output-names : Name given for DAI clocks output.
- clocks : phandle and clock specifier for codec MCLK.
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
- realtek,dmic-clk-rate-hz : Set the clock rate (hz) for the requirement of
the particular DMIC.
- realtek,dmic-delay-ms : Set the delay time (ms) for the requirement of
the particular DMIC.
- realtek,dmic-clk-driving-high : Set the high driving of the DMIC clock out.
- #sound-dai-cells: Should be set to '<1>'.
Pins on the device (for linking into audio routes) for RT5682:
* DMIC L1
* DMIC R1
* IN1P
* HPOL
* HPOR
Example:
rt5682 {
compatible = "realtek,rt5682i";
reg = <0x1a>;
interrupt-parent = <&gpio>;
interrupts = <TEGRA_GPIO(U, 6) IRQ_TYPE_LEVEL_HIGH>;
realtek,ldo1-en-gpios =
<&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
realtek,dmic1-data-pin = <1>;
realtek,dmic1-clk-pin = <1>;
realtek,jd-src = <1>;
realtek,btndet-delay = <16>;
#clock-cells = <1>;
clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk";
clocks = <&osc>;
clock-names = "mclk";
AVDD-supply = <&avdd_reg>;
MICVDD-supply = <&micvdd_reg>;
DBVDD-supply = <&dbvdd_reg>;
LDO1-IN-supply = <&ldo1_in_reg>;
};

View File

@ -159,19 +159,21 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
/* example for two devices with interrupt support */
#address-cells = <1>;
#size-cells = <0>;
pcm6240: audio-codec@48 {
compatible = "ti,pcm6240";
reg = <0x48>, /* primary-device */
<0x4b>; /* secondary-device */
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
/* example for two devices with interrupt support */
#address-cells = <1>;
#size-cells = <0>;
audio-codec@48 {
compatible = "ti,pcm6240";
reg = <0x48>, /* primary-device */
<0x4b>; /* secondary-device */
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
...

View File

@ -65,17 +65,19 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@4c {
compatible = "ti,tas2562";
reg = <0x4c>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
shutdown-gpios = <&gpio1 15 0>;
ti,imon-slot-no = <0>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@4c {
compatible = "ti,tas2562";
reg = <0x4c>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
shutdown-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
ti,imon-slot-no = <0>;
};
};

View File

@ -69,19 +69,21 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@41 {
compatible = "ti,tas2770";
reg = <0x41>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpio = <&gpio1 15 0>;
shutdown-gpios = <&gpio1 14 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@41 {
compatible = "ti,tas2770";
reg = <0x41>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpio = <&gpio1 15 GPIO_ACTIVE_HIGH>;
shutdown-gpios = <&gpio1 14 GPIO_ACTIVE_HIGH>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};

View File

@ -101,22 +101,24 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
/* example with quad tas2781s, such as tablet or pad device */
#address-cells = <1>;
#size-cells = <0>;
quad_tas2781: tas2781@38 {
compatible = "ti,tas2781";
reg = <0x38>, /* Audio slot 0 */
<0x3a>, /* Audio slot 1 */
<0x39>, /* Audio slot 2 */
<0x3b>; /* Audio slot 3 */
#include <dt-bindings/gpio/gpio.h>
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
i2c {
/* example with quad tas2781s, such as tablet or pad device */
#address-cells = <1>;
#size-cells = <0>;
audio-codec@38 {
compatible = "ti,tas2781";
reg = <0x38>, /* Audio slot 0 */
<0x3a>, /* Audio slot 1 */
<0x39>, /* Audio slot 2 */
<0x3b>; /* Audio slot 3 */
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
interrupt-parent = <&gpio1>;
interrupts = <15>;
};
};
...

View File

@ -62,21 +62,23 @@ unevaluatedProperties: false
examples:
- |
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec: codec@38 {
compatible = "ti,tas2764";
reg = <0x38>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpios = <&gpio1 15 0>;
shutdown-gpios = <&gpio1 15 0>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
#include <dt-bindings/gpio/gpio.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@38 {
compatible = "ti,tas2764";
reg = <0x38>;
#sound-dai-cells = <0>;
interrupt-parent = <&gpio1>;
interrupts = <14>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
shutdown-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
ti,imon-slot-no = <0>;
ti,vmon-slot-no = <2>;
};
};
...

View File

@ -112,22 +112,24 @@ unevaluatedProperties: false
examples:
- |
i2c {
#address-cells = <1>;
#size-cells = <0>;
#include <dt-bindings/gpio/gpio.h>
codec@2a {
compatible = "ti,tas5717";
reg = <0x2a>;
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 0>;
pdn-gpios = <&gpio1 15 0>;
AVDD-supply = <&avdd_supply>;
DVDD-supply = <&dvdd_supply>;
HPVDD-supply = <&hpvdd_supply>;
PVDD_AB-supply = <&pvdd_ab_supply>;
PVDD_CD-supply = <&pvdd_cd_supply>;
};
};
i2c {
#address-cells = <1>;
#size-cells = <0>;
codec@2a {
compatible = "ti,tas5717";
reg = <0x2a>;
#sound-dai-cells = <0>;
reset-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
pdn-gpios = <&gpio1 15 GPIO_ACTIVE_HIGH>;
AVDD-supply = <&avdd_supply>;
DVDD-supply = <&dvdd_supply>;
HPVDD-supply = <&hpvdd_supply>;
PVDD_AB-supply = <&pvdd_ab_supply>;
PVDD_CD-supply = <&pvdd_cd_supply>;
};
};
...

View File

@ -5528,8 +5528,8 @@ L: patches@opensource.cirrus.com
S: Supported
W: https://github.com/CirrusLogic/linux-drivers/wiki
T: git https://github.com/CirrusLogic/linux-drivers.git
F: drivers/firmware/cirrus/*
F: include/linux/firmware/cirrus/*
F: drivers/firmware/cirrus/
F: include/linux/firmware/cirrus/
CIRRUS LOGIC EP93XX ETHERNET DRIVER
M: Hartley Sweeten <hsweeten@visionengravers.com>

View File

@ -3,3 +3,23 @@
config FW_CS_DSP
tristate
default n
config FW_CS_DSP_KUNIT_TEST_UTILS
tristate
depends on KUNIT
select REGMAP
select FW_CS_DSP
config FW_CS_DSP_KUNIT_TEST
tristate "KUnit tests for Cirrus Logic cs_dsp" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
select REGMAP
select FW_CS_DSP
select FW_CS_DSP_KUNIT_TEST_UTILS
help
This builds KUnit tests for cs_dsp.
For more information on KUnit and unit tests in general,
please refer to the KUnit documentation in
Documentation/dev-tools/kunit/.
If in doubt, say "N".

View File

@ -1,3 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
#
obj-$(CONFIG_FW_CS_DSP) += cs_dsp.o
obj-y += test/

View File

@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-2.0
#
cs_dsp_test_utils-objs := \
cs_dsp_mock_mem_maps.o \
cs_dsp_mock_bin.o \
cs_dsp_mock_regmap.o \
cs_dsp_mock_utils.o \
cs_dsp_mock_wmfw.o
cs_dsp_test-objs := \
cs_dsp_test_bin.o \
cs_dsp_test_bin_error.o \
cs_dsp_test_callbacks.o \
cs_dsp_test_control_parse.o \
cs_dsp_test_control_cache.o \
cs_dsp_test_control_rw.o \
cs_dsp_test_wmfw.o \
cs_dsp_test_wmfw_error.o \
cs_dsp_tests.o
obj-$(CONFIG_FW_CS_DSP_KUNIT_TEST_UTILS) += cs_dsp_test_utils.o
obj-$(CONFIG_FW_CS_DSP_KUNIT_TEST) += cs_dsp_test.o

View File

@ -0,0 +1,199 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// bin file builder for cs_dsp KUnit tests.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <kunit/resource.h>
#include <kunit/test.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/firmware.h>
#include <linux/math.h>
#include <linux/overflow.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
/* Buffer large enough for bin file content */
#define CS_DSP_MOCK_BIN_BUF_SIZE 32768
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
struct cs_dsp_mock_bin_builder {
struct cs_dsp_test *test_priv;
void *buf;
void *write_p;
size_t bytes_used;
};
/**
* cs_dsp_mock_bin_get_firmware() - Get struct firmware wrapper for data.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
*
* Return: Pointer to a struct firmware wrapper for the data.
*/
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder)
{
struct firmware *fw;
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
fw->data = builder->buf;
fw->size = builder->bytes_used;
return fw;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_bin_add_raw_block() - Add a data block to the bin file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @alg_id: Algorithm ID.
* @alg_ver: Algorithm version.
* @type: Type of the block.
* @offset: Offset.
* @payload_data: Pointer to buffer containing the payload data.
* @payload_len_bytes: Length of payload data in bytes.
*/
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
int type, unsigned int offset,
const void *payload_data, size_t payload_len_bytes)
{
struct wmfw_coeff_item *item;
size_t bytes_needed = struct_size_t(struct wmfw_coeff_item, data, payload_len_bytes);
KUNIT_ASSERT_TRUE(builder->test_priv->test,
(builder->write_p + bytes_needed) <
(builder->buf + CS_DSP_MOCK_BIN_BUF_SIZE));
item = builder->write_p;
item->offset = cpu_to_le16(offset);
item->type = cpu_to_le16(type);
item->id = cpu_to_le32(alg_id);
item->ver = cpu_to_le32(alg_ver << 8);
item->len = cpu_to_le32(payload_len_bytes);
if (payload_len_bytes)
memcpy(item->data, payload_data, payload_len_bytes);
builder->write_p += bytes_needed;
builder->bytes_used += bytes_needed;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *builder,
const char *info, int type)
{
size_t info_len = strlen(info);
char *tmp = NULL;
if (info_len % 4) {
/* Create a padded string with length a multiple of 4 */
info_len = round_up(info_len, 4);
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
memcpy(tmp, info, info_len);
info = tmp;
}
cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len);
kunit_kfree(builder->test_priv->test, tmp);
}
/**
* cs_dsp_mock_bin_add_info() - Add an info block to the bin file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @info: Pointer to info string to be copied into the file.
*
* The string will be padded to a length that is a multiple of 4 bytes.
*/
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
const char *info)
{
cs_dsp_mock_bin_add_name_or_info(builder, info, WMFW_INFO_TEXT);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_bin_add_name() - Add a name block to the bin file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @name: Pointer to name string to be copied into the file.
*/
void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
const char *name)
{
cs_dsp_mock_bin_add_name_or_info(builder, name, WMFW_NAME_TEXT);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_name, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_bin_add_patch() - Add a patch data block to the bin file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @alg_id: Algorithm ID for the patch.
* @alg_ver: Algorithm version for the patch.
* @mem_region: Memory region for the patch.
* @reg_addr_offset: Offset to start of data in register addresses.
* @payload_data: Pointer to buffer containing the payload data.
* @payload_len_bytes: Length of payload data in bytes.
*/
void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
int mem_region, unsigned int reg_addr_offset,
const void *payload_data, size_t payload_len_bytes)
{
/* Payload length must be a multiple of 4 */
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
mem_region, reg_addr_offset,
payload_data, payload_len_bytes);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder.
*
* @priv: Pointer to struct cs_dsp_test.
* @format_version: Required bin format version.
* @fw_version: Firmware version to put in bin file.
*
* Return: Pointer to created struct cs_dsp_mock_bin_builder.
*/
struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
int format_version,
unsigned int fw_version)
{
struct cs_dsp_mock_bin_builder *builder;
struct wmfw_coeff_hdr *hdr;
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
builder->test_priv = priv;
builder->buf = vmalloc(CS_DSP_MOCK_BIN_BUF_SIZE);
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
/* Create header */
hdr = builder->buf;
memcpy(hdr->magic, "WMDR", sizeof(hdr->magic));
hdr->len = cpu_to_le32(offsetof(struct wmfw_coeff_hdr, data));
hdr->ver = cpu_to_le32(fw_version | (format_version << 24));
hdr->core_ver = cpu_to_le32(((u32)priv->dsp->type << 24) | priv->dsp->rev);
builder->write_p = hdr->data;
builder->bytes_used = offsetof(struct wmfw_coeff_hdr, data);
return builder;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_init, "FW_CS_DSP_KUNIT_TEST_UTILS");

View File

@ -0,0 +1,752 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Mock DSP memory maps for cs_dsp KUnit tests.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <kunit/test.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/math.h>
const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[] = {
{ .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
{ .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
{ .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
{ .type = WMFW_ADSP2_XM, .base = 0x2800000 },
{ .type = WMFW_ADSP2_YM, .base = 0x3400000 },
};
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
/* List of sizes in bytes, for each entry above */
const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = {
0x5000, /* PM_PACKED */
0x6000, /* XM_PACKED */
0x47F4, /* YM_PACKED */
0x8000, /* XM_UNPACKED_24 */
0x5FF8, /* YM_UNPACKED_24 */
0 /* terminator */
};
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
};
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
/* List of sizes in bytes, for each entry above */
const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[] = {
0x9000, /* PM */
0xa000, /* ZM */
0x2000, /* XM */
0x2000, /* YM */
0 /* terminator */
};
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
{ .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
};
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
/* List of sizes in bytes, for each entry above */
const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[] = {
0x6000, /* PM */
0x800, /* ZM */
0x800, /* XM */
0x800, /* YM */
0 /* terminator */
};
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
int cs_dsp_mock_count_regions(const unsigned int *region_sizes)
{
int i;
for (i = 0; region_sizes[i]; ++i)
;
return i;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_count_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_size_of_region() - Return size of given memory region.
*
* @dsp: Pointer to struct cs_dsp.
* @mem_type: Memory region type.
*
* Return: Size of region in bytes.
*/
unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type)
{
const unsigned int *sizes;
int i;
if (dsp->mem == cs_dsp_mock_halo_dsp1_regions)
sizes = cs_dsp_mock_halo_dsp1_region_sizes;
else if (dsp->mem == cs_dsp_mock_adsp2_32bit_dsp1_regions)
sizes = cs_dsp_mock_adsp2_32bit_dsp1_region_sizes;
else if (dsp->mem == cs_dsp_mock_adsp2_16bit_dsp1_regions)
sizes = cs_dsp_mock_adsp2_16bit_dsp1_region_sizes;
else
return 0;
for (i = 0; i < dsp->num_mems; ++i) {
if (dsp->mem[i].type == mem_type)
return sizes[i];
}
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_size_of_region, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_base_addr_for_mem() - Base register address for memory region.
*
* @priv: Pointer to struct cs_dsp_test.
* @mem_type: Memory region type.
*
* Return: Base register address of region.
*/
unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type)
{
int num_mems = priv->dsp->num_mems;
const struct cs_dsp_region *region = priv->dsp->mem;
int i;
for (i = 0; i < num_mems; ++i) {
if (region[i].type == mem_type)
return region[i].base;
}
KUNIT_FAIL(priv->test, "Unexpected region %d\n", mem_type);
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_base_addr_for_mem, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_reg_addr_inc_per_unpacked_word() - Unpacked register address increment per DSP word.
*
* @priv: Pointer to struct cs_dsp_test.
*
* Return: Amount by which register address increments to move to the next
* DSP word in unpacked XM/YM/ZM.
*/
unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv)
{
switch (priv->dsp->type) {
case WMFW_ADSP2:
return 2; /* two 16-bit register indexes per XM/YM/ZM word */
case WMFW_HALO:
return 4; /* one byte-addressed 32-bit register per XM/YM/ZM word */
default:
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
return -1;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_addr_inc_per_unpacked_word, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_reg_block_length_bytes() - Number of bytes in an access block.
*
* @priv: Pointer to struct cs_dsp_test.
* @mem_type: Memory region type.
*
* Return: Total number of bytes in a group of registers forming the
* smallest bus access size (including any padding bits). For unpacked
* memory this is the number of registers containing one DSP word.
* For packed memory this is the number of registers in one packed
* access block.
*/
unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type)
{
switch (priv->dsp->type) {
case WMFW_ADSP2:
switch (mem_type) {
case WMFW_ADSP2_PM:
return 3 * regmap_get_val_bytes(priv->dsp->regmap);
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
case WMFW_ADSP2_ZM:
return sizeof(u32);
default:
break;
}
break;
case WMFW_HALO:
switch (mem_type) {
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
return sizeof(u32);
case WMFW_HALO_PM_PACKED:
return 5 * sizeof(u32);
case WMFW_HALO_XM_PACKED:
case WMFW_HALO_YM_PACKED:
return 3 * sizeof(u32);
default:
break;
}
break;
default:
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
return 0;
}
KUNIT_FAIL(priv->test, "Unexpected mem type\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_reg_block_length_registers() - Number of registers in an access block.
*
* @priv: Pointer to struct cs_dsp_test.
* @mem_type: Memory region type.
*
* Return: Total number of register forming the smallest bus access size.
* For unpacked memory this is the number of registers containing one
* DSP word. For packed memory this is the number of registers in one
* packed access block.
*/
unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type)
{
return cs_dsp_mock_reg_block_length_bytes(priv, mem_type) /
regmap_get_val_bytes(priv->dsp->regmap);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_registers, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_reg_block_length_dsp_words() - Number of dsp_words in an access block.
*
* @priv: Pointer to struct cs_dsp_test.
* @mem_type: Memory region type.
*
* Return: Total number of DSP words in a group of registers forming the
* smallest bus access size.
*/
unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type)
{
switch (priv->dsp->type) {
case WMFW_ADSP2:
switch (mem_type) {
case WMFW_ADSP2_PM:
return regmap_get_val_bytes(priv->dsp->regmap) / 2;
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
case WMFW_ADSP2_ZM:
return 1;
default:
break;
}
break;
case WMFW_HALO:
switch (mem_type) {
case WMFW_ADSP2_XM:
case WMFW_ADSP2_YM:
return 1;
case WMFW_HALO_PM_PACKED:
case WMFW_HALO_XM_PACKED:
case WMFW_HALO_YM_PACKED:
return 4;
default:
break;
}
break;
default:
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
return 0;
}
KUNIT_FAIL(priv->test, "Unexpected mem type\n");
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_dsp_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_has_zm() - DSP has ZM
*
* @priv: Pointer to struct cs_dsp_test.
*
* Return: True if DSP has ZM.
*/
bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv)
{
switch (priv->dsp->type) {
case WMFW_ADSP2:
return true;
default:
return false;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_has_zm, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_packed_to_unpacked_mem_type() - Unpacked region that is
* the same memory as a packed region.
*
* @packed_mem_type: Type of packed memory region.
*
* Return: unpacked type that is the same memory as packed_mem_type.
*/
int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type)
{
switch (packed_mem_type) {
case WMFW_HALO_XM_PACKED:
return WMFW_ADSP2_XM;
case WMFW_HALO_YM_PACKED:
return WMFW_ADSP2_YM;
default:
return -1;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_packed_to_unpacked_mem_type, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_num_dsp_words_to_num_packed_regs() - Number of DSP words
* to number of packed registers.
*
* @num_dsp_words: Number of DSP words.
*
* Convert number of DSP words to number of packed registers rounded
* down to the nearest register.
*
* Return: Number of packed registers.
*/
unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words)
{
/* There are 3 registers for every 4 packed words */
return (num_dsp_words * 3) / 4;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_num_dsp_words_to_num_packed_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
static const struct wmfw_halo_id_hdr cs_dsp_mock_halo_xm_hdr = {
.fw = {
.core_id = cpu_to_be32(WMFW_HALO << 16),
.block_rev = cpu_to_be32(3 << 16),
.vendor_id = cpu_to_be32(0x2),
.id = cpu_to_be32(0xabcdef),
.ver = cpu_to_be32(0x090101),
},
/*
* Leave enough space for this header and 40 algorithm descriptors.
* base and size are counted in DSP words.
*/
.xm_base = cpu_to_be32(((sizeof(struct wmfw_halo_id_hdr) +
(40 * sizeof(struct wmfw_halo_alg_hdr)))
/ 4) * 3),
.xm_size = cpu_to_be32(0x20),
/* Allocate a dummy word of YM */
.ym_base = cpu_to_be32(0),
.ym_size = cpu_to_be32(1),
.n_algs = 0,
};
static const struct wmfw_adsp2_id_hdr cs_dsp_mock_adsp2_xm_hdr = {
.fw = {
.core_id = cpu_to_be32(WMFW_ADSP2 << 16),
.core_rev = cpu_to_be32(2 << 16),
.id = cpu_to_be32(0xabcdef),
.ver = cpu_to_be32(0x090101),
},
/*
* Leave enough space for this header and 40 algorithm descriptors.
* base and size are counted in DSP words.
*/
.xm = cpu_to_be32(((sizeof(struct wmfw_adsp2_id_hdr) +
(40 * sizeof(struct wmfw_adsp2_alg_hdr)))
/ 4) * 3),
.ym = cpu_to_be32(0),
.zm = cpu_to_be32(0),
.n_algs = 0,
};
/**
* cs_dsp_mock_xm_header_get_alg_base_in_words() - Algorithm base offset in DSP words.
*
* @priv: Pointer to struct cs_dsp_test.
* @alg_id: Algorithm ID.
* @mem_type: Memory region type.
*
* Lookup an algorithm in the XM header and return the base offset in
* DSP words of the algorithm data in the requested memory region.
*
* Return: Offset in DSP words.
*/
unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
unsigned int alg_id,
int mem_type)
{
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
union {
struct wmfw_adsp2_alg_hdr adsp2;
struct wmfw_halo_alg_hdr halo;
} alg;
unsigned int alg_hdr_addr;
unsigned int val, xm_base = 0, ym_base = 0, zm_base = 0;
int ret;
switch (priv->dsp->type) {
case WMFW_ADSP2:
alg_hdr_addr = xm + (sizeof(struct wmfw_adsp2_id_hdr) / 2);
for (;; alg_hdr_addr += sizeof(alg.adsp2) / 2) {
ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
KUNIT_ASSERT_GE(priv->test, ret, 0);
KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
&alg.adsp2, sizeof(alg.adsp2));
KUNIT_ASSERT_GE(priv->test, ret, 0);
if (be32_to_cpu(alg.adsp2.alg.id) == alg_id) {
xm_base = be32_to_cpu(alg.adsp2.xm);
ym_base = be32_to_cpu(alg.adsp2.ym);
zm_base = be32_to_cpu(alg.adsp2.zm);
break;
}
}
break;
case WMFW_HALO:
alg_hdr_addr = xm + sizeof(struct wmfw_halo_id_hdr);
for (;; alg_hdr_addr += sizeof(alg.halo)) {
ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
KUNIT_ASSERT_GE(priv->test, ret, 0);
KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
&alg.halo, sizeof(alg.halo));
KUNIT_ASSERT_GE(priv->test, ret, 0);
if (be32_to_cpu(alg.halo.alg.id) == alg_id) {
xm_base = be32_to_cpu(alg.halo.xm_base);
ym_base = be32_to_cpu(alg.halo.ym_base);
break;
}
}
break;
default:
KUNIT_FAIL(priv->test, "Unexpected DSP type %d\n", priv->dsp->type);
return 0;
}
switch (mem_type) {
case WMFW_ADSP2_XM:
case WMFW_HALO_XM_PACKED:
return xm_base;
case WMFW_ADSP2_YM:
case WMFW_HALO_YM_PACKED:
return ym_base;
case WMFW_ADSP2_ZM:
return zm_base;
default:
KUNIT_FAIL(priv->test, "Bad mem_type\n");
return 0;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_alg_base_in_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_xm_header_get_fw_version_from_regmap() - Firmware version.
*
* @priv: Pointer to struct cs_dsp_test.
*
* Return: Firmware version word value.
*/
unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv)
{
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
union {
struct wmfw_id_hdr adsp2;
struct wmfw_v3_id_hdr halo;
} hdr;
switch (priv->dsp->type) {
case WMFW_ADSP2:
regmap_raw_read(priv->dsp->regmap, xm, &hdr.adsp2, sizeof(hdr.adsp2));
return be32_to_cpu(hdr.adsp2.ver);
case WMFW_HALO:
regmap_raw_read(priv->dsp->regmap, xm, &hdr.halo, sizeof(hdr.halo));
return be32_to_cpu(hdr.halo.ver);
default:
KUNIT_FAIL(priv->test, NULL);
return 0;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version_from_regmap,
"FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_xm_header_get_fw_version() - Firmware version.
*
* @header: Pointer to struct cs_dsp_mock_xm_header.
*
* Return: Firmware version word value.
*/
unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header)
{
const struct wmfw_id_hdr *adsp2_hdr;
const struct wmfw_v3_id_hdr *halo_hdr;
switch (header->test_priv->dsp->type) {
case WMFW_ADSP2:
adsp2_hdr = header->blob_data;
return be32_to_cpu(adsp2_hdr->ver);
case WMFW_HALO:
halo_hdr = header->blob_data;
return be32_to_cpu(halo_hdr->ver);
default:
KUNIT_FAIL(header->test_priv->test, NULL);
return 0;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_xm_header_drop_from_regmap_cache() - Drop XM header from regmap cache.
*
* @priv: Pointer to struct cs_dsp_test.
*/
void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv)
{
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
unsigned int bytes;
__be32 num_algs_be32;
unsigned int num_algs;
switch (priv->dsp->type) {
case WMFW_ADSP2:
/*
* Could be one 32-bit register or two 16-bit registers.
* A raw read will read the requested number of bytes.
*/
regmap_raw_read(priv->dsp->regmap,
xm + (offsetof(struct wmfw_adsp2_id_hdr, n_algs) / 2),
&num_algs_be32, sizeof(num_algs_be32));
num_algs = be32_to_cpu(num_algs_be32);
bytes = sizeof(struct wmfw_adsp2_id_hdr) +
(num_algs * sizeof(struct wmfw_adsp2_alg_hdr)) +
4 /* terminator word */;
regcache_drop_region(priv->dsp->regmap, xm, xm + (bytes / 2) - 1);
break;
case WMFW_HALO:
regmap_read(priv->dsp->regmap,
xm + offsetof(struct wmfw_halo_id_hdr, n_algs),
&num_algs);
bytes = sizeof(struct wmfw_halo_id_hdr) +
(num_algs * sizeof(struct wmfw_halo_alg_hdr)) +
4 /* terminator word */;
regcache_drop_region(priv->dsp->regmap, xm, xm + bytes - 4);
break;
default:
break;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_drop_from_regmap_cache, "FW_CS_DSP_KUNIT_TEST_UTILS");
static void cs_dsp_mock_xm_header_add_adsp2_algs(struct cs_dsp_mock_xm_header *builder,
const struct cs_dsp_mock_alg_def *algs,
size_t num_algs)
{
struct wmfw_adsp2_id_hdr *hdr = builder->blob_data;
unsigned int next_free_xm_word, next_free_ym_word, next_free_zm_word;
next_free_xm_word = be32_to_cpu(hdr->xm);
next_free_ym_word = be32_to_cpu(hdr->ym);
next_free_zm_word = be32_to_cpu(hdr->zm);
/* Set num_algs in XM header. */
hdr->n_algs = cpu_to_be32(num_algs);
/* Create algorithm descriptor list */
struct wmfw_adsp2_alg_hdr *alg_info =
(struct wmfw_adsp2_alg_hdr *)(&hdr[1]);
for (; num_algs > 0; num_algs--, algs++, alg_info++) {
unsigned int alg_xm_last, alg_ym_last, alg_zm_last;
alg_info->alg.id = cpu_to_be32(algs->id);
alg_info->alg.ver = cpu_to_be32(algs->ver);
alg_info->xm = cpu_to_be32(algs->xm_base_words);
alg_info->ym = cpu_to_be32(algs->ym_base_words);
alg_info->zm = cpu_to_be32(algs->zm_base_words);
/* Check if we need to auto-allocate base addresses */
if (!alg_info->xm && algs->xm_size_words)
alg_info->xm = cpu_to_be32(next_free_xm_word);
if (!alg_info->ym && algs->ym_size_words)
alg_info->ym = cpu_to_be32(next_free_ym_word);
if (!alg_info->zm && algs->zm_size_words)
alg_info->zm = cpu_to_be32(next_free_zm_word);
alg_xm_last = be32_to_cpu(alg_info->xm) + algs->xm_size_words - 1;
if (alg_xm_last > next_free_xm_word)
next_free_xm_word = alg_xm_last;
alg_ym_last = be32_to_cpu(alg_info->ym) + algs->ym_size_words - 1;
if (alg_ym_last > next_free_ym_word)
next_free_ym_word = alg_ym_last;
alg_zm_last = be32_to_cpu(alg_info->zm) + algs->zm_size_words - 1;
if (alg_zm_last > next_free_zm_word)
next_free_zm_word = alg_zm_last;
}
/* Write list terminator */
*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
}
static void cs_dsp_mock_xm_header_add_halo_algs(struct cs_dsp_mock_xm_header *builder,
const struct cs_dsp_mock_alg_def *algs,
size_t num_algs)
{
struct wmfw_halo_id_hdr *hdr = builder->blob_data;
unsigned int next_free_xm_word, next_free_ym_word;
/* Assume we're starting with bare header */
next_free_xm_word = be32_to_cpu(hdr->xm_base) + be32_to_cpu(hdr->xm_size) - 1;
next_free_ym_word = be32_to_cpu(hdr->ym_base) + be32_to_cpu(hdr->ym_size) - 1;
/* Set num_algs in XM header */
hdr->n_algs = cpu_to_be32(num_algs);
/* Create algorithm descriptor list */
struct wmfw_halo_alg_hdr *alg_info =
(struct wmfw_halo_alg_hdr *)(&hdr[1]);
for (; num_algs > 0; num_algs--, algs++, alg_info++) {
unsigned int alg_xm_last, alg_ym_last;
alg_info->alg.id = cpu_to_be32(algs->id);
alg_info->alg.ver = cpu_to_be32(algs->ver);
alg_info->xm_base = cpu_to_be32(algs->xm_base_words);
alg_info->xm_size = cpu_to_be32(algs->xm_size_words);
alg_info->ym_base = cpu_to_be32(algs->ym_base_words);
alg_info->ym_size = cpu_to_be32(algs->ym_size_words);
/* Check if we need to auto-allocate base addresses */
if (!alg_info->xm_base && alg_info->xm_size)
alg_info->xm_base = cpu_to_be32(next_free_xm_word);
if (!alg_info->ym_base && alg_info->ym_size)
alg_info->ym_base = cpu_to_be32(next_free_ym_word);
alg_xm_last = be32_to_cpu(alg_info->xm_base) + be32_to_cpu(alg_info->xm_size) - 1;
if (alg_xm_last > next_free_xm_word)
next_free_xm_word = alg_xm_last;
alg_ym_last = be32_to_cpu(alg_info->ym_base) + be32_to_cpu(alg_info->ym_size) - 1;
if (alg_ym_last > next_free_ym_word)
next_free_ym_word = alg_ym_last;
}
/* Write list terminator */
*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
}
/**
* cs_dsp_mock_xm_header_write_to_regmap() - Write XM header to regmap.
*
* @header: Pointer to struct cs_dsp_mock_xm_header.
*
* The data in header is written to the XM addresses in the regmap.
*
* Return: 0 on success, else negative error code.
*/
int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header)
{
struct cs_dsp_test *priv = header->test_priv;
unsigned int reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
/*
* One 32-bit word corresponds to one 32-bit unpacked XM word so the
* blob can be written directly to the regmap.
*/
return regmap_raw_write(priv->dsp->regmap, reg_addr,
header->blob_data, header->blob_size_bytes);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_write_to_regmap, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_create_mock_xm_header() - Create a dummy XM header.
*
* @priv: Pointer to struct cs_dsp_test.
* @algs: Pointer to array of struct cs_dsp_mock_alg_def listing the
* dummy algorithm entries to include in the XM header.
* @num_algs: Number of entries in the algs array.
*
* Return: Pointer to created struct cs_dsp_mock_xm_header.
*/
struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
const struct cs_dsp_mock_alg_def *algs,
size_t num_algs)
{
struct cs_dsp_mock_xm_header *builder;
size_t total_bytes_required;
const void *header;
size_t header_size_bytes;
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
builder->test_priv = priv;
switch (priv->dsp->type) {
case WMFW_ADSP2:
header = &cs_dsp_mock_adsp2_xm_hdr;
header_size_bytes = sizeof(cs_dsp_mock_adsp2_xm_hdr);
total_bytes_required = header_size_bytes +
(num_algs * sizeof(struct wmfw_adsp2_alg_hdr))
+ 4; /* terminator word */
break;
case WMFW_HALO:
header = &cs_dsp_mock_halo_xm_hdr,
header_size_bytes = sizeof(cs_dsp_mock_halo_xm_hdr);
total_bytes_required = header_size_bytes +
(num_algs * sizeof(struct wmfw_halo_alg_hdr))
+ 4; /* terminator word */
break;
default:
KUNIT_FAIL(priv->test, "%s unexpected DSP type %d\n",
__func__, priv->dsp->type);
return NULL;
}
builder->blob_data = kunit_kzalloc(priv->test, total_bytes_required, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder->blob_data);
builder->blob_size_bytes = total_bytes_required;
memcpy(builder->blob_data, header, header_size_bytes);
switch (priv->dsp->type) {
case WMFW_ADSP2:
cs_dsp_mock_xm_header_add_adsp2_algs(builder, algs, num_algs);
break;
case WMFW_HALO:
cs_dsp_mock_xm_header_add_halo_algs(builder, algs, num_algs);
break;
default:
break;
}
return builder;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_create_mock_xm_header, "FW_CS_DSP_KUNIT_TEST_UTILS");

View File

@ -0,0 +1,367 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Mock regmap for cs_dsp KUnit tests.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <kunit/test.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/regmap.h>
static int cs_dsp_mock_regmap_read(void *context, const void *reg_buf,
const size_t reg_size, void *val_buf,
size_t val_size)
{
struct cs_dsp_test *priv = context;
/* Should never get here because the regmap is cache-only */
KUNIT_FAIL(priv->test, "Unexpected bus read @%#x", *(u32 *)reg_buf);
return -EIO;
}
static int cs_dsp_mock_regmap_gather_write(void *context,
const void *reg_buf, size_t reg_size,
const void *val_buf, size_t val_size)
{
struct cs_dsp_test *priv = context;
priv->saw_bus_write = true;
/* Should never get here because the regmap is cache-only */
KUNIT_FAIL(priv->test, "Unexpected bus gather_write @%#x", *(u32 *)reg_buf);
return -EIO;
}
static int cs_dsp_mock_regmap_write(void *context, const void *val_buf, size_t val_size)
{
struct cs_dsp_test *priv = context;
priv->saw_bus_write = true;
/* Should never get here because the regmap is cache-only */
KUNIT_FAIL(priv->test, "Unexpected bus write @%#x", *(u32 *)val_buf);
return -EIO;
}
static const struct regmap_bus cs_dsp_mock_regmap_bus = {
.read = cs_dsp_mock_regmap_read,
.write = cs_dsp_mock_regmap_write,
.gather_write = cs_dsp_mock_regmap_gather_write,
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
};
static const struct reg_default adsp2_32bit_register_defaults[] = {
{ 0xffe00, 0x0000 }, /* CONTROL */
{ 0xffe02, 0x0000 }, /* CLOCKING */
{ 0xffe04, 0x0001 }, /* STATUS1: RAM_RDY=1 */
{ 0xffe30, 0x0000 }, /* WDMW_CONFIG_1 */
{ 0xffe32, 0x0000 }, /* WDMA_CONFIG_2 */
{ 0xffe34, 0x0000 }, /* RDMA_CONFIG_1 */
{ 0xffe40, 0x0000 }, /* SCRATCH_0_1 */
{ 0xffe42, 0x0000 }, /* SCRATCH_2_3 */
};
static const struct regmap_range adsp2_32bit_registers[] = {
regmap_reg_range(0x80000, 0x88ffe), /* PM */
regmap_reg_range(0xa0000, 0xa9ffe), /* XM */
regmap_reg_range(0xc0000, 0xc1ffe), /* YM */
regmap_reg_range(0xe0000, 0xe1ffe), /* ZM */
regmap_reg_range(0xffe00, 0xffe7c), /* CORE CTRL */
};
const unsigned int cs_dsp_mock_adsp2_32bit_sysbase = 0xffe00;
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
static const struct regmap_access_table adsp2_32bit_rw = {
.yes_ranges = adsp2_32bit_registers,
.n_yes_ranges = ARRAY_SIZE(adsp2_32bit_registers),
};
static const struct regmap_config cs_dsp_mock_regmap_adsp2_32bit = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 2,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
.wr_table = &adsp2_32bit_rw,
.rd_table = &adsp2_32bit_rw,
.max_register = 0xffe7c,
.reg_defaults = adsp2_32bit_register_defaults,
.num_reg_defaults = ARRAY_SIZE(adsp2_32bit_register_defaults),
.cache_type = REGCACHE_MAPLE,
};
static const struct reg_default adsp2_16bit_register_defaults[] = {
{ 0x1100, 0x0000 }, /* CONTROL */
{ 0x1101, 0x0000 }, /* CLOCKING */
{ 0x1104, 0x0001 }, /* STATUS1: RAM_RDY=1 */
{ 0x1130, 0x0000 }, /* WDMW_CONFIG_1 */
{ 0x1131, 0x0000 }, /* WDMA_CONFIG_2 */
{ 0x1134, 0x0000 }, /* RDMA_CONFIG_1 */
{ 0x1140, 0x0000 }, /* SCRATCH_0 */
{ 0x1141, 0x0000 }, /* SCRATCH_1 */
{ 0x1142, 0x0000 }, /* SCRATCH_2 */
{ 0x1143, 0x0000 }, /* SCRATCH_3 */
};
static const struct regmap_range adsp2_16bit_registers[] = {
regmap_reg_range(0x001100, 0x001143), /* CORE CTRL */
regmap_reg_range(0x100000, 0x105fff), /* PM */
regmap_reg_range(0x180000, 0x1807ff), /* ZM */
regmap_reg_range(0x190000, 0x1947ff), /* XM */
regmap_reg_range(0x1a8000, 0x1a97ff), /* YM */
};
const unsigned int cs_dsp_mock_adsp2_16bit_sysbase = 0x001100;
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
static const struct regmap_access_table adsp2_16bit_rw = {
.yes_ranges = adsp2_16bit_registers,
.n_yes_ranges = ARRAY_SIZE(adsp2_16bit_registers),
};
static const struct regmap_config cs_dsp_mock_regmap_adsp2_16bit = {
.reg_bits = 32,
.val_bits = 16,
.reg_stride = 1,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
.wr_table = &adsp2_16bit_rw,
.rd_table = &adsp2_16bit_rw,
.max_register = 0x1a97ff,
.reg_defaults = adsp2_16bit_register_defaults,
.num_reg_defaults = ARRAY_SIZE(adsp2_16bit_register_defaults),
.cache_type = REGCACHE_MAPLE,
};
static const struct reg_default halo_register_defaults[] = {
/* CORE */
{ 0x2b80010, 0 }, /* HALO_CORE_SOFT_RESET */
{ 0x2b805c0, 0 }, /* HALO_SCRATCH1 */
{ 0x2b805c8, 0 }, /* HALO_SCRATCH2 */
{ 0x2b805d0, 0 }, /* HALO_SCRATCH3 */
{ 0x2b805c8, 0 }, /* HALO_SCRATCH4 */
{ 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */
{ 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */
/* SYSINFO */
{ 0x25e2040, 0 }, /* HALO_AHBM_WINDOW_DEBUG_0 */
{ 0x25e2044, 0 }, /* HALO_AHBM_WINDOW_DEBUG_1 */
};
static const struct regmap_range halo_readable_registers[] = {
regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */
regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */
regmap_reg_range(0x2800000, 0x2807fff), /* XM */
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
};
static const struct regmap_range halo_writeable_registers[] = {
regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
regmap_reg_range(0x2800000, 0x2807fff), /* XM */
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
};
const unsigned int cs_dsp_mock_halo_core_base = 0x2b80000;
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_core_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
const unsigned int cs_dsp_mock_halo_sysinfo_base = 0x25e0000;
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_sysinfo_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
static const struct regmap_access_table halo_readable = {
.yes_ranges = halo_readable_registers,
.n_yes_ranges = ARRAY_SIZE(halo_readable_registers),
};
static const struct regmap_access_table halo_writeable = {
.yes_ranges = halo_writeable_registers,
.n_yes_ranges = ARRAY_SIZE(halo_writeable_registers),
};
static const struct regmap_config cs_dsp_mock_regmap_halo = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
.wr_table = &halo_writeable,
.rd_table = &halo_readable,
.max_register = 0x3804ffc,
.reg_defaults = halo_register_defaults,
.num_reg_defaults = ARRAY_SIZE(halo_register_defaults),
.cache_type = REGCACHE_MAPLE,
};
/**
* cs_dsp_mock_regmap_drop_range() - drop a range of registers from the cache.
*
* @priv: Pointer to struct cs_dsp_test object.
* @first_reg: Address of first register to drop.
* @last_reg: Address of last register to drop.
*/
void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
unsigned int first_reg, unsigned int last_reg)
{
regcache_drop_region(priv->dsp->regmap, first_reg, last_reg);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_range, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_regmap_drop_regs() - drop a number of registers from the cache.
*
* @priv: Pointer to struct cs_dsp_test object.
* @first_reg: Address of first register to drop.
* @num_regs: Number of registers to drop.
*/
void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
unsigned int first_reg, size_t num_regs)
{
int stride = regmap_get_reg_stride(priv->dsp->regmap);
unsigned int last = first_reg + (stride * (num_regs - 1));
cs_dsp_mock_regmap_drop_range(priv, first_reg, last);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_regmap_drop_bytes() - drop a number of bytes from the cache.
*
* @priv: Pointer to struct cs_dsp_test object.
* @first_reg: Address of first register to drop.
* @num_bytes: Number of bytes to drop from the cache. Will be rounded
* down to a whole number of registers. Trailing bytes that
* are not a multiple of the register size will not be dropped.
* (This is intended to help detect math errors in test code.)
*/
void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
unsigned int first_reg, size_t num_bytes)
{
size_t num_regs = num_bytes / regmap_get_val_bytes(priv->dsp->regmap);
cs_dsp_mock_regmap_drop_regs(priv, first_reg, num_regs);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_regmap_drop_system_regs() - Drop DSP system registers from the cache.
*
* @priv: Pointer to struct cs_dsp_test object.
*
* Drops all DSP system registers from the regmap cache.
*/
void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv)
{
switch (priv->dsp->type) {
case WMFW_ADSP2:
if (priv->dsp->base) {
regcache_drop_region(priv->dsp->regmap,
priv->dsp->base,
priv->dsp->base + 0x7c);
}
return;
case WMFW_HALO:
if (priv->dsp->base) {
regcache_drop_region(priv->dsp->regmap,
priv->dsp->base,
priv->dsp->base + 0x47000);
}
/* sysinfo registers are read-only so don't drop them */
return;
default:
return;
}
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_system_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_regmap_is_dirty() - Test for dirty registers in the cache.
*
* @priv: Pointer to struct cs_dsp_test object.
* @drop_system_regs: If true the DSP system regs will be dropped from
* the cache before checking for dirty.
*
* All registers that are expected to be written must have been dropped
* from the cache (DSP system registers can be dropped by passing
* drop_system_regs == true). If any unexpected registers were written
* there will still be dirty entries in the cache and a cache sync will
* cause a write.
*
* Returns: true if there were dirty entries, false if not.
*/
bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs)
{
if (drop_system_regs)
cs_dsp_mock_regmap_drop_system_regs(priv);
priv->saw_bus_write = false;
regcache_cache_only(priv->dsp->regmap, false);
regcache_sync(priv->dsp->regmap);
regcache_cache_only(priv->dsp->regmap, true);
return priv->saw_bus_write;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_is_dirty, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_regmap_init() - Initialize a mock regmap.
*
* @priv: Pointer to struct cs_dsp_test object. This must have a
* valid pointer to a struct cs_dsp in which the type and
* rev fields are set to the type of DSP to be simulated.
*
* On success the priv->dsp->regmap will point to the created
* regmap instance.
*
* Return: zero on success, else negative error code.
*/
int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv)
{
const struct regmap_config *config;
int ret;
switch (priv->dsp->type) {
case WMFW_HALO:
config = &cs_dsp_mock_regmap_halo;
break;
case WMFW_ADSP2:
if (priv->dsp->rev == 0)
config = &cs_dsp_mock_regmap_adsp2_16bit;
else
config = &cs_dsp_mock_regmap_adsp2_32bit;
break;
default:
config = NULL;
break;
}
priv->dsp->regmap = devm_regmap_init(priv->dsp->dev,
&cs_dsp_mock_regmap_bus,
priv,
config);
if (IS_ERR(priv->dsp->regmap)) {
ret = PTR_ERR(priv->dsp->regmap);
kunit_err(priv->test, "Failed to allocate register map: %d\n", ret);
return ret;
}
/* Put regmap in cache-only so it accumulates the writes done by cs_dsp */
regcache_cache_only(priv->dsp->regmap, true);
return 0;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_init, "FW_CS_DSP_KUNIT_TEST_UTILS");

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Utility module for cs_dsp KUnit testing.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <linux/module.h>
MODULE_DESCRIPTION("Utilities for Cirrus Logic DSP driver testing");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("FW_CS_DSP");

View File

@ -0,0 +1,473 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// wmfw file builder for cs_dsp KUnit tests.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <kunit/resource.h>
#include <kunit/test.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/firmware.h>
#include <linux/math.h>
#include <linux/overflow.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
/* Buffer large enough for bin file content */
#define CS_DSP_MOCK_WMFW_BUF_SIZE 131072
struct cs_dsp_mock_wmfw_builder {
struct cs_dsp_test *test_priv;
int format_version;
void *buf;
size_t buf_size_bytes;
void *write_p;
size_t bytes_used;
void *alg_data_header;
unsigned int num_coeffs;
};
struct wmfw_adsp2_halo_header {
struct wmfw_header header;
struct wmfw_adsp2_sizes sizes;
struct wmfw_footer footer;
} __packed;
struct wmfw_long_string {
__le16 len;
u8 data[] __nonstring __counted_by(len);
} __packed;
struct wmfw_short_string {
u8 len;
u8 data[] __nonstring __counted_by(len);
} __packed;
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
/**
* cs_dsp_mock_wmfw_format_version() - Return format version.
*
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
*
* Return: Format version.
*/
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder)
{
return builder->format_version;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_format_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_wmfw_get_firmware() - Get struct firmware wrapper for data.
*
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
*
* Return: Pointer to a struct firmware wrapper for the data.
*/
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder)
{
struct firmware *fw;
if (!builder)
return NULL;
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
fw->data = builder->buf;
fw->size = builder->bytes_used;
return fw;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_wmfw_add_raw_block() - Add a block to the wmfw file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @block_type: Block type.
* @offset: Offset.
* @payload_data: Pointer to buffer containing the payload data,
* or NULL if no data.
* @payload_len_bytes: Length of payload data in bytes, or zero.
*/
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
int block_type, unsigned int offset,
const void *payload_data, size_t payload_len_bytes)
{
struct wmfw_region *header = builder->write_p;
unsigned int bytes_needed = struct_size_t(struct wmfw_region, data, payload_len_bytes);
KUNIT_ASSERT_TRUE(builder->test_priv->test,
(builder->write_p + bytes_needed) <
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
header->offset = cpu_to_le32(offset | (block_type << 24));
header->len = cpu_to_le32(payload_len_bytes);
if (payload_len_bytes > 0)
memcpy(header->data, payload_data, payload_len_bytes);
builder->write_p += bytes_needed;
builder->bytes_used += bytes_needed;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_wmfw_add_info() - Add an info block to the wmfw file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @info: Pointer to info string to be copied into the file.
*
* The string will be padded to a length that is a multiple of 4 bytes.
*/
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
const char *info)
{
size_t info_len = strlen(info);
char *tmp = NULL;
if (info_len % 4) {
/* Create a padded string with length a multiple of 4 */
info_len = round_up(info_len, 4);
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
memcpy(tmp, info, info_len);
info = tmp;
}
cs_dsp_mock_wmfw_add_raw_block(builder, WMFW_INFO_TEXT, 0, info, info_len);
kunit_kfree(builder->test_priv->test, tmp);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
/**
* cs_dsp_mock_wmfw_add_data_block() - Add a data block to the wmfw file.
*
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
* @mem_region: Memory region for the block.
* @mem_offset_dsp_words: Offset to start of destination in DSP words.
* @payload_data: Pointer to buffer containing the payload data.
* @payload_len_bytes: Length of payload data in bytes.
*/
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
int mem_region, unsigned int mem_offset_dsp_words,
const void *payload_data, size_t payload_len_bytes)
{
/* Blob payload length must be a multiple of 4 */
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
cs_dsp_mock_wmfw_add_raw_block(builder, mem_region, mem_offset_dsp_words,
payload_data, payload_len_bytes);
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_data_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
unsigned int alg_id,
const char *name,
const char *description)
{
struct wmfw_region *rgn = builder->write_p;
struct wmfw_adsp_alg_data *v1;
struct wmfw_short_string *shortstring;
struct wmfw_long_string *longstring;
size_t bytes_needed, name_len, description_len;
int offset;
/* Bytes needed for region header */
bytes_needed = offsetof(struct wmfw_region, data);
builder->alg_data_header = builder->write_p;
builder->num_coeffs = 0;
switch (builder->format_version) {
case 0:
KUNIT_FAIL(builder->test_priv->test, "wmfwV0 does not have alg blocks\n");
return;
case 1:
bytes_needed += offsetof(struct wmfw_adsp_alg_data, data);
KUNIT_ASSERT_TRUE(builder->test_priv->test,
(builder->write_p + bytes_needed) <
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
memset(builder->write_p, 0, bytes_needed);
/* Create region header */
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
/* Create algorithm entry */
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
v1->id = cpu_to_le32(alg_id);
if (name)
strscpy(v1->name, name, sizeof(v1->name));
if (description)
strscpy(v1->descr, description, sizeof(v1->descr));
break;
default:
name_len = 0;
description_len = 0;
if (name)
name_len = strlen(name);
if (description)
description_len = strlen(description);
bytes_needed += sizeof(__le32); /* alg id */
bytes_needed += round_up(name_len + sizeof(u8), sizeof(__le32));
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
bytes_needed += sizeof(__le32); /* coeff count */
KUNIT_ASSERT_TRUE(builder->test_priv->test,
(builder->write_p + bytes_needed) <
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
memset(builder->write_p, 0, bytes_needed);
/* Create region header */
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
/* Create algorithm entry */
*(__force __le32 *)&rgn->data[0] = cpu_to_le32(alg_id);
shortstring = (struct wmfw_short_string *)&rgn->data[4];
shortstring->len = name_len;
if (name_len)
memcpy(shortstring->data, name, name_len);
/* Round up to next __le32 */
offset = round_up(4 + struct_size_t(struct wmfw_short_string, data, name_len),
sizeof(__le32));
longstring = (struct wmfw_long_string *)&rgn->data[offset];
longstring->len = cpu_to_le16(description_len);
if (description_len)
memcpy(longstring->data, description, description_len);
break;
}
builder->write_p += bytes_needed;
builder->bytes_used += bytes_needed;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_start_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
const struct cs_dsp_mock_coeff_def *def)
{
struct wmfw_adsp_coeff_data *v1;
struct wmfw_short_string *shortstring;
struct wmfw_long_string *longstring;
size_t bytes_needed, shortname_len, fullname_len, description_len;
__le32 *ple32;
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, builder->alg_data_header);
switch (builder->format_version) {
case 0:
return;
case 1:
bytes_needed = offsetof(struct wmfw_adsp_coeff_data, data);
KUNIT_ASSERT_TRUE(builder->test_priv->test,
(builder->write_p + bytes_needed) <
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
v1 = (struct wmfw_adsp_coeff_data *)builder->write_p;
memset(v1, 0, sizeof(*v1));
v1->hdr.offset = cpu_to_le16(def->offset_dsp_words);
v1->hdr.type = cpu_to_le16(def->mem_type);
v1->hdr.size = cpu_to_le32(bytes_needed - sizeof(v1->hdr));
v1->ctl_type = cpu_to_le16(def->type);
v1->flags = cpu_to_le16(def->flags);
v1->len = cpu_to_le32(def->length_bytes);
if (def->fullname)
strscpy(v1->name, def->fullname, sizeof(v1->name));
if (def->description)
strscpy(v1->descr, def->description, sizeof(v1->descr));
break;
default:
fullname_len = 0;
description_len = 0;
shortname_len = strlen(def->shortname);
if (def->fullname)
fullname_len = strlen(def->fullname);
if (def->description)
description_len = strlen(def->description);
bytes_needed = sizeof(__le32) * 2; /* type, offset and size */
bytes_needed += round_up(shortname_len + sizeof(u8), sizeof(__le32));
bytes_needed += round_up(fullname_len + sizeof(u8), sizeof(__le32));
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
bytes_needed += sizeof(__le32) * 2; /* flags, type and length */
KUNIT_ASSERT_TRUE(builder->test_priv->test,
(builder->write_p + bytes_needed) <
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
ple32 = (__force __le32 *)builder->write_p;
*ple32++ = cpu_to_le32(def->offset_dsp_words | (def->mem_type << 16));
*ple32++ = cpu_to_le32(bytes_needed - sizeof(__le32) - sizeof(__le32));
shortstring = (__force struct wmfw_short_string *)ple32;
shortstring->len = shortname_len;
memcpy(shortstring->data, def->shortname, shortname_len);
/* Round up to next __le32 multiple */
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, shortname_len),
sizeof(*ple32)) / sizeof(*ple32);
shortstring = (__force struct wmfw_short_string *)ple32;
shortstring->len = fullname_len;
memcpy(shortstring->data, def->fullname, fullname_len);
/* Round up to next __le32 multiple */
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, fullname_len),
sizeof(*ple32)) / sizeof(*ple32);
longstring = (__force struct wmfw_long_string *)ple32;
longstring->len = cpu_to_le16(description_len);
memcpy(longstring->data, def->description, description_len);
/* Round up to next __le32 multiple */
ple32 += round_up(struct_size_t(struct wmfw_long_string, data, description_len),
sizeof(*ple32)) / sizeof(*ple32);
*ple32++ = cpu_to_le32(def->type | (def->flags << 16));
*ple32 = cpu_to_le32(def->length_bytes);
break;
}
builder->write_p += bytes_needed;
builder->bytes_used += bytes_needed;
builder->num_coeffs++;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_coeff_desc, "FW_CS_DSP_KUNIT_TEST_UTILS");
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder)
{
struct wmfw_region *rgn = builder->alg_data_header;
struct wmfw_adsp_alg_data *v1;
const struct wmfw_short_string *shortstring;
const struct wmfw_long_string *longstring;
size_t offset;
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, rgn);
/* Fill in data size */
rgn->len = cpu_to_le32((u8 *)builder->write_p - (u8 *)rgn->data);
/* Fill in coefficient count */
switch (builder->format_version) {
case 0:
return;
case 1:
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
v1->ncoeff = cpu_to_le32(builder->num_coeffs);
break;
default:
offset = 4; /* skip alg id */
/* Get name length and round up to __le32 multiple */
shortstring = (const struct wmfw_short_string *)&rgn->data[offset];
offset += round_up(struct_size_t(struct wmfw_short_string, data, shortstring->len),
sizeof(__le32));
/* Get description length and round up to __le32 multiple */
longstring = (const struct wmfw_long_string *)&rgn->data[offset];
offset += round_up(struct_size_t(struct wmfw_long_string, data,
le16_to_cpu(longstring->len)),
sizeof(__le32));
*(__force __le32 *)&rgn->data[offset] = cpu_to_le32(builder->num_coeffs);
break;
}
builder->alg_data_header = NULL;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_end_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
static void cs_dsp_init_adsp2_halo_wmfw(struct cs_dsp_mock_wmfw_builder *builder)
{
struct wmfw_adsp2_halo_header *hdr = builder->buf;
const struct cs_dsp *dsp = builder->test_priv->dsp;
memcpy(hdr->header.magic, "WMFW", sizeof(hdr->header.magic));
hdr->header.len = cpu_to_le32(sizeof(*hdr));
hdr->header.ver = builder->format_version;
hdr->header.core = dsp->type;
hdr->header.rev = cpu_to_le16(dsp->rev);
hdr->sizes.pm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_PM));
hdr->sizes.xm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_XM));
hdr->sizes.ym = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_YM));
switch (dsp->type) {
case WMFW_ADSP2:
hdr->sizes.zm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_ZM));
break;
default:
break;
}
builder->write_p = &hdr[1];
builder->bytes_used += sizeof(*hdr);
}
/**
* cs_dsp_mock_wmfw_init() - Initialize a struct cs_dsp_mock_wmfw_builder.
*
* @priv: Pointer to struct cs_dsp_test.
* @format_version: Required wmfw format version.
*
* Return: Pointer to created struct cs_dsp_mock_wmfw_builder.
*/
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
int format_version)
{
struct cs_dsp_mock_wmfw_builder *builder;
/* If format version isn't given use the default for the target core */
if (format_version < 0) {
switch (priv->dsp->type) {
case WMFW_ADSP2:
format_version = 2;
break;
default:
format_version = 3;
break;
}
}
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
builder->test_priv = priv;
builder->format_version = format_version;
builder->buf = vmalloc(CS_DSP_MOCK_WMFW_BUF_SIZE);
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
builder->buf_size_bytes = CS_DSP_MOCK_WMFW_BUF_SIZE;
switch (priv->dsp->type) {
case WMFW_ADSP2:
case WMFW_HALO:
cs_dsp_init_adsp2_halo_wmfw(builder);
break;
default:
break;
}
return builder;
}
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_init, "FW_CS_DSP_KUNIT_TEST_UTILS");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,600 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// KUnit tests for cs_dsp.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
#include <kunit/device.h>
#include <kunit/resource.h>
#include <kunit/test.h>
#include <linux/build_bug.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/random.h>
#include <linux/regmap.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *);
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *);
struct cs_dsp_test_local {
struct cs_dsp_mock_bin_builder *bin_builder;
struct cs_dsp_mock_xm_header *xm_header;
struct cs_dsp_mock_wmfw_builder *wmfw_builder;
struct firmware *wmfw;
int wmfw_version;
};
struct cs_dsp_bin_test_param {
int block_type;
};
static const struct cs_dsp_mock_alg_def cs_dsp_bin_err_test_mock_algs[] = {
{
.id = 0xfafa,
.ver = 0x100000,
.xm_size_words = 164,
.ym_size_words = 164,
.zm_size_words = 164,
},
};
/* Load a bin containing unknown blocks. They should be skipped. */
static void bin_load_with_unknown_blocks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
unsigned int reg_addr;
u8 *payload_data, *readback;
u8 random_data[8];
const unsigned int payload_size_bytes = 64;
payload_data = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
get_random_bytes(payload_data, payload_size_bytes);
readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);
/* Add some unknown blocks at the start of the bin */
get_random_bytes(random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
0xf5, 0,
random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
0xf500, 0,
random_data, sizeof(random_data));
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
0xc300, 0,
random_data, sizeof(random_data));
/* Add a single payload to be written to DSP memory */
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
WMFW_ADSP2_YM, 0,
payload_data, payload_size_bytes);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
/* Check that the payload was written to memory */
reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_YM);
KUNIT_EXPECT_EQ(test,
regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
0);
KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);
}
/* Load a bin that doesn't have a valid magic marker. */
static void bin_err_wrong_magic(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
memcpy((void *)bin->data, "WMFW", 4);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
memcpy((void *)bin->data, "xMDR", 4);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
memcpy((void *)bin->data, "WxDR", 4);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
memcpy((void *)bin->data, "WMxR", 4);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
memcpy((void *)bin->data, "WMDx", 4);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
memset((void *)bin->data, 0, 4);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
/* Load a bin that is too short for a valid header. */
static void bin_err_too_short_for_header(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
do {
bin->size--;
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
} while (bin->size > 0);
}
/* Header length field isn't a valid header length. */
static void bin_err_bad_header_length(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
struct wmfw_coeff_hdr *header;
unsigned int real_len, len;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
header = (struct wmfw_coeff_hdr *)bin->data;
real_len = le32_to_cpu(header->len);
for (len = 0; len < real_len; len++) {
header->len = cpu_to_le32(len);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
for (len = real_len + 1; len < real_len + 7; len++) {
header->len = cpu_to_le32(len);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
header->len = cpu_to_le32(0xffffffff);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
header->len = cpu_to_le32(0x80000000);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
header->len = cpu_to_le32(0x7fffffff);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
/* Wrong core type in header. */
static void bin_err_bad_core_type(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
struct wmfw_coeff_hdr *header;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
header = (struct wmfw_coeff_hdr *)bin->data;
header->core_ver = cpu_to_le32(0);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
header->core_ver = cpu_to_le32(1);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
header->core_ver = cpu_to_le32(priv->dsp->type + 1);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
header->core_ver = cpu_to_le32(0xff);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
/* File too short to contain a full block header */
static void bin_too_short_for_block_header(struct kunit *test)
{
const struct cs_dsp_bin_test_param *param = test->param_value;
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
unsigned int header_length;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
header_length = bin->size;
kunit_kfree(test, bin);
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
param->block_type, 0,
NULL, 0);
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
KUNIT_ASSERT_GT(test, bin->size, header_length);
for (bin->size--; bin->size > header_length; bin->size--) {
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
}
/* File too short to contain the block payload */
static void bin_too_short_for_block_payload(struct kunit *test)
{
const struct cs_dsp_bin_test_param *param = test->param_value;
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
static const u8 payload[256] = { };
int i;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
param->block_type, 0,
payload, sizeof(payload));
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
for (i = 0; i < sizeof(payload); i++) {
bin->size--;
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
}
/* Block payload length is a garbage value */
static void bin_block_payload_len_garbage(struct kunit *test)
{
const struct cs_dsp_bin_test_param *param = test->param_value;
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *bin;
struct wmfw_coeff_hdr *header;
struct wmfw_coeff_item *block;
u32 payload = 0;
/* Sanity-check that the wmfw loads ok without the bin */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
0);
cs_dsp_power_down(priv->dsp);
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
cs_dsp_bin_err_test_mock_algs[0].id,
cs_dsp_bin_err_test_mock_algs[0].ver,
param->block_type, 0,
&payload, sizeof(payload));
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
header = (struct wmfw_coeff_hdr *)bin->data;
block = (struct wmfw_coeff_item *)&bin->data[le32_to_cpu(header->len)];
/* Sanity check that we're looking at the correct part of the bin */
KUNIT_ASSERT_EQ(test, le16_to_cpu(block->type), param->block_type);
KUNIT_ASSERT_EQ(test, le32_to_cpu(block->len), sizeof(payload));
block->len = cpu_to_le32(0x8000);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
block->len = cpu_to_le32(0xffff);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
block->len = cpu_to_le32(0x7fffffff);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
block->len = cpu_to_le32(0x80000000);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
block->len = cpu_to_le32(0xffffffff);
KUNIT_EXPECT_LT(test,
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
0);
}
static void cs_dsp_bin_err_test_exit(struct kunit *test)
{
/*
* Testing error conditions can produce a lot of log output
* from cs_dsp error messages, so rate limit the test cases.
*/
usleep_range(200, 500);
}
static int cs_dsp_bin_err_test_common_init(struct kunit *test, struct cs_dsp *dsp,
int wmfw_version)
{
struct cs_dsp_test *priv;
struct cs_dsp_test_local *local;
struct device *test_dev;
int ret;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
if (!local)
return -ENOMEM;
priv->test = test;
priv->dsp = dsp;
test->priv = priv;
priv->local = local;
priv->local->wmfw_version = wmfw_version;
/* Create dummy struct device */
test_dev = kunit_device_register(test, "cs_dsp_test_drv");
if (IS_ERR(test_dev))
return PTR_ERR(test_dev);
dsp->dev = get_device(test_dev);
if (!dsp->dev)
return -ENODEV;
ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
if (ret)
return ret;
dev_set_drvdata(dsp->dev, priv);
/* Allocate regmap */
ret = cs_dsp_mock_regmap_init(priv);
if (ret)
return ret;
/*
* There must always be a XM header with at least 1 algorithm, so create
* a dummy one that tests can use and extract it to a data payload.
*/
local->xm_header = cs_dsp_create_mock_xm_header(priv,
cs_dsp_bin_err_test_mock_algs,
ARRAY_SIZE(cs_dsp_bin_err_test_mock_algs));
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->xm_header);
local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, priv->local->wmfw_version);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);
/* Add dummy XM header payload to wmfw */
cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
WMFW_ADSP2_XM, 0,
local->xm_header->blob_data,
local->xm_header->blob_size_bytes);
local->wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
local->bin_builder =
cs_dsp_mock_bin_init(priv, 1,
cs_dsp_mock_xm_header_get_fw_version_from_regmap(priv));
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->bin_builder);
/* Init cs_dsp */
dsp->client_ops = kunit_kzalloc(test, sizeof(*dsp->client_ops), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp->client_ops);
switch (dsp->type) {
case WMFW_ADSP2:
ret = cs_dsp_adsp2_init(dsp);
break;
case WMFW_HALO:
ret = cs_dsp_halo_init(dsp);
break;
default:
KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
return -EINVAL;
}
if (ret)
return ret;
/* Automatically call cs_dsp_remove() when test case ends */
return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
}
static int cs_dsp_bin_err_test_halo_init(struct kunit *test)
{
struct cs_dsp *dsp;
/* Fill in cs_dsp and initialize */
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
return -ENOMEM;
dsp->num = 1;
dsp->type = WMFW_HALO;
dsp->mem = cs_dsp_mock_halo_dsp1_regions;
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
dsp->base = cs_dsp_mock_halo_core_base;
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
return cs_dsp_bin_err_test_common_init(test, dsp, 3);
}
static int cs_dsp_bin_err_test_adsp2_32bit_init(struct kunit *test)
{
struct cs_dsp *dsp;
/* Fill in cs_dsp and initialize */
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
return -ENOMEM;
dsp->num = 1;
dsp->type = WMFW_ADSP2;
dsp->rev = 1;
dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
return cs_dsp_bin_err_test_common_init(test, dsp, 2);
}
static int cs_dsp_bin_err_test_adsp2_16bit_init(struct kunit *test)
{
struct cs_dsp *dsp;
/* Fill in cs_dsp and initialize */
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
return -ENOMEM;
dsp->num = 1;
dsp->type = WMFW_ADSP2;
dsp->rev = 0;
dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
return cs_dsp_bin_err_test_common_init(test, dsp, 1);
}
static struct kunit_case cs_dsp_bin_err_test_cases_halo[] = {
{ } /* terminator */
};
static void cs_dsp_bin_err_block_types_desc(const struct cs_dsp_bin_test_param *param,
char *desc)
{
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "block_type:%#x", param->block_type);
}
/* Some block types to test against, including illegal types */
static const struct cs_dsp_bin_test_param bin_test_block_types_cases[] = {
{ .block_type = WMFW_INFO_TEXT << 8 },
{ .block_type = WMFW_METADATA << 8 },
{ .block_type = WMFW_ADSP2_PM },
{ .block_type = WMFW_ADSP2_XM },
{ .block_type = 0x33 },
{ .block_type = 0xf500 },
{ .block_type = 0xc000 },
};
KUNIT_ARRAY_PARAM(bin_test_block_types,
bin_test_block_types_cases,
cs_dsp_bin_err_block_types_desc);
static struct kunit_case cs_dsp_bin_err_test_cases_adsp2[] = {
KUNIT_CASE(bin_load_with_unknown_blocks),
KUNIT_CASE(bin_err_wrong_magic),
KUNIT_CASE(bin_err_too_short_for_header),
KUNIT_CASE(bin_err_bad_header_length),
KUNIT_CASE(bin_err_bad_core_type),
KUNIT_CASE_PARAM(bin_too_short_for_block_header, bin_test_block_types_gen_params),
KUNIT_CASE_PARAM(bin_too_short_for_block_payload, bin_test_block_types_gen_params),
KUNIT_CASE_PARAM(bin_block_payload_len_garbage, bin_test_block_types_gen_params),
{ } /* terminator */
};
static struct kunit_suite cs_dsp_bin_err_test_halo = {
.name = "cs_dsp_bin_err_halo",
.init = cs_dsp_bin_err_test_halo_init,
.exit = cs_dsp_bin_err_test_exit,
.test_cases = cs_dsp_bin_err_test_cases_halo,
};
static struct kunit_suite cs_dsp_bin_err_test_adsp2_32bit = {
.name = "cs_dsp_bin_err_adsp2_32bit",
.init = cs_dsp_bin_err_test_adsp2_32bit_init,
.exit = cs_dsp_bin_err_test_exit,
.test_cases = cs_dsp_bin_err_test_cases_adsp2,
};
static struct kunit_suite cs_dsp_bin_err_test_adsp2_16bit = {
.name = "cs_dsp_bin_err_adsp2_16bit",
.init = cs_dsp_bin_err_test_adsp2_16bit_init,
.exit = cs_dsp_bin_err_test_exit,
.test_cases = cs_dsp_bin_err_test_cases_adsp2,
};
kunit_test_suites(&cs_dsp_bin_err_test_halo,
&cs_dsp_bin_err_test_adsp2_32bit,
&cs_dsp_bin_err_test_adsp2_16bit);

View File

@ -0,0 +1,688 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// KUnit tests for cs_dsp.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
//
#include <kunit/device.h>
#include <kunit/resource.h>
#include <kunit/test.h>
#include <kunit/test-bug.h>
#include <linux/build_bug.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/random.h>
#include <linux/regmap.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#define ADSP2_LOCK_REGION_CTRL 0x7A
#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *)
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *)
struct cs_dsp_test_local {
struct cs_dsp_mock_wmfw_builder *wmfw_builder;
int num_control_add;
int num_control_remove;
int num_pre_run;
int num_post_run;
int num_pre_stop;
int num_post_stop;
int num_watchdog_expired;
struct cs_dsp_coeff_ctl *passed_ctl[16];
struct cs_dsp *passed_dsp;
};
struct cs_dsp_callbacks_test_param {
const struct cs_dsp_client_ops *ops;
const char *case_name;
};
static const struct cs_dsp_mock_alg_def cs_dsp_callbacks_test_mock_algs[] = {
{
.id = 0xfafa,
.ver = 0x100000,
.xm_size_words = 164,
.ym_size_words = 164,
.zm_size_words = 164,
},
};
static const struct cs_dsp_mock_coeff_def mock_coeff_template = {
.shortname = "Dummy Coeff",
.type = WMFW_CTL_TYPE_BYTES,
.mem_type = WMFW_ADSP2_YM,
.flags = WMFW_CTL_FLAG_VOLATILE,
.length_bytes = 4,
};
static int cs_dsp_test_control_add_callback(struct cs_dsp_coeff_ctl *ctl)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_ctl[local->num_control_add] = ctl;
local->num_control_add++;
return 0;
}
static void cs_dsp_test_control_remove_callback(struct cs_dsp_coeff_ctl *ctl)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_ctl[local->num_control_remove] = ctl;
local->num_control_remove++;
}
static int cs_dsp_test_pre_run_callback(struct cs_dsp *dsp)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_dsp = dsp;
local->num_pre_run++;
return 0;
}
static int cs_dsp_test_post_run_callback(struct cs_dsp *dsp)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_dsp = dsp;
local->num_post_run++;
return 0;
}
static void cs_dsp_test_pre_stop_callback(struct cs_dsp *dsp)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_dsp = dsp;
local->num_pre_stop++;
}
static void cs_dsp_test_post_stop_callback(struct cs_dsp *dsp)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_dsp = dsp;
local->num_post_stop++;
}
static void cs_dsp_test_watchdog_expired_callback(struct cs_dsp *dsp)
{
struct kunit *test = kunit_get_current_test();
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
local->passed_dsp = dsp;
local->num_watchdog_expired++;
}
static const struct cs_dsp_client_ops cs_dsp_callback_test_client_ops = {
.control_add = cs_dsp_test_control_add_callback,
.control_remove = cs_dsp_test_control_remove_callback,
.pre_run = cs_dsp_test_pre_run_callback,
.post_run = cs_dsp_test_post_run_callback,
.pre_stop = cs_dsp_test_pre_stop_callback,
.post_stop = cs_dsp_test_post_stop_callback,
.watchdog_expired = cs_dsp_test_watchdog_expired_callback,
};
static const struct cs_dsp_client_ops cs_dsp_callback_test_empty_client_ops = {
/* No entries */
};
static void cs_dsp_test_run_stop_callbacks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *wmfw;
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
local->passed_dsp = NULL;
cs_dsp_stop(priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
local->passed_dsp = NULL;
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
local->passed_dsp = NULL;
cs_dsp_stop(priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 2);
KUNIT_EXPECT_EQ(test, local->num_post_stop, 2);
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
local->passed_dsp = NULL;
}
static void cs_dsp_test_ctl_v1_callbacks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
struct cs_dsp_coeff_ctl *ctl;
struct firmware *wmfw;
int i;
/* Add a control for each memory */
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
cs_dsp_callbacks_test_mock_algs[0].id,
"dummyalg", NULL);
def.shortname = "zm";
def.mem_type = WMFW_ADSP2_ZM;
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
def.shortname = "ym";
def.mem_type = WMFW_ADSP2_YM;
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
def.shortname = "xm";
def.mem_type = WMFW_ADSP2_XM;
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
/* There should have been an add callback for each control */
KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list), 3);
KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
i = 0;
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
/*
* Call cs_dsp_remove() and there should be a remove callback
* for each control
*/
memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
cs_dsp_remove(priv->dsp);
/* Prevent double cleanup */
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
KUNIT_EXPECT_EQ(test, local->num_control_remove, 3);
i = 0;
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
}
static void cs_dsp_test_ctl_v2_callbacks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
struct cs_dsp_coeff_ctl *ctl;
struct firmware *wmfw;
char name[2] = { };
int i;
/* Add some controls */
def.shortname = name;
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
cs_dsp_callbacks_test_mock_algs[0].id,
"dummyalg", NULL);
for (i = 0; i < ARRAY_SIZE(local->passed_ctl); ++i) {
name[0] = 'A' + i;
def.offset_dsp_words = i;
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
}
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
/* There should have been an add callback for each control */
KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list),
ARRAY_SIZE(local->passed_ctl));
KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
i = 0;
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
/*
* Call cs_dsp_remove() and there should be a remove callback
* for each control
*/
memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
cs_dsp_remove(priv->dsp);
/* Prevent double cleanup */
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
KUNIT_EXPECT_EQ(test, local->num_control_remove, ARRAY_SIZE(local->passed_ctl));
i = 0;
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
}
static void cs_dsp_test_no_callbacks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
struct firmware *wmfw;
/* Add a controls */
def.shortname = "A";
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
cs_dsp_callbacks_test_mock_algs[0].id,
"dummyalg", NULL);
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
/* Run a sequence of ops that would invoke callbacks */
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
cs_dsp_stop(priv->dsp);
cs_dsp_remove(priv->dsp);
/* Prevent double cleanup */
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
/* Something went very wrong if any of our callbacks were called */
KUNIT_EXPECT_EQ(test, local->num_control_add, 0);
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
KUNIT_EXPECT_EQ(test, local->num_pre_run, 0);
KUNIT_EXPECT_EQ(test, local->num_post_run, 0);
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
}
static void cs_dsp_test_adsp2v2_watchdog_callback(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *wmfw;
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
/* Set the watchdog timeout bit */
regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
ADSP2_WDT_TIMEOUT_STS_MASK);
/* Notify an interrupt and the watchdog callback should be called */
cs_dsp_adsp2_bus_error(priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
}
static void cs_dsp_test_adsp2v2_watchdog_no_callbacks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *wmfw;
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
/* Set the watchdog timeout bit */
regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
ADSP2_WDT_TIMEOUT_STS_MASK);
/* Notify an interrupt, which will look for a watchdog callback */
cs_dsp_adsp2_bus_error(priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
}
static void cs_dsp_test_halo_watchdog_callback(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *wmfw;
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
/* Notify an interrupt and the watchdog callback should be called */
cs_dsp_halo_wdt_expire(priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
}
static void cs_dsp_test_halo_watchdog_no_callbacks(struct kunit *test)
{
struct cs_dsp_test *priv = test->priv;
struct cs_dsp_test_local *local = priv->local;
struct firmware *wmfw;
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
KUNIT_EXPECT_EQ(test,
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
0);
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
/* Notify an interrupt, which will look for a watchdog callback */
cs_dsp_halo_wdt_expire(priv->dsp);
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
}
static int cs_dsp_callbacks_test_common_init(struct kunit *test, struct cs_dsp *dsp,
int wmfw_version)
{
const struct cs_dsp_callbacks_test_param *param = test->param_value;
struct cs_dsp_test *priv;
struct cs_dsp_test_local *local;
struct device *test_dev;
struct cs_dsp_mock_xm_header *xm_header;
int ret;
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
if (!local)
return -ENOMEM;
priv->test = test;
priv->dsp = dsp;
test->priv = priv;
priv->local = local;
/* Create dummy struct device */
test_dev = kunit_device_register(test, "cs_dsp_test_drv");
if (IS_ERR(test_dev))
return PTR_ERR(test_dev);
dsp->dev = get_device(test_dev);
if (!dsp->dev)
return -ENODEV;
ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
if (ret)
return ret;
dev_set_drvdata(dsp->dev, priv);
/* Allocate regmap */
ret = cs_dsp_mock_regmap_init(priv);
if (ret)
return ret;
/*
* There must always be a XM header with at least 1 algorithm,
* so create a dummy one and pre-populate XM so the wmfw doesn't
* have to contain an XM blob.
*/
xm_header = cs_dsp_create_mock_xm_header(priv,
cs_dsp_callbacks_test_mock_algs,
ARRAY_SIZE(cs_dsp_callbacks_test_mock_algs));
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xm_header);
cs_dsp_mock_xm_header_write_to_regmap(xm_header);
local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, wmfw_version);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);
/* Add dummy XM header payload to wmfw */
cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
WMFW_ADSP2_XM, 0,
xm_header->blob_data,
xm_header->blob_size_bytes);
/* Init cs_dsp */
dsp->client_ops = param->ops;
switch (dsp->type) {
case WMFW_ADSP2:
ret = cs_dsp_adsp2_init(dsp);
break;
case WMFW_HALO:
ret = cs_dsp_halo_init(dsp);
break;
default:
KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
return -EINVAL;
}
if (ret)
return ret;
/* Automatically call cs_dsp_remove() when test case ends */
return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
}
static int cs_dsp_callbacks_test_halo_init(struct kunit *test)
{
struct cs_dsp *dsp;
/* Fill in cs_dsp and initialize */
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
return -ENOMEM;
dsp->num = 1;
dsp->type = WMFW_HALO;
dsp->mem = cs_dsp_mock_halo_dsp1_regions;
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
dsp->base = cs_dsp_mock_halo_core_base;
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
return cs_dsp_callbacks_test_common_init(test, dsp, 3);
}
static int cs_dsp_callbacks_test_adsp2_32bit_init(struct kunit *test, int rev)
{
struct cs_dsp *dsp;
/* Fill in cs_dsp and initialize */
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
return -ENOMEM;
dsp->num = 1;
dsp->type = WMFW_ADSP2;
dsp->rev = rev;
dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
return cs_dsp_callbacks_test_common_init(test, dsp, 2);
}
static int cs_dsp_callbacks_test_adsp2v2_32bit_init(struct kunit *test)
{
return cs_dsp_callbacks_test_adsp2_32bit_init(test, 2);
}
static int cs_dsp_callbacks_test_adsp2v1_32bit_init(struct kunit *test)
{
return cs_dsp_callbacks_test_adsp2_32bit_init(test, 1);
}
static int cs_dsp_callbacks_test_adsp2_16bit_init(struct kunit *test)
{
struct cs_dsp *dsp;
/* Fill in cs_dsp and initialize */
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
if (!dsp)
return -ENOMEM;
dsp->num = 1;
dsp->type = WMFW_ADSP2;
dsp->rev = 0;
dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
return cs_dsp_callbacks_test_common_init(test, dsp, 1);
}
static void cs_dsp_callbacks_param_desc(const struct cs_dsp_callbacks_test_param *param,
char *desc)
{
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->case_name);
}
/* Parameterize on different client callback ops tables */
static const struct cs_dsp_callbacks_test_param cs_dsp_callbacks_ops_cases[] = {
{ .ops = &cs_dsp_callback_test_client_ops, .case_name = "all ops" },
};
KUNIT_ARRAY_PARAM(cs_dsp_callbacks_ops,
cs_dsp_callbacks_ops_cases,
cs_dsp_callbacks_param_desc);
static const struct cs_dsp_callbacks_test_param cs_dsp_no_callbacks_cases[] = {
{ .ops = &cs_dsp_callback_test_empty_client_ops, .case_name = "empty ops" },
};
KUNIT_ARRAY_PARAM(cs_dsp_no_callbacks,
cs_dsp_no_callbacks_cases,
cs_dsp_callbacks_param_desc);
static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv1_test_cases[] = {
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v1_callbacks, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
{ } /* terminator */
};
static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv2_test_cases[] = {
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
{ } /* terminator */
};
static struct kunit_case cs_dsp_callbacks_halo_test_cases[] = {
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
{ } /* terminator */
};
static struct kunit_case cs_dsp_watchdog_adsp2v2_test_cases[] = {
KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),
{ } /* terminator */
};
static struct kunit_case cs_dsp_watchdog_halo_test_cases[] = {
KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),
{ } /* terminator */
};
static struct kunit_suite cs_dsp_callbacks_test_halo = {
.name = "cs_dsp_callbacks_halo",
.init = cs_dsp_callbacks_test_halo_init,
.test_cases = cs_dsp_callbacks_halo_test_cases,
};
static struct kunit_suite cs_dsp_callbacks_test_adsp2v2_32bit = {
.name = "cs_dsp_callbacks_adsp2v2_32bit_wmfwv2",
.init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
.test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
};
static struct kunit_suite cs_dsp_callbacks_test_adsp2v1_32bit = {
.name = "cs_dsp_callbacks_adsp2v1_32bit_wmfwv2",
.init = cs_dsp_callbacks_test_adsp2v1_32bit_init,
.test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
};
static struct kunit_suite cs_dsp_callbacks_test_adsp2_16bit = {
.name = "cs_dsp_callbacks_adsp2_16bit_wmfwv1",
.init = cs_dsp_callbacks_test_adsp2_16bit_init,
.test_cases = cs_dsp_callbacks_adsp2_wmfwv1_test_cases,
};
static struct kunit_suite cs_dsp_watchdog_test_adsp2v2_32bit = {
.name = "cs_dsp_watchdog_adsp2v2_32bit",
.init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
.test_cases = cs_dsp_watchdog_adsp2v2_test_cases,
};
static struct kunit_suite cs_dsp_watchdog_test_halo_32bit = {
.name = "cs_dsp_watchdog_halo",
.init = cs_dsp_callbacks_test_halo_init,
.test_cases = cs_dsp_watchdog_halo_test_cases,
};
kunit_test_suites(&cs_dsp_callbacks_test_halo,
&cs_dsp_callbacks_test_adsp2v2_32bit,
&cs_dsp_callbacks_test_adsp2v1_32bit,
&cs_dsp_callbacks_test_adsp2_16bit,
&cs_dsp_watchdog_test_adsp2v2_32bit,
&cs_dsp_watchdog_test_halo_32bit);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: GPL-2.0-only
//
// Utility module for cs_dsp KUnit testing.
//
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <linux/module.h>
MODULE_DESCRIPTION("KUnit tests for Cirrus Logic DSP driver");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("FW_CS_DSP");
MODULE_IMPORT_NS("FW_CS_DSP_KUNIT_TEST_UTILS");

View File

@ -10,6 +10,5 @@
#define AIF3_PB 4
#define AIF3_CAP 5
#define AIF4_PB 6
#define NUM_CODEC_DAIS 7
#endif

View File

@ -0,0 +1,160 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Support utilities for cs_dsp testing.
*
* Copyright (C) 2024 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/regmap.h>
#include <linux/firmware/cirrus/wmfw.h>
struct kunit;
struct cs_dsp_test;
struct cs_dsp_test_local;
/**
* struct cs_dsp_test - base class for test utilities
*
* @test: Pointer to struct kunit instance.
* @dsp: Pointer to struct cs_dsp instance.
* @local: Private data for each test suite.
*/
struct cs_dsp_test {
struct kunit *test;
struct cs_dsp *dsp;
struct cs_dsp_test_local *local;
/* Following members are private */
bool saw_bus_write;
};
/**
* struct cs_dsp_mock_alg_def - Info for creating a mock algorithm entry.
*
* @id Algorithm ID.
* @ver; Algorithm version.
* @xm_base_words XM base address in DSP words.
* @xm_size_words XM size in DSP words.
* @ym_base_words YM base address in DSP words.
* @ym_size_words YM size in DSP words.
* @zm_base_words ZM base address in DSP words.
* @zm_size_words ZM size in DSP words.
*/
struct cs_dsp_mock_alg_def {
unsigned int id;
unsigned int ver;
unsigned int xm_base_words;
unsigned int xm_size_words;
unsigned int ym_base_words;
unsigned int ym_size_words;
unsigned int zm_base_words;
unsigned int zm_size_words;
};
struct cs_dsp_mock_coeff_def {
const char *shortname;
const char *fullname;
const char *description;
u16 type;
u16 flags;
u16 mem_type;
unsigned int offset_dsp_words;
unsigned int length_bytes;
};
/**
* struct cs_dsp_mock_xm_header - XM header builder
*
* @test_priv: Pointer to the struct cs_dsp_test.
* @blob_data: Pointer to the created blob data.
* @blob_size_bytes: Size of the data at blob_data.
*/
struct cs_dsp_mock_xm_header {
struct cs_dsp_test *test_priv;
void *blob_data;
size_t blob_size_bytes;
};
struct cs_dsp_mock_wmfw_builder;
struct cs_dsp_mock_bin_builder;
extern const unsigned int cs_dsp_mock_adsp2_32bit_sysbase;
extern const unsigned int cs_dsp_mock_adsp2_16bit_sysbase;
extern const unsigned int cs_dsp_mock_halo_core_base;
extern const unsigned int cs_dsp_mock_halo_sysinfo_base;
extern const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[];
extern const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[];
extern const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[];
extern const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[];
extern const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[];
extern const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[];
int cs_dsp_mock_count_regions(const unsigned int *region_sizes);
unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type);
unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type);
unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv);
unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type);
unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type);
unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type);
bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv);
int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type);
unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words);
unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
unsigned int alg_id,
int mem_type);
unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv);
unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header);
void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv);
int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header);
struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
const struct cs_dsp_mock_alg_def *algs,
size_t num_algs);
int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv);
void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
unsigned int first_reg, unsigned int last_reg);
void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
unsigned int first_reg, size_t num_regs);
void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
unsigned int first_reg, size_t num_bytes);
void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv);
bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs);
struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
int format_version,
unsigned int fw_version);
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
int type, unsigned int offset,
const void *payload_data, size_t payload_len_bytes);
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
const char *info);
void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
const char *name);
void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
unsigned int alg_id, unsigned int alg_ver,
int mem_region, unsigned int reg_addr_offset,
const void *payload_data, size_t payload_len_bytes);
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder);
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
int format_version);
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
int mem_region, unsigned int mem_offset_dsp_words,
const void *payload_data, size_t payload_len_bytes);
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
const char *info);
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
int mem_region, unsigned int mem_offset_dsp_words,
const void *payload_data, size_t payload_len_bytes);
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
unsigned int alg_id,
const char *name,
const char *description);
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
const struct cs_dsp_mock_coeff_def *def);
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder);
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder);
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder);

View File

@ -9,6 +9,9 @@
#ifndef __SDCA_H__
#define __SDCA_H__
#include <linux/types.h>
#include <linux/kconfig.h>
struct sdw_slave;
#define SDCA_MAX_FUNCTION_COUNT 8
@ -20,9 +23,9 @@ struct sdw_slave;
* @name: human-readable string
*/
struct sdca_function_desc {
u64 adr;
u32 type;
const char *name;
u32 type;
u8 adr;
};
/**

View File

@ -9,6 +9,8 @@
#ifndef __SDCA_FUNCTION_H__
#define __SDCA_FUNCTION_H__
#include <linux/bits.h>
/*
* SDCA Function Types from SDCA specification v1.0a Section 5.1.2
* all Function types not described are reserved
@ -40,6 +42,7 @@ enum sdca_function_type {
#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ"
#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack"
#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
#define SDCA_FUNCTION_TYPE_IMP_DEF_NAME "ImplementationDefined"
enum sdca_entity0_controls {
SDCA_CTL_ENTITY_0_COMMIT_GROUP_MASK = 0x01,

View File

@ -264,9 +264,13 @@ static inline void simple_util_debug_info(struct simple_util_priv *priv)
simple_util_debug_dai(priv, "codec", dai);
if (link->name)
dev_dbg(dev, "dai name = %s\n", link->name);
dev_dbg(dev, "link name = %s\n", link->name);
if (link->dai_fmt)
dev_dbg(dev, "dai format = %04x\n", link->dai_fmt);
dev_dbg(dev, "link format = %04x\n", link->dai_fmt);
if (link->playback_only)
dev_dbg(dev, "link has playback_only");
if (link->capture_only)
dev_dbg(dev, "link has capture_only");
if (props->adata.convert_rate)
dev_dbg(dev, "convert_rate = %d\n", props->adata.convert_rate);
if (props->adata.convert_channels)

View File

@ -1118,7 +1118,6 @@ struct snd_soc_card {
unsigned int instantiated:1;
unsigned int topology_shortname_created:1;
unsigned int fully_routed:1;
unsigned int disable_route_checks:1;
unsigned int probed:1;
unsigned int component_chaining:1;

View File

@ -224,6 +224,8 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,
bool playback);
int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);
/* MAXIM codec support */
int asoc_sdw_maxim_init(struct snd_soc_card *card,

View File

@ -334,6 +334,14 @@ union snd_codec_options {
struct snd_dec_wma wma_d;
struct snd_dec_alac alac_d;
struct snd_dec_ape ape_d;
struct {
__u32 out_sample_rate;
} src_d;
} __attribute__((packed, aligned(4)));
struct snd_codec_desc_src {
__u32 out_sample_rate_min;
__u32 out_sample_rate_max;
} __attribute__((packed, aligned(4)));
/** struct snd_codec_desc - description of codec capabilities
@ -347,6 +355,9 @@ union snd_codec_options {
* @modes: Supported modes. See SND_AUDIOMODE defines
* @formats: Supported formats. See SND_AUDIOSTREAMFORMAT defines
* @min_buffer: Minimum buffer size handled by codec implementation
* @pcm_formats: Output (for decoders) or input (for encoders)
* PCM formats (required to accel mode, 0 for other modes)
* @u_space: union space (for codec dependent data)
* @reserved: reserved for future use
*
* This structure provides a scalar value for profiles, modes and stream
@ -370,7 +381,12 @@ struct snd_codec_desc {
__u32 modes;
__u32 formats;
__u32 min_buffer;
__u32 reserved[15];
__u32 pcm_formats;
union {
__u32 u_space[6];
struct snd_codec_desc_src src;
} __attribute__((packed, aligned(4)));
__u32 reserved[8];
} __attribute__((packed, aligned(4)));
/** struct snd_codec
@ -395,6 +411,8 @@ struct snd_codec_desc {
* @align: Block alignment in bytes of an audio sample.
* Only required for PCM or IEC formats.
* @options: encoder-specific settings
* @pcm_format: Output (for decoders) or input (for encoders)
* PCM formats (required to accel mode, 0 for other modes)
* @reserved: reserved for future use
*/
@ -411,7 +429,8 @@ struct snd_codec {
__u32 format;
__u32 align;
union snd_codec_options options;
__u32 reserved[3];
__u32 pcm_format;
__u32 reserved[2];
} __attribute__((packed, aligned(4)));
#endif

View File

@ -153,6 +153,8 @@
/* Stream */
#define SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3 1200
#define SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3 1201
#define SOF_TKN_STREAM_PLAYBACK_PAUSE_SUPPORTED 1202
#define SOF_TKN_STREAM_CAPTURE_PAUSE_SUPPORTED 1203
/* Led control for mute switches */
#define SOF_TKN_MUTE_LED_USE 1300

View File

@ -105,7 +105,7 @@ config SND_SOC_AMD_ACP6x
config SND_SOC_AMD_YC_MACH
tristate "AMD YC support for DMIC"
select SND_SOC_DMIC
depends on SND_SOC_AMD_ACP6x
depends on SND_SOC_AMD_ACP6x && ACPI
help
This option enables machine driver for Yellow Carp platform
using dmic. ACP IP has PDM Decoder block with DMA controller.

View File

@ -83,6 +83,7 @@ static int acp63_init(void __iomem *acp_base, struct device *dev)
return ret;
}
acp63_enable_interrupts(acp_base);
writel(0, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
}
@ -97,6 +98,7 @@ static int acp63_deinit(void __iomem *acp_base, struct device *dev)
return ret;
}
writel(0, acp_base + ACP_CONTROL);
writel(1, acp_base + ACP_ZSC_DSP_CTRL);
return 0;
}
@ -312,6 +314,7 @@ static struct snd_soc_acpi_mach *acp63_sdw_machine_select(struct device *dev)
if (mach && mach->link_mask) {
mach->mach_params.links = mach->links;
mach->mach_params.link_mask = mach->link_mask;
mach->mach_params.subsystem_rev = acp_data->acp_rev;
return mach;
}
}
@ -669,8 +672,10 @@ static int __maybe_unused snd_acp63_suspend(struct device *dev)
adata = dev_get_drvdata(dev);
if (adata->is_sdw_dev) {
adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
if (adata->sdw_en_stat)
if (adata->sdw_en_stat) {
writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
}
ret = acp63_deinit(adata->acp63_base, dev);
if (ret)
@ -685,9 +690,10 @@ static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat)
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
ret = acp63_init(adata->acp63_base, dev);
if (ret) {
dev_err(dev, "ACP init failed\n");
@ -705,8 +711,10 @@ static int __maybe_unused snd_acp63_resume(struct device *dev)
int ret;
adata = dev_get_drvdata(dev);
if (adata->sdw_en_stat)
if (adata->sdw_en_stat) {
writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
return 0;
}
ret = acp63_init(adata->acp63_base, dev);
if (ret)

View File

@ -692,7 +692,7 @@ config SND_SOC_AW88261
the input amplitude.
config SND_SOC_AW88081
tristate "Soc Audio for awinic aw88081"
tristate "Soc Audio for awinic aw88081/aw88083"
depends on I2C
select REGMAP_I2C
select SND_SOC_AW88395_LIB

View File

@ -80,7 +80,7 @@ snd-soc-cs35l56-shared-y := cs35l56-shared.o
snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
snd-soc-cs35l56-spi-y := cs35l56-spi.o
snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
snd-soc-cs40l50-objs := cs40l50-codec.o
snd-soc-cs40l50-y := cs40l50-codec.o
snd-soc-cs42l42-y := cs42l42.o
snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
snd-soc-cs42l42-sdw-y := cs42l42-sdw.o
@ -92,7 +92,7 @@ snd-soc-cs42l52-y := cs42l52.o
snd-soc-cs42l56-y := cs42l56.o
snd-soc-cs42l73-y := cs42l73.o
snd-soc-cs42l83-i2c-y := cs42l83-i2c.o
snd-soc-cs42l84-objs := cs42l84.o
snd-soc-cs42l84-y := cs42l84.o
snd-soc-cs4234-y := cs4234.o
snd-soc-cs4265-y := cs4265.o
snd-soc-cs4270-y := cs4270.o
@ -334,8 +334,8 @@ snd-soc-wcd-classh-y := wcd-clsh-v2.o
snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
snd-soc-wcd9335-y := wcd9335.o
snd-soc-wcd934x-y := wcd934x.o
snd-soc-wcd937x-objs := wcd937x.o
snd-soc-wcd937x-sdw-objs := wcd937x-sdw.o
snd-soc-wcd937x-y := wcd937x.o
snd-soc-wcd937x-sdw-y := wcd937x-sdw.o
snd-soc-wcd938x-y := wcd938x.o
snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
snd-soc-wcd939x-y := wcd939x.o

View File

@ -23,7 +23,6 @@ MODULE_DEVICE_TABLE(i2c, ad193x_id);
static int ad193x_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
const struct i2c_device_id *id = i2c_match_id(ad193x_id, client);
config = ad193x_regmap_config;
config.val_bits = 8;
@ -31,7 +30,7 @@ static int ad193x_i2c_probe(struct i2c_client *client)
return ad193x_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
(enum ad193x_type)id->driver_data);
(uintptr_t)i2c_get_match_data(client));
}
static struct i2c_driver ad193x_i2c_driver = {

View File

@ -14,12 +14,9 @@
#include "adau1761.h"
static const struct i2c_device_id adau1761_i2c_ids[];
static int adau1761_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client);
config = adau1761_regmap_config;
config.val_bits = 8;
@ -27,7 +24,7 @@ static int adau1761_i2c_probe(struct i2c_client *client)
return adau1761_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
(uintptr_t)i2c_get_match_data(client), NULL);
}
static void adau1761_i2c_remove(struct i2c_client *client)

View File

@ -14,12 +14,9 @@
#include "adau1781.h"
static const struct i2c_device_id adau1781_i2c_ids[];
static int adau1781_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client);
config = adau1781_regmap_config;
config.val_bits = 8;
@ -27,7 +24,7 @@ static int adau1781_i2c_probe(struct i2c_client *client)
return adau1781_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
(uintptr_t)i2c_get_match_data(client), NULL);
}
static void adau1781_i2c_remove(struct i2c_client *client)

View File

@ -14,12 +14,9 @@
#include "adau1977.h"
static const struct i2c_device_id adau1977_i2c_ids[];
static int adau1977_i2c_probe(struct i2c_client *client)
{
struct regmap_config config;
const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client);
config = adau1977_regmap_config;
config.val_bits = 8;
@ -27,7 +24,7 @@ static int adau1977_i2c_probe(struct i2c_client *client)
return adau1977_probe(&client->dev,
devm_regmap_init_i2c(client, &config),
id->driver_data, NULL);
(uintptr_t)i2c_get_match_data(client), NULL);
}
static const struct i2c_device_id adau1977_i2c_ids[] = {

View File

@ -987,9 +987,9 @@ static int alc5623_i2c_probe(struct i2c_client *client)
struct alc5623_priv *alc5623;
struct device_node *np;
unsigned int vid1, vid2;
unsigned int matched_id;
int ret;
u32 val32;
const struct i2c_device_id *id;
alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
GFP_KERNEL);
@ -1016,12 +1016,12 @@ static int alc5623_i2c_probe(struct i2c_client *client)
}
vid2 >>= 8;
id = i2c_match_id(alc5623_i2c_table, client);
matched_id = (uintptr_t)i2c_get_match_data(client);
if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
if ((vid1 != 0x10ec) || (vid2 != matched_id)) {
dev_err(&client->dev, "unknown or wrong codec\n");
dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
0x10ec, id->driver_data,
dev_err(&client->dev, "Expected %x:%x, got %x:%x\n",
0x10ec, matched_id,
vid1, vid2);
return -ENODEV;
}

View File

@ -1108,7 +1108,7 @@ static int alc5632_i2c_probe(struct i2c_client *client)
struct alc5632_priv *alc5632;
int ret, ret1, ret2;
unsigned int vid1, vid2;
const struct i2c_device_id *id;
unsigned int matched_id;
alc5632 = devm_kzalloc(&client->dev,
sizeof(struct alc5632_priv), GFP_KERNEL);
@ -1134,9 +1134,9 @@ static int alc5632_i2c_probe(struct i2c_client *client)
vid2 >>= 8;
id = i2c_match_id(alc5632_i2c_table, client);
matched_id = (uintptr_t)i2c_get_match_data(client);
if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
if ((vid1 != 0x10EC) || (vid2 != matched_id)) {
dev_err(&client->dev,
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
return -EINVAL;

View File

@ -14,13 +14,18 @@
#include "aw88081.h"
#include "aw88395/aw88395_device.h"
enum aw8808x_type {
AW88081,
AW88083,
};
struct aw88081 {
struct aw_device *aw_pa;
struct mutex lock;
struct delayed_work start_work;
struct regmap *regmap;
struct aw_container *aw_cfg;
enum aw8808x_type devtype;
bool phase_sync;
};
@ -32,6 +37,14 @@ static const struct regmap_config aw88081_regmap_config = {
.val_format_endian = REGMAP_ENDIAN_BIG,
};
static const struct regmap_config aw88083_regmap_config = {
.val_bits = 16,
.reg_bits = 8,
.max_register = AW88083_REG_MAX,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
};
static int aw88081_dev_get_iis_status(struct aw_device *aw_dev)
{
unsigned int reg_val;
@ -196,6 +209,41 @@ static void aw88081_dev_amppd(struct aw_device *aw_dev, bool amppd)
~AW88081_EN_PA_MASK, AW88081_EN_PA_WORKING_VALUE);
}
static void aw88083_i2c_wen(struct aw88081 *aw88081, bool flag)
{
struct aw_device *aw_dev = aw88081->aw_pa;
if (aw88081->devtype != AW88083)
return;
if (flag)
regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_ENABLE_VALUE);
else
regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
~AW88083_I2C_WEN_MASK, AW88083_I2C_WEN_DISABLE_VALUE);
}
static void aw88083_dev_amppd(struct aw_device *aw_dev, bool amppd)
{
if (amppd)
regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
~AW88083_AMPPD_MASK, AW88083_AMPPD_POWER_DOWN_VALUE);
else
regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
~AW88083_AMPPD_MASK, AW88083_AMPPD_WORKING_VALUE);
}
static void aw88083_dev_pllpd(struct aw_device *aw_dev, bool pllpd)
{
if (pllpd)
regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
~AW88083_PLL_PD_MASK, AW88083_PLL_PD_WORKING_VALUE);
else
regmap_update_bits(aw_dev->regmap, AW88081_SYSCTRL_REG,
~AW88083_PLL_PD_MASK, AW88083_PLL_PD_POWER_DOWN_VALUE);
}
static void aw88081_dev_clear_int_status(struct aw_device *aw_dev)
{
unsigned int int_status;
@ -284,12 +332,90 @@ static void aw88081_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
AW88081_ULS_HMUTE_DISABLE_VALUE);
}
static int aw88081_dev_reg_value_check(struct aw_device *aw_dev,
unsigned char reg_addr, unsigned short *reg_val)
{
unsigned int read_vol;
if (reg_addr == AW88081_SYSCTRL_REG) {
*reg_val &= ~(~AW88081_EN_PA_MASK |
~AW88081_PWDN_MASK |
~AW88081_HMUTE_MASK |
~AW88081_ULS_HMUTE_MASK);
*reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE |
AW88081_PWDN_POWER_DOWN_VALUE |
AW88081_HMUTE_ENABLE_VALUE |
AW88081_ULS_HMUTE_ENABLE_VALUE;
}
if (reg_addr == AW88081_SYSCTRL2_REG) {
read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
aw_dev->volume_desc.init_volume = read_vol;
}
/* i2stxen */
if (reg_addr == AW88081_I2SCTRL3_REG) {
/* close tx */
*reg_val &= AW88081_I2STXEN_MASK;
*reg_val |= AW88081_I2STXEN_DISABLE_VALUE;
}
return 0;
}
static int aw88083_dev_reg_value_check(struct aw_device *aw_dev,
unsigned char reg_addr, unsigned short *reg_val)
{
unsigned int read_vol;
if (reg_addr == AW88081_SYSCTRL_REG) {
*reg_val &= ~(~AW88083_AMPPD_MASK |
~AW88081_PWDN_MASK |
~AW88081_HMUTE_MASK |
~AW88083_I2C_WEN_MASK);
*reg_val |= AW88083_AMPPD_POWER_DOWN_VALUE |
AW88081_PWDN_POWER_DOWN_VALUE |
AW88081_HMUTE_ENABLE_VALUE |
AW88083_I2C_WEN_ENABLE_VALUE;
}
if (reg_addr == AW88081_SYSCTRL2_REG) {
read_vol = (*reg_val & (~AW88081_VOL_MASK)) >> AW88081_VOL_START_BIT;
aw_dev->volume_desc.init_volume = read_vol;
}
return 0;
}
static int aw88081_reg_value_check(struct aw88081 *aw88081,
unsigned char reg_addr, unsigned short *reg_val)
{
struct aw_device *aw_dev = aw88081->aw_pa;
int ret;
switch (aw88081->devtype) {
case AW88081:
ret = aw88081_dev_reg_value_check(aw_dev, reg_addr, reg_val);
break;
case AW88083:
ret = aw88083_dev_reg_value_check(aw_dev, reg_addr, reg_val);
break;
default:
dev_err(aw_dev->dev, "unsupported device\n");
ret = -EINVAL;
break;
}
return ret;
}
static int aw88081_dev_reg_update(struct aw88081 *aw88081,
unsigned char *data, unsigned int len)
{
struct aw_device *aw_dev = aw88081->aw_pa;
struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
unsigned int read_vol;
int data_len, i, ret;
int16_t *reg_data;
u16 reg_val;
@ -312,30 +438,9 @@ static int aw88081_dev_reg_update(struct aw88081 *aw88081,
reg_addr = reg_data[i];
reg_val = reg_data[i + 1];
if (reg_addr == AW88081_SYSCTRL_REG) {
reg_val &= ~(~AW88081_EN_PA_MASK |
~AW88081_PWDN_MASK |
~AW88081_HMUTE_MASK |
~AW88081_ULS_HMUTE_MASK);
reg_val |= AW88081_EN_PA_POWER_DOWN_VALUE |
AW88081_PWDN_POWER_DOWN_VALUE |
AW88081_HMUTE_ENABLE_VALUE |
AW88081_ULS_HMUTE_ENABLE_VALUE;
}
if (reg_addr == AW88081_SYSCTRL2_REG) {
read_vol = (reg_val & (~AW88081_VOL_MASK)) >>
AW88081_VOL_START_BIT;
aw_dev->volume_desc.init_volume = read_vol;
}
/* i2stxen */
if (reg_addr == AW88081_I2SCTRL3_REG) {
/* close tx */
reg_val &= AW88081_I2STXEN_MASK;
reg_val |= AW88081_I2STXEN_DISABLE_VALUE;
}
ret = aw88081_reg_value_check(aw88081, reg_addr, &reg_val);
if (ret)
return ret;
ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
if (ret)
@ -474,8 +579,60 @@ pll_check_fail:
return ret;
}
static int aw88081_dev_stop(struct aw_device *aw_dev)
static int aw88083_dev_start(struct aw88081 *aw88081)
{
struct aw_device *aw_dev = aw88081->aw_pa;
if (aw_dev->status == AW88081_DEV_PW_ON) {
dev_dbg(aw_dev->dev, "already power on");
return 0;
}
aw88083_i2c_wen(aw88081, true);
/* power on */
aw88081_dev_pwd(aw_dev, false);
usleep_range(AW88081_2000_US, AW88081_2000_US + 10);
aw88083_dev_pllpd(aw_dev, true);
/* amppd on */
aw88083_dev_amppd(aw_dev, false);
usleep_range(AW88081_2000_US, AW88081_2000_US + 50);
/* close mute */
aw88081_dev_mute(aw_dev, false);
aw88083_i2c_wen(aw88081, false);
aw_dev->status = AW88081_DEV_PW_ON;
return 0;
}
static int aw88081_device_start(struct aw88081 *aw88081)
{
int ret;
switch (aw88081->devtype) {
case AW88081:
ret = aw88081_dev_start(aw88081);
break;
case AW88083:
ret = aw88083_dev_start(aw88081);
break;
default:
ret = -EINVAL;
dev_err(aw88081->aw_pa->dev, "unsupported device\n");
break;
}
return ret;
}
static int aw88081_dev_stop(struct aw88081 *aw88081)
{
struct aw_device *aw_dev = aw88081->aw_pa;
if (aw_dev->status == AW88081_DEV_PW_OFF) {
dev_dbg(aw_dev->dev, "already power off");
return 0;
@ -503,6 +660,56 @@ static int aw88081_dev_stop(struct aw_device *aw_dev)
return 0;
}
static int aw88083_dev_stop(struct aw88081 *aw88081)
{
struct aw_device *aw_dev = aw88081->aw_pa;
if (aw_dev->status == AW88081_DEV_PW_OFF) {
dev_dbg(aw_dev->dev, "already power off");
return 0;
}
aw_dev->status = AW88081_DEV_PW_OFF;
aw88083_i2c_wen(aw88081, true);
/* set mute */
aw88081_dev_mute(aw_dev, true);
usleep_range(AW88081_2000_US, AW88081_2000_US + 100);
/* enable amppd */
aw88083_dev_amppd(aw_dev, true);
aw88083_dev_pllpd(aw_dev, false);
/* set power down */
aw88081_dev_pwd(aw_dev, true);
aw88083_i2c_wen(aw88081, false);
return 0;
}
static int aw88081_stop(struct aw88081 *aw88081)
{
int ret;
switch (aw88081->devtype) {
case AW88081:
ret = aw88081_dev_stop(aw88081);
break;
case AW88083:
ret = aw88083_dev_stop(aw88081);
break;
default:
dev_err(aw88081->aw_pa->dev, "unsupported device\n");
ret = -EINVAL;
break;
}
return ret;
}
static int aw88081_reg_update(struct aw88081 *aw88081, bool force)
{
struct aw_device *aw_dev = aw88081->aw_pa;
@ -540,7 +747,7 @@ static void aw88081_start_pa(struct aw88081 *aw88081)
dev_err(aw88081->aw_pa->dev, "fw update failed, cnt:%d\n", i);
continue;
}
ret = aw88081_dev_start(aw88081);
ret = aw88081_device_start(aw88081);
if (ret) {
dev_err(aw88081->aw_pa->dev, "aw88081 device start failed. retry = %d", i);
continue;
@ -745,7 +952,7 @@ static int aw88081_profile_set(struct snd_kcontrol *kcontrol,
}
if (aw88081->aw_pa->status) {
aw88081_dev_stop(aw88081->aw_pa);
aw88081_stop(aw88081);
aw88081_start(aw88081, AW88081_SYNC_START);
}
@ -781,12 +988,16 @@ static int aw88081_volume_set(struct snd_kcontrol *kcontrol,
if (value < mc->min || value > mc->max)
return -EINVAL;
aw88083_i2c_wen(aw88081, true);
if (vol_desc->ctl_volume != value) {
vol_desc->ctl_volume = value;
aw88081_dev_set_volume(aw88081->aw_pa, vol_desc->ctl_volume);
return 1;
}
aw88083_i2c_wen(aw88081, false);
return 0;
}
@ -860,13 +1071,19 @@ static int aw88081_init(struct aw88081 *aw88081, struct i2c_client *i2c, struct
dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
return ret;
}
if (chip_id != AW88081_CHIP_ID) {
switch (chip_id) {
case AW88081_CHIP_ID:
dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
break;
case AW88083_CHIP_ID:
dev_dbg(&i2c->dev, "chip id = 0x%x\n", chip_id);
break;
default:
dev_err(&i2c->dev, "unsupported device");
return -ENXIO;
}
dev_dbg(&i2c->dev, "chip id = %x\n", chip_id);
aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
if (!aw_dev)
return -ENOMEM;
@ -875,7 +1092,7 @@ static int aw88081_init(struct aw88081 *aw88081, struct i2c_client *i2c, struct
aw_dev->i2c = i2c;
aw_dev->regmap = regmap;
aw_dev->dev = &i2c->dev;
aw_dev->chip_id = AW88081_CHIP_ID;
aw_dev->chip_id = chip_id;
aw_dev->acf = NULL;
aw_dev->prof_info.prof_desc = NULL;
aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
@ -912,21 +1129,8 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg
return ret;
}
aw88081_dev_clear_int_status(aw_dev);
aw88081_dev_uls_hmute(aw_dev, true);
aw88081_dev_mute(aw_dev, true);
usleep_range(AW88081_5000_US, AW88081_5000_US + 10);
aw88081_dev_i2s_tx_enable(aw_dev, false);
usleep_range(AW88081_1000_US, AW88081_1000_US + 100);
aw88081_dev_amppd(aw_dev, true);
aw88081_dev_pwd(aw_dev, true);
aw_dev->status = AW88081_DEV_PW_ON;
aw88081_stop(aw88081);
return 0;
}
@ -977,7 +1181,7 @@ static int aw88081_playback_event(struct snd_soc_dapm_widget *w,
aw88081_start(aw88081, AW88081_ASYNC_START);
break;
case SND_SOC_DAPM_POST_PMD:
aw88081_dev_stop(aw88081->aw_pa);
aw88081_stop(aw88081);
break;
default:
break;
@ -1036,8 +1240,17 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88081 = {
.num_controls = ARRAY_SIZE(aw88081_controls),
};
static const struct i2c_device_id aw88081_i2c_id[] = {
{ AW88081_I2C_NAME, AW88081},
{ AW88083_I2C_NAME, AW88083},
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id);
static int aw88081_i2c_probe(struct i2c_client *i2c)
{
const struct regmap_config *regmap_config;
const struct i2c_device_id *id;
struct aw88081 *aw88081;
int ret;
@ -1049,11 +1262,25 @@ static int aw88081_i2c_probe(struct i2c_client *i2c)
if (!aw88081)
return -ENOMEM;
id = i2c_match_id(aw88081_i2c_id, i2c);
aw88081->devtype = id->driver_data;
mutex_init(&aw88081->lock);
i2c_set_clientdata(i2c, aw88081);
aw88081->regmap = devm_regmap_init_i2c(i2c, &aw88081_regmap_config);
switch (aw88081->devtype) {
case AW88081:
regmap_config = &aw88081_regmap_config;
break;
case AW88083:
regmap_config = &aw88083_regmap_config;
break;
default:
return -EINVAL;
}
aw88081->regmap = devm_regmap_init_i2c(i2c, regmap_config);
if (IS_ERR(aw88081->regmap))
return dev_err_probe(&i2c->dev, PTR_ERR(aw88081->regmap),
"failed to init regmap\n");
@ -1068,12 +1295,6 @@ static int aw88081_i2c_probe(struct i2c_client *i2c)
aw88081_dai, ARRAY_SIZE(aw88081_dai));
}
static const struct i2c_device_id aw88081_i2c_id[] = {
{ AW88081_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id);
static struct i2c_driver aw88081_i2c_driver = {
.driver = {
.name = AW88081_I2C_NAME,

View File

@ -231,6 +231,49 @@
#define AW88081_CCO_MUX_BYPASS_VALUE \
(AW88081_CCO_MUX_BYPASS << AW88081_CCO_MUX_START_BIT)
#define AW88083_I2C_WEN_START_BIT (14)
#define AW88083_I2C_WEN_BITS_LEN (2)
#define AW88083_I2C_WEN_MASK \
(~(((1<<AW88083_I2C_WEN_BITS_LEN)-1) << AW88083_I2C_WEN_START_BIT))
#define AW88083_I2C_WEN_DISABLE (0)
#define AW88083_I2C_WEN_DISABLE_VALUE \
(AW88083_I2C_WEN_DISABLE << AW88083_I2C_WEN_START_BIT)
#define AW88083_I2C_WEN_ENABLE (2)
#define AW88083_I2C_WEN_ENABLE_VALUE \
(AW88083_I2C_WEN_ENABLE << AW88083_I2C_WEN_START_BIT)
#define AW88083_PLL_PD_START_BIT (2)
#define AW88083_PLL_PD_BITS_LEN (1)
#define AW88083_PLL_PD_MASK \
(~(((1<<AW88083_PLL_PD_BITS_LEN)-1) << AW88083_PLL_PD_START_BIT))
#define AW88083_PLL_PD_POWER_DOWN (1)
#define AW88083_PLL_PD_POWER_DOWN_VALUE \
(AW88083_PLL_PD_POWER_DOWN << AW88083_PLL_PD_START_BIT)
#define AW88083_PLL_PD_WORKING (0)
#define AW88083_PLL_PD_WORKING_VALUE \
(AW88083_PLL_PD_WORKING << AW88083_PLL_PD_START_BIT)
#define AW88083_AMPPD_START_BIT (1)
#define AW88083_AMPPD_BITS_LEN (1)
#define AW88083_AMPPD_MASK \
(~(((1<<AW88083_AMPPD_BITS_LEN)-1) << AW88083_AMPPD_START_BIT))
#define AW88083_AMPPD_WORKING (0)
#define AW88083_AMPPD_WORKING_VALUE \
(AW88083_AMPPD_WORKING << AW88083_AMPPD_START_BIT)
#define AW88083_AMPPD_POWER_DOWN (1)
#define AW88083_AMPPD_POWER_DOWN_VALUE \
(AW88083_AMPPD_POWER_DOWN << AW88083_AMPPD_START_BIT)
#define AW88083_REG_MAX (0x7D)
#define AW88083_I2C_NAME "aw88083"
#define AW88083_CHIP_ID 0x2407
#define AW88081_START_RETRIES (5)
#define AW88081_START_WORK_DELAY_MS (0)

View File

@ -646,6 +646,12 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
.rates = CS35L56_RATES,
.formats = CS35L56_RX_FORMATS,
},
.symmetric_rate = 1,
.ops = &cs35l56_sdw_dai_ops,
},
{
.name = "cs35l56-sdw1c",
.id = 2,
.capture = {
.stream_name = "SDW1 Capture",
.channels_min = 1,
@ -655,7 +661,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
},
.symmetric_rate = 1,
.ops = &cs35l56_sdw_dai_ops,
}
},
};
static int cs35l56_write_cal(struct cs35l56_private *cs35l56)

View File

@ -12,7 +12,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/find.h>
#include <linux/bitmap.h>
#include <linux/gcd.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>

View File

@ -13,9 +13,9 @@
#include "cs42l51.h"
static struct i2c_device_id cs42l51_i2c_id[] = {
{"cs42l51"},
{}
static const struct i2c_device_id cs42l51_i2c_id[] = {
{ "cs42l51" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);

View File

@ -1087,7 +1087,7 @@ static const struct of_device_id cs42l84_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42l84_of_match);
static const struct i2c_device_id cs42l84_id[] = {
{"cs42l84", 0},
{ "cs42l84" },
{}
};
MODULE_DEVICE_TABLE(i2c, cs42l84_id);

View File

@ -758,7 +758,7 @@ static int es8323_i2c_probe(struct i2c_client *i2c_client)
}
static const struct i2c_device_id es8323_i2c_id[] = {
{ "es8323", 0 },
{ "es8323" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);

View File

@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
@ -3965,7 +3966,7 @@ static int madera_enable_fll(struct madera_fll *fll)
}
madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
already_enabled ? "enabled" : "disabled");
str_enabled_disabled(already_enabled));
if (fll->fout < MADERA_FLL_MIN_FOUT ||
fll->fout > MADERA_FLL_MAX_FOUT) {
@ -4252,7 +4253,7 @@ static int madera_enable_fll_ao(struct madera_fll *fll,
pm_runtime_get_sync(madera->dev);
madera_fll_dbg(fll, "Enabling FLL_AO, initially %s\n",
already_enabled ? "enabled" : "disabled");
str_enabled_disabled(already_enabled));
/* FLL_AO_HOLD must be set before configuring any registers */
regmap_update_bits(fll->madera->regmap,
@ -4576,7 +4577,7 @@ static int madera_fllhj_enable(struct madera_fll *fll)
pm_runtime_get_sync(madera->dev);
madera_fll_dbg(fll, "Enabling FLL, initially %s\n",
already_enabled ? "enabled" : "disabled");
str_enabled_disabled(already_enabled));
/* FLLn_HOLD must be set before configuring any registers */
regmap_update_bits(fll->madera->regmap,

View File

@ -1731,7 +1731,6 @@ MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
static int max98088_i2c_probe(struct i2c_client *i2c)
{
struct max98088_priv *max98088;
const struct i2c_device_id *id;
max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
GFP_KERNEL);
@ -1747,8 +1746,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c)
if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
return PTR_ERR(max98088->mclk);
id = i2c_match_id(max98088_i2c_id, i2c);
max98088->devtype = id->driver_data;
max98088->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98088);
max98088->pdata = i2c->dev.platform_data;

View File

@ -2543,8 +2543,6 @@ MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
static int max98090_i2c_probe(struct i2c_client *i2c)
{
struct max98090_priv *max98090;
const struct acpi_device_id *acpi_id;
kernel_ulong_t driver_data = 0;
int ret;
pr_debug("max98090_i2c_probe\n");
@ -2554,21 +2552,7 @@ static int max98090_i2c_probe(struct i2c_client *i2c)
if (max98090 == NULL)
return -ENOMEM;
if (ACPI_HANDLE(&i2c->dev)) {
acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
&i2c->dev);
if (!acpi_id) {
dev_err(&i2c->dev, "No driver data\n");
return -EINVAL;
}
driver_data = acpi_id->driver_data;
} else {
const struct i2c_device_id *i2c_id =
i2c_match_id(max98090_i2c_id, i2c);
driver_data = i2c_id->driver_data;
}
max98090->devtype = driver_data;
max98090->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98090);
max98090->pdata = i2c->dev.platform_data;

View File

@ -2115,7 +2115,6 @@ static int max98095_i2c_probe(struct i2c_client *i2c)
{
struct max98095_priv *max98095;
int ret;
const struct i2c_device_id *id;
max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv),
GFP_KERNEL);
@ -2131,8 +2130,7 @@ static int max98095_i2c_probe(struct i2c_client *i2c)
return ret;
}
id = i2c_match_id(max98095_i2c_id, i2c);
max98095->devtype = id->driver_data;
max98095->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98095);
max98095->pdata = i2c->dev.platform_data;

View File

@ -454,7 +454,7 @@ static int ntp8835_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ntp8835_i2c_id[] = {
{ "ntp8835", 0 },
{ "ntp8835" },
{}
};
MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id);

View File

@ -371,7 +371,7 @@ static int ntp8918_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ntp8918_i2c_id[] = {
{ "ntp8918", 0 },
{ "ntp8918" },
{}
};
MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);

View File

@ -33,8 +33,7 @@ MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
static int pcm186x_i2c_probe(struct i2c_client *i2c)
{
const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c);
const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
const enum pcm186x_type type = (uintptr_t)i2c_get_match_data(i2c);
int irq = i2c->irq;
struct regmap *regmap;

View File

@ -2059,7 +2059,6 @@ static char *str_to_upper(char *str)
static int pcmdevice_i2c_probe(struct i2c_client *i2c)
{
const struct i2c_device_id *id = i2c_match_id(pcmdevice_i2c_id, i2c);
struct pcmdevice_priv *pcm_dev;
struct device_node *np;
unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES];
@ -2069,7 +2068,7 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c)
if (!pcm_dev)
return -ENOMEM;
pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0;
pcm_dev->chip_id = (uintptr_t)i2c_get_match_data(i2c);
pcm_dev->dev = &i2c->dev;
pcm_dev->client = i2c;

View File

@ -186,6 +186,12 @@ static int rt5682_i2c_probe(struct i2c_client *i2c)
return -ENODEV;
}
regmap_read(rt5682->regmap, RT5682_INT_DEVICE_ID, &val);
if (val == 0x6956) {
dev_dbg(&i2c->dev, "ALC5682I-VE device\n");
rt5682->ve_ic = true;
}
mutex_init(&rt5682->calibrate_mutex);
rt5682_calibrate(rt5682);

View File

@ -395,6 +395,7 @@ bool rt5682_volatile_register(struct device *dev, unsigned int reg)
case RT5682_4BTN_IL_CMD_1:
case RT5682_AJD1_CTRL:
case RT5682_HP_CALIB_CTRL_1:
case RT5682_INT_DEVICE_ID:
case RT5682_DEVICE_ID:
case RT5682_I2C_MODE:
case RT5682_HP_CALIB_CTRL_10:
@ -419,6 +420,7 @@ bool rt5682_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case RT5682_RESET:
case RT5682_INT_DEVICE_ID:
case RT5682_VERSION_ID:
case RT5682_VENDOR_ID:
case RT5682_DEVICE_ID:
@ -3139,7 +3141,10 @@ void rt5682_calibrate(struct rt5682_priv *rt5682)
regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100);
regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800);
regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000);
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005);
if (rt5682->ve_ic)
regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x7005);
else
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005);
regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c);
regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d);
regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321);
@ -3168,7 +3173,10 @@ void rt5682_calibrate(struct rt5682_priv *rt5682)
regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000);
regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000);
regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000);
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
if (rt5682->ve_ic)
regmap_write(rt5682->regmap, RT5682_CHOP_ADC, 0x2005);
else
regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005);
regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4);
regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0c0c);

View File

@ -22,6 +22,7 @@
/* Info */
#define RT5682_RESET 0x0000
#define RT5682_INT_DEVICE_ID 0x00f9
#define RT5682_VERSION_ID 0x00fd
#define RT5682_VENDOR_ID 0x00fe
#define RT5682_DEVICE_ID 0x00ff
@ -1446,6 +1447,7 @@ struct rt5682_priv {
bool hw_init;
bool first_hw_init;
bool is_sdw;
bool ve_ic;
#ifdef CONFIG_COMMON_CLK
struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS];

View File

@ -372,47 +372,6 @@ static const struct regmap_config rt715_sdw_regmap = {
.use_single_write = true,
};
int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload,
unsigned int *sdw_addr_h, unsigned int *sdw_data_h,
unsigned int *sdw_addr_l, unsigned int *sdw_data_l)
{
unsigned int offset_h, offset_l, e_verb;
if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */
if (verb == 0x7ff) /* special case */
offset_h = 0;
else
offset_h = 0x3000;
if (verb & 0x800) /* get command */
e_verb = (verb - 0xf00) | 0x80;
else /* set command */
e_verb = (verb - 0x700);
*sdw_data_h = payload; /* 7 bits payload */
*sdw_addr_l = *sdw_data_l = 0;
} else { /* 4 bits command */
if ((verb & 0x800) == 0x800) { /* read */
offset_h = 0x9000;
offset_l = 0xa000;
} else { /* write */
offset_h = 0x7000;
offset_l = 0x8000;
}
e_verb = verb >> 8;
*sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */
*sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */
*sdw_addr_l += offset_l;
*sdw_data_l = payload & 0xff;
}
*sdw_addr_h = (e_verb << 8) | nid;
*sdw_addr_h += offset_h;
return 0;
}
EXPORT_SYMBOL(hda_to_sdw);
static int rt715_update_status(struct sdw_slave *slave,
enum sdw_slave_status status)
{

View File

@ -220,8 +220,5 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave);
int rt715_init(struct device *dev, struct regmap *sdw_regmap,
struct regmap *regmap, struct sdw_slave *slave);
int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload,
unsigned int *sdw_addr_h, unsigned int *sdw_data_h,
unsigned int *sdw_addr_l, unsigned int *sdw_data_l);
int rt715_clock_config(struct device *dev);
#endif /* __RT715_H__ */

View File

@ -2011,8 +2011,8 @@ static void sma1307_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id sma1307_i2c_id[] = {
{ "sma1307a", 0 },
{ "sma1307aq", 0 },
{ "sma1307a" },
{ "sma1307aq" },
{ }
};

View File

@ -13,8 +13,6 @@
#include "ssm2602.h"
static const struct i2c_device_id ssm2602_i2c_id[];
/*
* ssm2602 2 wire address is determined by GPIO5
* state during powerup.
@ -23,8 +21,7 @@ static const struct i2c_device_id ssm2602_i2c_id[];
*/
static int ssm2602_i2c_probe(struct i2c_client *client)
{
const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client);
return ssm2602_probe(&client->dev, id->driver_data,
return ssm2602_probe(&client->dev, (uintptr_t)i2c_get_match_data(client),
devm_regmap_init_i2c(client, &ssm2602_regmap_config));
}

View File

@ -731,16 +731,14 @@ static int tas2562_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct tas2562_data *data;
int ret;
const struct i2c_device_id *id;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
id = i2c_match_id(tas2562_id, client);
data->client = client;
data->dev = &client->dev;
data->model_id = id->driver_data;
data->model_id = (uintptr_t)i2c_get_match_data(client);
tas2562_parse_dt(data);

View File

@ -489,14 +489,11 @@ static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
int rc = 1;
int i, j;
guard(mutex)(&tas_priv->codec_lock);
if (tas_priv->chip_id != TAS2563) {
rc = -1;
goto out;
}
if (tas_priv->chip_id != TAS2563)
return -1;
for (i = 0; i < tas_priv->ndev; i++) {
struct tasdevice *tasdev = tas_priv->tasdevice;
@ -523,8 +520,8 @@ static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
q[j].val, 4);
}
}
out:
return rc;
return 1;
}
static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
@ -576,7 +573,7 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
struct cali_reg *p = &cali_data->cali_reg_array;
unsigned char *src = ucontrol->value.bytes.data;
unsigned char *dst = cali_data->data;
int rc = 1, i = 0;
int i = 0;
int j;
guard(mutex)(&priv->codec_lock);
@ -605,7 +602,7 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
i += 3;
memcpy(dst, &src[i], cali_data->total_sz);
return rc;
return 1;
}
static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol,
@ -1115,25 +1112,21 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
char *conf_name, *prog_name;
int nr_controls = 4;
int mix_index = 0;
int ret;
/* Alloc kcontrol via devm_kzalloc, which don't manually
* free the kcontrol
*/
dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls,
sizeof(dsp_ctrls[0]), GFP_KERNEL);
if (!dsp_ctrls) {
ret = -ENOMEM;
goto out;
}
if (!dsp_ctrls)
return -ENOMEM;
/* Create mixer items for selecting the active Program and Config */
prog_name = devm_kstrdup(tas_priv->dev, "Speaker Program Id",
GFP_KERNEL);
if (!prog_name) {
ret = -ENOMEM;
goto out;
}
if (!prog_name)
return -ENOMEM;
dsp_ctrls[mix_index].name = prog_name;
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
dsp_ctrls[mix_index].info = tasdevice_info_programs;
@ -1143,10 +1136,9 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
conf_name = devm_kstrdup(tas_priv->dev, "Speaker Config Id",
GFP_KERNEL);
if (!conf_name) {
ret = -ENOMEM;
goto out;
}
if (!conf_name)
return -ENOMEM;
dsp_ctrls[mix_index].name = conf_name;
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
dsp_ctrls[mix_index].info = tasdevice_info_configurations;
@ -1156,10 +1148,9 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
active_dev_num = devm_kstrdup(tas_priv->dev, "Activate Tasdevice Num",
GFP_KERNEL);
if (!active_dev_num) {
ret = -ENOMEM;
goto out;
}
if (!active_dev_num)
return -ENOMEM;
dsp_ctrls[mix_index].name = active_dev_num;
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
dsp_ctrls[mix_index].info = tasdevice_info_active_num;
@ -1168,21 +1159,17 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
mix_index++;
chip_id = devm_kstrdup(tas_priv->dev, "Tasdevice Chip Id", GFP_KERNEL);
if (!chip_id) {
ret = -ENOMEM;
goto out;
}
if (!chip_id)
return -ENOMEM;
dsp_ctrls[mix_index].name = chip_id;
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
dsp_ctrls[mix_index].info = tasdevice_info_chip_id;
dsp_ctrls[mix_index].get = tasdevice_get_chip_id;
mix_index++;
ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
return snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
nr_controls < mix_index ? nr_controls : mix_index);
out:
return ret;
}
static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
@ -1469,7 +1456,6 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream,
unsigned int slot_width;
unsigned int fsrate;
int bclk_rate;
int rc = 0;
fsrate = params_rate(params);
switch (fsrate) {
@ -1479,8 +1465,7 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream,
default:
dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n",
__func__, fsrate);
rc = -EINVAL;
goto out;
return -EINVAL;
}
slot_width = params_width(params);
@ -1493,20 +1478,17 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream,
default:
dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n",
__func__, slot_width);
rc = -EINVAL;
goto out;
return -EINVAL;
}
bclk_rate = snd_soc_params_to_bclk(params);
if (bclk_rate < 0) {
dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n",
__func__, bclk_rate);
rc = bclk_rate;
goto out;
return bclk_rate;
}
out:
return rc;
return 0;
}
static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
@ -1663,7 +1645,6 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv)
static int tasdevice_i2c_probe(struct i2c_client *i2c)
{
const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c);
const struct acpi_device_id *acpi_id;
struct tasdevice_priv *tas_priv;
int ret;
@ -1685,7 +1666,7 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c)
tas_priv->chip_id = acpi_id->driver_data;
tas_priv->isacpi = true;
} else {
tas_priv->chip_id = id ? id->driver_data : 0;
tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c);
tas_priv->isacpi = false;
}

View File

@ -43,7 +43,6 @@ static const char * const tas5720_supply_names[] = {
struct tas5720_data {
struct snd_soc_component *component;
struct regmap *regmap;
struct i2c_client *tas5720_client;
enum tas572x_type devtype;
struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
struct delayed_work fault_check_work;
@ -729,7 +728,6 @@ static int tas5720_probe(struct i2c_client *client)
struct device *dev = &client->dev;
struct tas5720_data *data;
const struct regmap_config *regmap_config;
const struct i2c_device_id *id;
int ret;
int i;
@ -737,11 +735,9 @@ static int tas5720_probe(struct i2c_client *client)
if (!data)
return -ENOMEM;
id = i2c_match_id(tas5720_id, client);
data->tas5720_client = client;
data->devtype = id->driver_data;
data->devtype = (uintptr_t)i2c_get_match_data(client);
switch (id->driver_data) {
switch (data->devtype) {
case TAS5720:
regmap_config = &tas5720_regmap_config;
break;
@ -774,7 +770,7 @@ static int tas5720_probe(struct i2c_client *client)
dev_set_drvdata(dev, data);
switch (id->driver_data) {
switch (data->devtype) {
case TAS5720:
ret = devm_snd_soc_register_component(&client->dev,
&soc_component_dev_tas5720,

View File

@ -1401,7 +1401,6 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct adc3xxx *adc3xxx = NULL;
const struct i2c_device_id *id;
int ret;
adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL);
@ -1466,8 +1465,7 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, adc3xxx);
id = i2c_match_id(adc3xxx_i2c_id, i2c);
adc3xxx->type = id->driver_data;
adc3xxx->type = (uintptr_t)i2c_get_match_data(i2c);
/* Reset codec chip */
gpiod_set_value_cansleep(adc3xxx->rst_pin, 1);

View File

@ -1736,12 +1736,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c)
{
struct aic31xx_priv *aic31xx;
unsigned int micbias_value = MICBIAS_2_0V;
const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c);
int i, ret;
dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
id->name, (int)id->driver_data);
aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
if (!aic31xx)
return -ENOMEM;
@ -1758,7 +1754,7 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c)
aic31xx->dev = &i2c->dev;
aic31xx->irq = i2c->irq;
aic31xx->codec_type = id->driver_data;
aic31xx->codec_type = (uintptr_t)i2c_get_match_data(i2c);
dev_set_drvdata(aic31xx->dev, aic31xx);

View File

@ -31,14 +31,13 @@ static int aic3x_i2c_probe(struct i2c_client *i2c)
{
struct regmap *regmap;
struct regmap_config config;
const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c);
config = aic3x_regmap;
config.reg_bits = 8;
config.val_bits = 8;
regmap = devm_regmap_init_i2c(i2c, &config);
return aic3x_probe(&i2c->dev, regmap, id->driver_data);
return aic3x_probe(&i2c->dev, regmap, (uintptr_t)i2c_get_match_data(i2c));
}
static void aic3x_i2c_remove(struct i2c_client *i2c)

View File

@ -222,7 +222,6 @@ static int tpa6130a2_probe(struct i2c_client *client)
struct tpa6130a2_data *data;
struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
struct device_node *np = client->dev.of_node;
const struct i2c_device_id *id;
const char *regulator;
unsigned int version;
int ret;
@ -251,8 +250,7 @@ static int tpa6130a2_probe(struct i2c_client *client)
i2c_set_clientdata(client, data);
id = i2c_match_id(tpa6130a2_id, client);
data->id = id->driver_data;
data->id = (uintptr_t)i2c_get_match_data(client);
if (data->power_gpio >= 0) {
ret = devm_gpio_request(dev, data->power_gpio,

View File

@ -319,7 +319,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops,
uda1342_suspend, uda1342_resume, NULL);
static const struct i2c_device_id uda1342_i2c_id[] = {
{ "uda1342", 0 },
{ "uda1342" },
{ }
};
MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id);

View File

@ -159,6 +159,8 @@
{"AMIC MUX" #id, "ADC5", "ADC5"}, \
{"AMIC MUX" #id, "ADC6", "ADC6"}
#define NUM_CODEC_DAIS 7
enum {
WCD9335_RX0 = 0,
WCD9335_RX1,

View File

@ -2196,18 +2196,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c)
return ret;
}
if (i2c->dev.of_node) {
const struct of_device_id *match;
match = of_match_node(wm8904_of_match, i2c->dev.of_node);
if (match == NULL)
return -EINVAL;
wm8904->devtype = (uintptr_t)match->data;
} else {
const struct i2c_device_id *id =
i2c_match_id(wm8904_i2c_id, i2c);
wm8904->devtype = id->driver_data;
}
wm8904->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, wm8904);
wm8904->pdata = i2c->dev.platform_data;

View File

@ -1166,12 +1166,10 @@ static struct spi_driver wm8985_spi_driver = {
#endif
#if IS_ENABLED(CONFIG_I2C)
static const struct i2c_device_id wm8985_i2c_id[];
static int wm8985_i2c_probe(struct i2c_client *i2c)
{
struct wm8985_priv *wm8985;
const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c);
int ret;
wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL);
@ -1180,7 +1178,7 @@ static int wm8985_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, wm8985);
wm8985->dev_type = id->driver_data;
wm8985->dev_type = (uintptr_t)i2c_get_match_data(i2c);
wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
if (IS_ERR(wm8985->regmap)) {

View File

@ -8,6 +8,8 @@ config SND_SOC_FSL_ASRC
depends on HAS_DMA
select REGMAP_MMIO
select SND_SOC_GENERIC_DMAENGINE_PCM
select SND_COMPRESS_ACCEL
select SND_COMPRESS_OFFLOAD
help
Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
support for the Freescale CPUs.

View File

@ -10,7 +10,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
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-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.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

View File

@ -932,7 +932,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
if (!asrc_pdev)
priv->card.num_dapm_routes /= 2;
if (of_property_read_bool(np, "audio-routing")) {
if (of_property_present(np, "audio-routing")) {
ret = snd_soc_of_parse_audio_routing(&priv->card, "audio-routing");
if (ret) {
dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);

View File

@ -1063,6 +1063,139 @@ static int fsl_asrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
return REG_ASRDx(dir, index);
}
/* Get sample numbers in FIFO */
static unsigned int fsl_asrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
u32 val;
regmap_read(asrc->regmap, REG_ASRFST(index), &val);
val &= ASRFSTi_OUTPUT_FIFO_MASK;
return val >> ASRFSTi_OUTPUT_FIFO_SHIFT;
}
static int fsl_asrc_m2m_prepare(struct fsl_asrc_pair *pair)
{
struct fsl_asrc_pair_priv *pair_priv = pair->private;
struct fsl_asrc *asrc = pair->asrc;
struct device *dev = &asrc->pdev->dev;
struct asrc_config config;
int ret;
/* fill config */
config.pair = pair->index;
config.channel_num = pair->channels;
config.input_sample_rate = pair->rate[IN];
config.output_sample_rate = pair->rate[OUT];
config.input_format = pair->sample_format[IN];
config.output_format = pair->sample_format[OUT];
config.inclk = INCLK_NONE;
config.outclk = OUTCLK_ASRCK1_CLK;
pair_priv->config = &config;
ret = fsl_asrc_config_pair(pair, true);
if (ret) {
dev_err(dev, "failed to config pair: %d\n", ret);
return ret;
}
pair->first_convert = 1;
return 0;
}
static int fsl_asrc_m2m_start(struct fsl_asrc_pair *pair)
{
if (pair->first_convert) {
fsl_asrc_start_pair(pair);
pair->first_convert = 0;
}
/*
* Clear DMA request during the stall state of ASRC:
* During STALL state, the remaining in input fifo would never be
* smaller than the input threshold while the output fifo would not
* be bigger than output one. Thus the DMA request would be cleared.
*/
fsl_asrc_set_watermarks(pair, ASRC_FIFO_THRESHOLD_MIN,
ASRC_FIFO_THRESHOLD_MAX);
/* Update the real input threshold to raise DMA request */
fsl_asrc_set_watermarks(pair, ASRC_M2M_INPUTFIFO_WML,
ASRC_M2M_OUTPUTFIFO_WML);
return 0;
}
static int fsl_asrc_m2m_stop(struct fsl_asrc_pair *pair)
{
if (!pair->first_convert) {
fsl_asrc_stop_pair(pair);
pair->first_convert = 1;
}
return 0;
}
/* calculate capture data length according to output data length and sample rate */
static int fsl_asrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
{
unsigned int in_width, out_width;
unsigned int channels = pair->channels;
unsigned int in_samples, out_samples;
unsigned int out_length;
in_width = snd_pcm_format_physical_width(pair->sample_format[IN]) / 8;
out_width = snd_pcm_format_physical_width(pair->sample_format[OUT]) / 8;
in_samples = input_buffer_length / in_width / channels;
out_samples = pair->rate[OUT] * in_samples / pair->rate[IN];
out_length = (out_samples - ASRC_OUTPUT_LAST_SAMPLE) * out_width * channels;
return out_length;
}
static int fsl_asrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc = pair->asrc;
struct fsl_asrc_priv *asrc_priv = asrc->private;
int wml = (dir == IN) ? ASRC_M2M_INPUTFIFO_WML : ASRC_M2M_OUTPUTFIFO_WML;
if (!asrc_priv->soc->use_edma)
return wml * pair->channels;
else
return 1;
}
static int fsl_asrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
{
cap->fmt_in = FSL_ASRC_FORMATS;
cap->fmt_out = FSL_ASRC_FORMATS | SNDRV_PCM_FMTBIT_S8;
cap->rate_in = supported_asrc_rate;
cap->rate_in_count = ARRAY_SIZE(supported_asrc_rate);
cap->rate_out = supported_asrc_rate;
cap->rate_out_count = ARRAY_SIZE(supported_asrc_rate);
cap->chan_min = 1;
cap->chan_max = 10;
return 0;
}
static int fsl_asrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc = pair->asrc;
int i;
for (i = 0; i < pair->channels * 4; i++)
regmap_write(asrc->regmap, REG_ASRDI(pair->index), 0);
pair->first_convert = 1;
return 0;
}
static int fsl_asrc_runtime_resume(struct device *dev);
static int fsl_asrc_runtime_suspend(struct device *dev);
@ -1147,6 +1280,15 @@ static int fsl_asrc_probe(struct platform_device *pdev)
asrc->get_fifo_addr = fsl_asrc_get_fifo_addr;
asrc->pair_priv_size = sizeof(struct fsl_asrc_pair_priv);
asrc->m2m_prepare = fsl_asrc_m2m_prepare;
asrc->m2m_start = fsl_asrc_m2m_start;
asrc->m2m_stop = fsl_asrc_m2m_stop;
asrc->get_output_fifo_size = fsl_asrc_get_output_fifo_size;
asrc->m2m_calc_out_len = fsl_asrc_m2m_calc_out_len;
asrc->m2m_get_maxburst = fsl_asrc_m2m_get_maxburst;
asrc->m2m_pair_resume = fsl_asrc_m2m_pair_resume;
asrc->m2m_get_cap = fsl_asrc_m2m_get_cap;
if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
asrc_priv->clk_map[IN] = input_clk_map_imx35;
asrc_priv->clk_map[OUT] = output_clk_map_imx35;
@ -1242,6 +1384,12 @@ static int fsl_asrc_probe(struct platform_device *pdev)
goto err_pm_get_sync;
}
ret = fsl_asrc_m2m_init(asrc);
if (ret) {
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
return ret;
}
return 0;
err_pm_get_sync:
@ -1254,6 +1402,10 @@ err_pm_disable:
static void fsl_asrc_remove(struct platform_device *pdev)
{
struct fsl_asrc *asrc = dev_get_drvdata(&pdev->dev);
fsl_asrc_m2m_exit(asrc);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
fsl_asrc_runtime_suspend(&pdev->dev);
@ -1355,10 +1507,29 @@ static int fsl_asrc_runtime_suspend(struct device *dev)
return 0;
}
static int fsl_asrc_suspend(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
int ret;
fsl_asrc_m2m_suspend(asrc);
ret = pm_runtime_force_suspend(dev);
return ret;
}
static int fsl_asrc_resume(struct device *dev)
{
struct fsl_asrc *asrc = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_force_resume(dev);
fsl_asrc_m2m_resume(asrc);
return ret;
}
static const struct dev_pm_ops fsl_asrc_pm = {
SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
};
static const struct fsl_asrc_soc_data fsl_asrc_imx35_data = {
@ -1396,7 +1567,7 @@ static struct platform_driver fsl_asrc_driver = {
.driver = {
.name = "fsl-asrc",
.of_match_table = fsl_asrc_ids,
.pm = &fsl_asrc_pm,
.pm = pm_ptr(&fsl_asrc_pm),
},
};
module_platform_driver(fsl_asrc_driver);

View File

@ -12,6 +12,8 @@
#include "fsl_asrc_common.h"
#define ASRC_M2M_INPUTFIFO_WML 0x4
#define ASRC_M2M_OUTPUTFIFO_WML 0x2
#define ASRC_DMA_BUFFER_NUM 2
#define ASRC_INPUTFIFO_THRESHOLD 32
#define ASRC_OUTPUTFIFO_THRESHOLD 32

View File

@ -21,6 +21,26 @@ enum asrc_pair_index {
#define PAIR_CTX_NUM 0x4
/**
* struct fsl_asrc_m2m_cap - capability data
* @fmt_in: input sample format
* @fmt_out: output sample format
* @chan_min: minimum channel number
* @chan_max: maximum channel number
* @rate_in: minimum rate
* @rate_out: maximum rete
*/
struct fsl_asrc_m2m_cap {
u64 fmt_in;
u64 fmt_out;
int chan_min;
int chan_max;
const unsigned int *rate_in;
int rate_in_count;
const unsigned int *rate_out;
int rate_out_count;
};
/**
* fsl_asrc_pair: ASRC Pair common data
*
@ -34,6 +54,14 @@ enum asrc_pair_index {
* @pos: hardware pointer position
* @req_dma_chan: flag to release dev_to_dev chan
* @private: pair private area
* @complete: dma task complete
* @sample_format: format of m2m
* @rate: rate of m2m
* @buf_len: buffer length of m2m
* @dma_buffer: buffer pointers
* @first_convert: start of conversion
* @ratio_mod_flag: flag for new ratio modifier
* @ratio_mod: ratio modification
*/
struct fsl_asrc_pair {
struct fsl_asrc *asrc;
@ -49,6 +77,16 @@ struct fsl_asrc_pair {
bool req_dma_chan;
void *private;
/* used for m2m */
struct completion complete[2];
snd_pcm_format_t sample_format[2];
unsigned int rate[2];
unsigned int buf_len[2];
struct snd_dma_buffer dma_buffer[2];
unsigned int first_convert;
bool ratio_mod_flag;
unsigned int ratio_mod;
};
/**
@ -62,6 +100,7 @@ struct fsl_asrc_pair {
* @mem_clk: clock source to access register
* @ipg_clk: clock source to drive peripheral
* @spba_clk: SPBA clock (optional, depending on SoC design)
* @card: compress sound card
* @lock: spin lock for resource protection
* @pair: pair pointers
* @channel_avail: non-occupied channel numbers
@ -72,6 +111,17 @@ struct fsl_asrc_pair {
* @request_pair: function pointer
* @release_pair: function pointer
* @get_fifo_addr: function pointer
* @m2m_get_cap: function pointer
* @m2m_prepare: function pointer
* @m2m_start: function pointer
* @m2m_unprepare: function pointer
* @m2m_stop: function pointer
* @m2m_calc_out_len: function pointer
* @m2m_get_maxburst: function pointer
* @m2m_pair_suspend: function pointer
* @m2m_pair_resume: function pointer
* @m2m_set_ratio_mod: function pointer
* @get_output_fifo_size: function pointer
* @pair_priv_size: size of pair private struct.
* @private: private data structure
*/
@ -84,6 +134,7 @@ struct fsl_asrc {
struct clk *mem_clk;
struct clk *ipg_clk;
struct clk *spba_clk;
struct snd_card *card;
spinlock_t lock; /* spin lock for resource protection */
struct fsl_asrc_pair *pair[PAIR_CTX_NUM];
@ -97,6 +148,20 @@ struct fsl_asrc {
int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
void (*release_pair)(struct fsl_asrc_pair *pair);
int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index);
int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap);
int (*m2m_prepare)(struct fsl_asrc_pair *pair);
int (*m2m_start)(struct fsl_asrc_pair *pair);
int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
int (*m2m_stop)(struct fsl_asrc_pair *pair);
int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair);
int (*m2m_pair_resume)(struct fsl_asrc_pair *pair);
int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val);
unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair);
size_t pair_priv_size;
void *private;
@ -105,4 +170,9 @@ struct fsl_asrc {
#define DRV_NAME "fsl-asrc-dai"
extern struct snd_soc_component_driver fsl_asrc_component;
int fsl_asrc_m2m_init(struct fsl_asrc *asrc);
void fsl_asrc_m2m_exit(struct fsl_asrc *asrc);
int fsl_asrc_m2m_resume(struct fsl_asrc *asrc);
int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc);
#endif /* _FSL_ASRC_COMMON_H */

View File

@ -0,0 +1,727 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
// Copyright (C) 2019-2024 NXP
//
// Freescale ASRC Memory to Memory (M2M) driver
#include <linux/dma/imx-dma.h>
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <sound/asound.h>
#include <sound/dmaengine_pcm.h>
#include <sound/initval.h>
#include "fsl_asrc_common.h"
#define DIR_STR(dir) (dir) == IN ? "in" : "out"
#define ASRC_xPUT_DMA_CALLBACK(dir) \
(((dir) == IN) ? asrc_input_dma_callback \
: asrc_output_dma_callback)
/* Maximum output and capture buffer size */
#define ASRC_M2M_BUFFER_SIZE (512 * 1024)
/* Maximum output and capture period size */
#define ASRC_M2M_PERIOD_SIZE (48 * 1024)
/* dma complete callback */
static void asrc_input_dma_callback(void *data)
{
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
complete(&pair->complete[IN]);
}
/* dma complete callback */
static void asrc_output_dma_callback(void *data)
{
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
complete(&pair->complete[OUT]);
}
/**
*asrc_read_last_fifo: read all the remaining data from FIFO
*@pair: Structure pointer of fsl_asrc_pair
*@dma_vaddr: virtual address of capture buffer
*@length: payload length of capture buffer
*/
static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length)
{
struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
u32 i, reg, size, t_size = 0, width;
u32 *reg32 = NULL;
u16 *reg16 = NULL;
u8 *reg24 = NULL;
width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
if (width == 32)
reg32 = dma_vaddr + *length;
else if (width == 16)
reg16 = dma_vaddr + *length;
else
reg24 = dma_vaddr + *length;
retry:
size = asrc->get_output_fifo_size(pair);
if (size + *length > ASRC_M2M_BUFFER_SIZE)
goto end;
for (i = 0; i < size * pair->channels; i++) {
regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), &reg);
if (reg32) {
*reg32++ = reg;
} else if (reg16) {
*reg16++ = (u16)reg;
} else {
*reg24++ = (u8)reg;
*reg24++ = (u8)(reg >> 8);
*reg24++ = (u8)(reg >> 16);
}
}
t_size += size;
/* In case there is data left in FIFO */
if (size)
goto retry;
end:
/* Update payload length */
if (reg32)
*length += t_size * pair->channels * 4;
else if (reg16)
*length += t_size * pair->channels * 2;
else
*length += t_size * pair->channels * 3;
}
/* config dma channel */
static int asrc_dmaconfig(struct fsl_asrc_pair *pair,
struct dma_chan *chan,
u32 dma_addr, dma_addr_t buf_addr, u32 buf_len,
int dir, int width)
{
struct fsl_asrc *asrc = pair->asrc;
struct device *dev = &asrc->pdev->dev;
struct dma_slave_config slave_config;
enum dma_slave_buswidth buswidth;
unsigned int sg_len, max_period_size;
struct scatterlist *sg;
int ret, i;
switch (width) {
case 8:
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
break;
case 16:
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
break;
case 24:
buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
break;
case 32:
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
break;
default:
dev_err(dev, "invalid word width\n");
return -EINVAL;
}
memset(&slave_config, 0, sizeof(slave_config));
if (dir == IN) {
slave_config.direction = DMA_MEM_TO_DEV;
slave_config.dst_addr = dma_addr;
slave_config.dst_addr_width = buswidth;
slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair);
} else {
slave_config.direction = DMA_DEV_TO_MEM;
slave_config.src_addr = dma_addr;
slave_config.src_addr_width = buswidth;
slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair);
}
ret = dmaengine_slave_config(chan, &slave_config);
if (ret) {
dev_err(dev, "failed to config dmaengine for %s task: %d\n",
DIR_STR(dir), ret);
return -EINVAL;
}
max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8);
/* scatter gather mode */
sg_len = buf_len / max_period_size;
if (buf_len % max_period_size)
sg_len += 1;
sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
if (!sg)
return -ENOMEM;
sg_init_table(sg, sg_len);
for (i = 0; i < (sg_len - 1); i++) {
sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
sg_dma_len(&sg[i]) = max_period_size;
}
sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
sg_dma_len(&sg[i]) = buf_len - i * max_period_size;
pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len,
slave_config.direction,
DMA_PREP_INTERRUPT);
kfree(sg);
if (!pair->desc[dir]) {
dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir));
return -EINVAL;
}
pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
pair->desc[dir]->callback_param = pair;
return 0;
}
/* main function of converter */
static void asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task)
{
struct fsl_asrc *asrc = pair->asrc;
struct device *dev = &asrc->pdev->dev;
enum asrc_pair_index index = pair->index;
struct snd_dma_buffer *src_buf, *dst_buf;
unsigned int in_buf_len;
unsigned int out_dma_len;
unsigned int width;
u32 fifo_addr;
int ret;
/* set ratio mod */
if (asrc->m2m_set_ratio_mod) {
if (pair->ratio_mod_flag) {
asrc->m2m_set_ratio_mod(pair, pair->ratio_mod);
pair->ratio_mod_flag = false;
}
}
src_buf = &pair->dma_buffer[IN];
dst_buf = &pair->dma_buffer[OUT];
width = snd_pcm_format_physical_width(pair->sample_format[IN]);
fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
in_buf_len = task->input_size;
if (in_buf_len < width * pair->channels / 8 ||
in_buf_len > ASRC_M2M_BUFFER_SIZE ||
in_buf_len % (width * pair->channels / 8)) {
dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len);
goto end;
}
/* dma config for output dma channel */
ret = asrc_dmaconfig(pair,
pair->dma_chan[IN],
fifo_addr,
src_buf->addr,
in_buf_len, IN, width);
if (ret) {
dev_err(dev, "out dma config error\n");
goto end;
}
width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len);
if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) {
/* dma config for capture dma channel */
ret = asrc_dmaconfig(pair,
pair->dma_chan[OUT],
fifo_addr,
dst_buf->addr,
out_dma_len, OUT, width);
if (ret) {
dev_err(dev, "cap dma config error\n");
goto end;
}
} else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) {
dev_err(dev, "cap buffer size error\n");
goto end;
}
reinit_completion(&pair->complete[IN]);
reinit_completion(&pair->complete[OUT]);
/* Submit DMA request */
dmaengine_submit(pair->desc[IN]);
dma_async_issue_pending(pair->desc[IN]->chan);
if (out_dma_len > 0) {
dmaengine_submit(pair->desc[OUT]);
dma_async_issue_pending(pair->desc[OUT]->chan);
}
asrc->m2m_start(pair);
if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
dev_err(dev, "out DMA task timeout\n");
goto end;
}
if (out_dma_len > 0) {
if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) {
dev_err(dev, "cap DMA task timeout\n");
goto end;
}
}
/* read the last words from FIFO */
asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len);
/* update payload length for capture */
task->output_size = out_dma_len;
end:
return;
}
static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
{
struct fsl_asrc *asrc = stream->private_data;
struct snd_compr_runtime *runtime = stream->runtime;
struct device *dev = &asrc->pdev->dev;
struct fsl_asrc_pair *pair;
int size, ret;
pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
if (!pair)
return -ENOMEM;
pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
pair->asrc = asrc;
init_completion(&pair->complete[IN]);
init_completion(&pair->complete[OUT]);
runtime->private_data = pair;
size = ASRC_M2M_BUFFER_SIZE;
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]);
if (ret)
goto error_alloc_in_buf;
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]);
if (ret)
goto error_alloc_out_buf;
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
dev_err(dev, "Failed to power up asrc\n");
goto err_pm_runtime;
}
return 0;
err_pm_runtime:
snd_dma_free_pages(&pair->dma_buffer[OUT]);
error_alloc_out_buf:
snd_dma_free_pages(&pair->dma_buffer[IN]);
error_alloc_in_buf:
kfree(pair);
return ret;
}
static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream)
{
struct fsl_asrc *asrc = stream->private_data;
struct snd_compr_runtime *runtime = stream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct device *dev = &asrc->pdev->dev;
pm_runtime_put_sync(dev);
snd_dma_free_pages(&pair->dma_buffer[IN]);
snd_dma_free_pages(&pair->dma_buffer[OUT]);
kfree(runtime->private_data);
return 0;
}
static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream,
struct snd_compr_params *params)
{
struct fsl_asrc *asrc = stream->private_data;
struct snd_compr_runtime *runtime = stream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct fsl_asrc_m2m_cap cap;
int ret, i;
ret = asrc->m2m_get_cap(&cap);
if (ret)
return -EINVAL;
if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in)
pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format;
else
return -EINVAL;
if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out)
pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format;
else
return -EINVAL;
/* check input rate is in scope */
for (i = 0; i < cap.rate_in_count; i++)
if (params->codec.sample_rate == cap.rate_in[i]) {
pair->rate[IN] = params->codec.sample_rate;
break;
}
if (i == cap.rate_in_count)
return -EINVAL;
/* check output rate is in scope */
for (i = 0; i < cap.rate_out_count; i++)
if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) {
pair->rate[OUT] = params->codec.options.src_d.out_sample_rate;
break;
}
if (i == cap.rate_out_count)
return -EINVAL;
if (params->codec.ch_in != params->codec.ch_out ||
params->codec.ch_in < cap.chan_min ||
params->codec.ch_in > cap.chan_max)
return -EINVAL;
pair->channels = params->codec.ch_in;
pair->buf_len[IN] = params->buffer.fragment_size;
pair->buf_len[OUT] = params->buffer.fragment_size;
return 0;
}
static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
{
struct snd_dma_buffer *dmab = dmabuf->priv;
return snd_dma_buffer_mmap(dmab, vma);
}
static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment,
enum dma_data_direction direction)
{
struct snd_dma_buffer *dmab = attachment->dmabuf->priv;
struct sg_table *sgt;
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return NULL;
if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0)
goto free;
if (dma_map_sgtable(attachment->dev, sgt, direction, 0))
goto free;
return sgt;
free:
sg_free_table(sgt);
kfree(sgt);
return NULL;
}
static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment,
struct sg_table *table,
enum dma_data_direction direction)
{
dma_unmap_sgtable(attachment->dev, table, direction, 0);
}
static void fsl_asrc_m2m_release(struct dma_buf *dmabuf)
{
/* buffer is released by fsl_asrc_m2m_comp_release() */
}
static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = {
.mmap = fsl_asrc_m2m_mmap,
.map_dma_buf = fsl_asrc_m2m_map_dma_buf,
.unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf,
.release = fsl_asrc_m2m_release,
};
static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in);
DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out);
struct fsl_asrc *asrc = stream->private_data;
struct snd_compr_runtime *runtime = stream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct device *dev = &asrc->pdev->dev;
int ret;
exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
exp_info_in.size = ASRC_M2M_BUFFER_SIZE;
exp_info_in.flags = O_RDWR;
exp_info_in.priv = &pair->dma_buffer[IN];
task->input = dma_buf_export(&exp_info_in);
if (IS_ERR(task->input)) {
ret = PTR_ERR(task->input);
return ret;
}
exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops;
exp_info_out.size = ASRC_M2M_BUFFER_SIZE;
exp_info_out.flags = O_RDWR;
exp_info_out.priv = &pair->dma_buffer[OUT];
task->output = dma_buf_export(&exp_info_out);
if (IS_ERR(task->output)) {
ret = PTR_ERR(task->output);
return ret;
}
/* Request asrc pair/context */
ret = asrc->request_pair(pair->channels, pair);
if (ret) {
dev_err(dev, "failed to request pair: %d\n", ret);
goto err_request_pair;
}
ret = asrc->m2m_prepare(pair);
if (ret) {
dev_err(dev, "failed to start pair part one: %d\n", ret);
goto err_start_part_one;
}
/* Request dma channels */
pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
if (!pair->dma_chan[IN]) {
dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
ret = -EBUSY;
goto err_dma_channel_in;
}
pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
if (!pair->dma_chan[OUT]) {
dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
ret = -EBUSY;
goto err_dma_channel_out;
}
return 0;
err_dma_channel_out:
dma_release_channel(pair->dma_chan[IN]);
err_dma_channel_in:
if (asrc->m2m_unprepare)
asrc->m2m_unprepare(pair);
err_start_part_one:
asrc->release_pair(pair);
err_request_pair:
return ret;
}
static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
struct snd_compr_runtime *runtime = stream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
asrc_m2m_device_run(pair, task);
return 0;
}
static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
return 0;
}
static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream,
struct snd_compr_task_runtime *task)
{
struct fsl_asrc *asrc = stream->private_data;
struct snd_compr_runtime *runtime = stream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
/* Stop & release pair/context */
if (asrc->m2m_stop)
asrc->m2m_stop(pair);
if (asrc->m2m_unprepare)
asrc->m2m_unprepare(pair);
asrc->release_pair(pair);
/* Release dma channel */
if (pair->dma_chan[IN])
dma_release_channel(pair->dma_chan[IN]);
if (pair->dma_chan[OUT])
dma_release_channel(pair->dma_chan[OUT]);
return 0;
}
static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream,
struct snd_compr_caps *caps)
{
caps->num_codecs = 1;
caps->min_fragment_size = 4096;
caps->max_fragment_size = 4096;
caps->min_fragments = 1;
caps->max_fragments = 1;
caps->codecs[0] = SND_AUDIOCODEC_PCM;
return 0;
}
static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
struct snd_compr_codec_caps *codec)
{
struct fsl_asrc_m2m_cap cap;
snd_pcm_format_t k;
int j = 0;
int ret;
ret = asrc->m2m_get_cap(&cap);
if (ret)
return -EINVAL;
pcm_for_each_format(k) {
if (pcm_format_to_bits(k) & cap.fmt_in) {
codec->descriptor[j].max_ch = cap.chan_max;
memcpy(codec->descriptor[j].sample_rates,
cap.rate_in,
cap.rate_in_count * sizeof(__u32));
codec->descriptor[j].num_sample_rates = cap.rate_in_count;
codec->descriptor[j].formats = (__force __u32)k;
codec->descriptor[j].pcm_formats = cap.fmt_out;
codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0];
codec->descriptor[j].src.out_sample_rate_max =
cap.rate_out[cap.rate_out_count - 1];
j++;
}
}
codec->codec = SND_AUDIOCODEC_PCM;
codec->num_descriptors = j;
return 0;
}
static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec)
{
struct fsl_asrc *asrc = stream->private_data;
return fsl_asrc_m2m_fill_codec_caps(asrc, codec);
}
static struct snd_compr_ops fsl_asrc_m2m_compr_ops = {
.open = fsl_asrc_m2m_comp_open,
.free = fsl_asrc_m2m_comp_release,
.set_params = fsl_asrc_m2m_comp_set_params,
.get_caps = fsl_asrc_m2m_get_caps,
.get_codec_caps = fsl_asrc_m2m_get_codec_caps,
.task_create = fsl_asrc_m2m_comp_task_create,
.task_start = fsl_asrc_m2m_comp_task_start,
.task_stop = fsl_asrc_m2m_comp_task_stop,
.task_free = fsl_asrc_m2m_comp_task_free,
};
int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc)
{
struct fsl_asrc_pair *pair;
int i;
for (i = 0; i < PAIR_CTX_NUM; i++) {
pair = asrc->pair[i];
if (!pair)
continue;
if (!completion_done(&pair->complete[IN])) {
if (pair->dma_chan[IN])
dmaengine_terminate_all(pair->dma_chan[IN]);
asrc_input_dma_callback((void *)pair);
}
if (!completion_done(&pair->complete[OUT])) {
if (pair->dma_chan[OUT])
dmaengine_terminate_all(pair->dma_chan[OUT]);
asrc_output_dma_callback((void *)pair);
}
if (asrc->m2m_pair_suspend)
asrc->m2m_pair_suspend(pair);
}
return 0;
}
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend);
int fsl_asrc_m2m_resume(struct fsl_asrc *asrc)
{
struct fsl_asrc_pair *pair;
int i;
for (i = 0; i < PAIR_CTX_NUM; i++) {
pair = asrc->pair[i];
if (!pair)
continue;
if (asrc->m2m_pair_resume)
asrc->m2m_pair_resume(pair);
}
return 0;
}
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume);
int fsl_asrc_m2m_init(struct fsl_asrc *asrc)
{
struct device *dev = &asrc->pdev->dev;
struct snd_card *card;
struct snd_compr *compr;
int ret;
ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
THIS_MODULE, 0, &card);
if (ret < 0)
return ret;
strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver));
strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname));
strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname));
asrc->card = card;
compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL);
if (!compr) {
ret = -ENOMEM;
goto err;
}
compr->ops = &fsl_asrc_m2m_compr_ops;
compr->private_data = asrc;
ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr);
if (ret < 0)
goto err;
ret = snd_card_register(card);
if (ret < 0)
goto err;
return 0;
err:
snd_card_free(card);
return ret;
}
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init);
void fsl_asrc_m2m_exit(struct fsl_asrc *asrc)
{
struct snd_card *card = asrc->card;
snd_card_free(card);
}
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit);
MODULE_IMPORT_NS("DMA_BUF");
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
MODULE_DESCRIPTION("Freescale ASRC M2M driver");
MODULE_LICENSE("GPL");

View File

@ -1861,6 +1861,224 @@ static int fsl_easrc_get_fifo_addr(u8 dir, enum asrc_pair_index index)
return REG_EASRC_FIFO(dir, index);
}
/* Get sample numbers in FIFO */
static unsigned int fsl_easrc_get_output_fifo_size(struct fsl_asrc_pair *pair)
{
struct fsl_asrc *asrc = pair->asrc;
enum asrc_pair_index index = pair->index;
u32 val;
regmap_read(asrc->regmap, REG_EASRC_SFS(index), &val);
val &= EASRC_SFS_NSGO_MASK;
return val >> EASRC_SFS_NSGO_SHIFT;
}
static int fsl_easrc_m2m_prepare(struct fsl_asrc_pair *pair)
{
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
struct fsl_asrc *asrc = pair->asrc;
struct device *dev = &asrc->pdev->dev;
int ret;
ctx_priv->in_params.sample_rate = pair->rate[IN];
ctx_priv->in_params.sample_format = pair->sample_format[IN];
ctx_priv->out_params.sample_rate = pair->rate[OUT];
ctx_priv->out_params.sample_format = pair->sample_format[OUT];
ctx_priv->in_params.fifo_wtmk = FSL_EASRC_INPUTFIFO_WML;
ctx_priv->out_params.fifo_wtmk = FSL_EASRC_OUTPUTFIFO_WML;
/* Fill the right half of the re-sampler with zeros */
ctx_priv->rs_init_mode = 0x2;
/* Zero fill the right half of the prefilter */
ctx_priv->pf_init_mode = 0x2;
ret = fsl_easrc_set_ctx_format(pair,
&ctx_priv->in_params.sample_format,
&ctx_priv->out_params.sample_format);
if (ret) {
dev_err(dev, "failed to set context format: %d\n", ret);
return ret;
}
ret = fsl_easrc_config_context(asrc, pair->index);
if (ret) {
dev_err(dev, "failed to config context %d\n", ret);
return ret;
}
ctx_priv->in_params.iterations = 1;
ctx_priv->in_params.group_len = pair->channels;
ctx_priv->in_params.access_len = pair->channels;
ctx_priv->out_params.iterations = 1;
ctx_priv->out_params.group_len = pair->channels;
ctx_priv->out_params.access_len = pair->channels;
ret = fsl_easrc_set_ctx_organziation(pair);
if (ret) {
dev_err(dev, "failed to set fifo organization\n");
return ret;
}
/* The context start flag */
pair->first_convert = 1;
return 0;
}
static int fsl_easrc_m2m_start(struct fsl_asrc_pair *pair)
{
/* start context once */
if (pair->first_convert) {
fsl_easrc_start_context(pair);
pair->first_convert = 0;
}
return 0;
}
static int fsl_easrc_m2m_stop(struct fsl_asrc_pair *pair)
{
/* Stop pair/context */
if (!pair->first_convert) {
fsl_easrc_stop_context(pair);
pair->first_convert = 1;
}
return 0;
}
/* calculate capture data length according to output data length and sample rate */
static int fsl_easrc_m2m_calc_out_len(struct fsl_asrc_pair *pair, int input_buffer_length)
{
struct fsl_asrc *easrc = pair->asrc;
struct fsl_easrc_priv *easrc_priv = easrc->private;
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
unsigned int in_rate = ctx_priv->in_params.norm_rate;
unsigned int out_rate = ctx_priv->out_params.norm_rate;
unsigned int channels = pair->channels;
unsigned int in_samples, out_samples;
unsigned int in_width, out_width;
unsigned int out_length;
unsigned int frac_bits;
u64 val1, val2;
switch (easrc_priv->rs_num_taps) {
case EASRC_RS_32_TAPS:
/* integer bits = 5; */
frac_bits = 39;
break;
case EASRC_RS_64_TAPS:
/* integer bits = 6; */
frac_bits = 38;
break;
case EASRC_RS_128_TAPS:
/* integer bits = 7; */
frac_bits = 37;
break;
default:
return -EINVAL;
}
val1 = (u64)in_rate << frac_bits;
do_div(val1, out_rate);
val1 += (s64)ctx_priv->ratio_mod << (frac_bits - 31);
in_width = snd_pcm_format_physical_width(ctx_priv->in_params.sample_format) / 8;
out_width = snd_pcm_format_physical_width(ctx_priv->out_params.sample_format) / 8;
ctx_priv->in_filled_len += input_buffer_length;
if (ctx_priv->in_filled_len <= ctx_priv->in_filled_sample * in_width * channels) {
out_length = 0;
} else {
in_samples = ctx_priv->in_filled_len / (in_width * channels) -
ctx_priv->in_filled_sample;
/* right shift 12 bit to make ratio in 32bit space */
val2 = (u64)in_samples << (frac_bits - 12);
val1 = val1 >> 12;
do_div(val2, val1);
out_samples = val2;
out_length = out_samples * out_width * channels;
ctx_priv->in_filled_len = ctx_priv->in_filled_sample * in_width * channels;
}
return out_length;
}
static int fsl_easrc_m2m_get_maxburst(u8 dir, struct fsl_asrc_pair *pair)
{
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
if (dir == IN)
return ctx_priv->in_params.fifo_wtmk * pair->channels;
else
return ctx_priv->out_params.fifo_wtmk * pair->channels;
}
static int fsl_easrc_m2m_pair_suspend(struct fsl_asrc_pair *pair)
{
fsl_easrc_stop_context(pair);
return 0;
}
static int fsl_easrc_m2m_pair_resume(struct fsl_asrc_pair *pair)
{
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
pair->first_convert = 1;
ctx_priv->in_filled_len = 0;
return 0;
}
/* val is Q31 */
static int fsl_easrc_m2m_set_ratio_mod(struct fsl_asrc_pair *pair, int val)
{
struct fsl_easrc_ctx_priv *ctx_priv = pair->private;
struct fsl_asrc *easrc = pair->asrc;
struct fsl_easrc_priv *easrc_priv = easrc->private;
unsigned int frac_bits;
ctx_priv->ratio_mod += val;
switch (easrc_priv->rs_num_taps) {
case EASRC_RS_32_TAPS:
/* integer bits = 5; */
frac_bits = 39;
break;
case EASRC_RS_64_TAPS:
/* integer bits = 6; */
frac_bits = 38;
break;
case EASRC_RS_128_TAPS:
/* integer bits = 7; */
frac_bits = 37;
break;
default:
return -EINVAL;
}
val <<= (frac_bits - 31);
regmap_write(easrc->regmap, REG_EASRC_RUC(pair->index), EASRC_RSUC_RS_RM(val));
return 0;
}
static int fsl_easrc_m2m_get_cap(struct fsl_asrc_m2m_cap *cap)
{
cap->fmt_in = FSL_EASRC_FORMATS;
cap->fmt_out = FSL_EASRC_FORMATS | SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
cap->rate_in = easrc_rates;
cap->rate_in_count = ARRAY_SIZE(easrc_rates);
cap->rate_out = easrc_rates;
cap->rate_out_count = ARRAY_SIZE(easrc_rates);
cap->chan_min = 1;
cap->chan_max = 32;
return 0;
}
static const struct of_device_id fsl_easrc_dt_ids[] = {
{ .compatible = "fsl,imx8mn-easrc",},
{}
@ -1926,6 +2144,16 @@ static int fsl_easrc_probe(struct platform_device *pdev)
easrc->release_pair = fsl_easrc_release_context;
easrc->get_fifo_addr = fsl_easrc_get_fifo_addr;
easrc->pair_priv_size = sizeof(struct fsl_easrc_ctx_priv);
easrc->m2m_prepare = fsl_easrc_m2m_prepare;
easrc->m2m_start = fsl_easrc_m2m_start;
easrc->m2m_stop = fsl_easrc_m2m_stop;
easrc->get_output_fifo_size = fsl_easrc_get_output_fifo_size;
easrc->m2m_calc_out_len = fsl_easrc_m2m_calc_out_len;
easrc->m2m_get_maxburst = fsl_easrc_m2m_get_maxburst;
easrc->m2m_pair_suspend = fsl_easrc_m2m_pair_suspend;
easrc->m2m_pair_resume = fsl_easrc_m2m_pair_resume;
easrc->m2m_set_ratio_mod = fsl_easrc_m2m_set_ratio_mod;
easrc->m2m_get_cap = fsl_easrc_m2m_get_cap;
easrc_priv->rs_num_taps = EASRC_RS_32_TAPS;
easrc_priv->const_coeff = 0x3FF0000000000000;
@ -1976,6 +2204,12 @@ static int fsl_easrc_probe(struct platform_device *pdev)
goto err_pm_disable;
}
ret = fsl_asrc_m2m_init(easrc);
if (ret) {
dev_err(&pdev->dev, "failed to init m2m device %d\n", ret);
return ret;
}
return 0;
err_pm_disable:
@ -1985,6 +2219,10 @@ err_pm_disable:
static void fsl_easrc_remove(struct platform_device *pdev)
{
struct fsl_asrc *easrc = dev_get_drvdata(&pdev->dev);
fsl_asrc_m2m_exit(easrc);
pm_runtime_disable(&pdev->dev);
}
@ -2085,10 +2323,29 @@ disable_mem_clk:
return ret;
}
static int fsl_easrc_suspend(struct device *dev)
{
struct fsl_asrc *easrc = dev_get_drvdata(dev);
int ret;
fsl_asrc_m2m_suspend(easrc);
ret = pm_runtime_force_suspend(dev);
return ret;
}
static int fsl_easrc_resume(struct device *dev)
{
struct fsl_asrc *easrc = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_force_resume(dev);
fsl_asrc_m2m_resume(easrc);
return ret;
}
static const struct dev_pm_ops fsl_easrc_pm_ops = {
RUNTIME_PM_OPS(fsl_easrc_runtime_suspend, fsl_easrc_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SYSTEM_SLEEP_PM_OPS(fsl_easrc_suspend, fsl_easrc_resume)
};
static struct platform_driver fsl_easrc_driver = {

View File

@ -601,6 +601,8 @@ struct fsl_easrc_slot {
* @out_missed_sample: sample missed in output
* @st1_addexp: exponent added for stage1
* @st2_addexp: exponent added for stage2
* @ratio_mod: update ratio
* @in_filled_len: input filled length
*/
struct fsl_easrc_ctx_priv {
struct fsl_easrc_io_params in_params;
@ -618,6 +620,8 @@ struct fsl_easrc_ctx_priv {
int out_missed_sample;
int st1_addexp;
int st2_addexp;
int ratio_mod;
unsigned int in_filled_len;
};
/**

View File

@ -35,6 +35,15 @@
#define MICFIL_AUDIO_PLL2 1
#define MICFIL_CLK_EXT3 2
static const unsigned int fsl_micfil_rates[] = {
8000, 11025, 16000, 22050, 32000, 44100, 48000,
};
static const struct snd_pcm_hw_constraint_list fsl_micfil_rate_constraints = {
.count = ARRAY_SIZE(fsl_micfil_rates),
.list = fsl_micfil_rates,
};
enum quality {
QUALITY_HIGH,
QUALITY_MEDIUM,
@ -486,29 +495,12 @@ static int fsl_micfil_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
unsigned int rates[MICFIL_NUM_RATES] = {8000, 11025, 16000, 22050, 32000, 44100, 48000};
int i, j, k = 0;
u64 clk_rate;
if (!micfil) {
dev_err(dai->dev, "micfil dai priv_data not set\n");
return -EINVAL;
}
micfil->constraint_rates.list = micfil->constraint_rates_list;
micfil->constraint_rates.count = 0;
for (j = 0; j < MICFIL_NUM_RATES; j++) {
for (i = 0; i < MICFIL_CLK_SRC_NUM; i++) {
clk_rate = clk_get_rate(micfil->clk_src[i]);
if (clk_rate != 0 && do_div(clk_rate, rates[j]) == 0) {
micfil->constraint_rates_list[k++] = rates[j];
micfil->constraint_rates.count++;
break;
}
}
}
if (micfil->constraint_rates.count > 0)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@ -1239,6 +1231,13 @@ static int fsl_micfil_probe(struct platform_device *pdev)
if (IS_ERR(micfil->clk_src[MICFIL_CLK_EXT3]))
micfil->clk_src[MICFIL_CLK_EXT3] = NULL;
fsl_asoc_constrain_rates(&micfil->constraint_rates,
&fsl_micfil_rate_constraints,
micfil->clk_src[MICFIL_AUDIO_PLL1],
micfil->clk_src[MICFIL_AUDIO_PLL2],
micfil->clk_src[MICFIL_CLK_EXT3],
micfil->constraint_rates_list);
/* init regmap */
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))

Some files were not shown because too many files have changed in this diff Show More