mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-15 02:05:33 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
# Conflicts: # drivers/gpu/drm/bridge/lontium-lt9611.c
This commit is contained in:
commit
dd0bc662b5
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -23,6 +23,8 @@ properties:
|
||||
- fsl,imx8qm-mqs
|
||||
- fsl,imx8qxp-mqs
|
||||
- fsl,imx93-mqs
|
||||
- fsl,imx943-aonmix-mqs
|
||||
- fsl,imx943-wakeupmix-mqs
|
||||
- fsl,imx95-aonmix-mqs
|
||||
- fsl,imx95-netcmix-mqs
|
||||
|
||||
|
@ -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>;
|
||||
};
|
||||
|
@ -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";
|
||||
};
|
||||
|
@ -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.
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
156
Documentation/devicetree/bindings/sound/realtek,rt5682.yaml
Normal file
156
Documentation/devicetree/bindings/sound/realtek,rt5682.yaml
Normal 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>;
|
||||
};
|
||||
};
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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>;
|
||||
};
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
@ -5529,8 +5529,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>
|
||||
|
@ -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".
|
||||
|
@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
obj-$(CONFIG_FW_CS_DSP) += cs_dsp.o
|
||||
|
||||
obj-y += test/
|
||||
|
23
drivers/firmware/cirrus/test/Makefile
Normal file
23
drivers/firmware/cirrus/test/Makefile
Normal 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
|
199
drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
Normal file
199
drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
Normal 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");
|
752
drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
Normal file
752
drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
Normal 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");
|
367
drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
Normal file
367
drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
Normal 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");
|
13
drivers/firmware/cirrus/test/cs_dsp_mock_utils.c
Normal file
13
drivers/firmware/cirrus/test/cs_dsp_mock_utils.c
Normal 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");
|
473
drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
Normal file
473
drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
Normal 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");
|
2556
drivers/firmware/cirrus/test/cs_dsp_test_bin.c
Normal file
2556
drivers/firmware/cirrus/test/cs_dsp_test_bin.c
Normal file
File diff suppressed because it is too large
Load Diff
600
drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
Normal file
600
drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
Normal 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);
|
688
drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
Normal file
688
drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
Normal 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);
|
3282
drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c
Normal file
3282
drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c
Normal file
File diff suppressed because it is too large
Load Diff
1851
drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c
Normal file
1851
drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c
Normal file
File diff suppressed because it is too large
Load Diff
2669
drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c
Normal file
2669
drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c
Normal file
File diff suppressed because it is too large
Load Diff
2211
drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c
Normal file
2211
drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c
Normal file
File diff suppressed because it is too large
Load Diff
1347
drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c
Normal file
1347
drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c
Normal file
File diff suppressed because it is too large
Load Diff
14
drivers/firmware/cirrus/test/cs_dsp_tests.c
Normal file
14
drivers/firmware/cirrus/test/cs_dsp_tests.c
Normal 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");
|
@ -45,6 +45,7 @@ struct lt9611 {
|
||||
struct device_node *dsi1_node;
|
||||
struct mipi_dsi_device *dsi0;
|
||||
struct mipi_dsi_device *dsi1;
|
||||
struct platform_device *audio_pdev;
|
||||
|
||||
bool ac_mode;
|
||||
|
||||
@ -756,6 +757,7 @@ static enum drm_mode_status lt9611_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
const struct drm_display_mode *mode)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
unsigned long long rate;
|
||||
|
||||
if (mode->hdisplay > 3840)
|
||||
return MODE_BAD_HVALUE;
|
||||
@ -763,7 +765,17 @@ static enum drm_mode_status lt9611_bridge_mode_valid(struct drm_bridge *bridge,
|
||||
if (mode->hdisplay > 2000 && !lt9611->dsi1_node)
|
||||
return MODE_PANEL;
|
||||
|
||||
return MODE_OK;
|
||||
rate = drm_hdmi_compute_mode_clock(mode, 8, HDMI_COLORSPACE_RGB);
|
||||
return bridge->funcs->hdmi_tmds_char_rate_valid(bridge, mode, rate);
|
||||
}
|
||||
|
||||
static int lt9611_bridge_atomic_check(struct drm_bridge *bridge,
|
||||
struct drm_bridge_state *bridge_state,
|
||||
struct drm_crtc_state *crtc_state,
|
||||
struct drm_connector_state *conn_state)
|
||||
{
|
||||
return drm_atomic_helper_connector_hdmi_check(conn_state->connector,
|
||||
conn_state->state);
|
||||
}
|
||||
|
||||
static void lt9611_bridge_atomic_pre_enable(struct drm_bridge *bridge,
|
||||
@ -854,10 +866,6 @@ static int lt9611_hdmi_clear_infoframe(struct drm_bridge *bridge,
|
||||
unsigned int mask;
|
||||
|
||||
switch (type) {
|
||||
case HDMI_INFOFRAME_TYPE_AUDIO:
|
||||
mask = LT9611_INFOFRAME_AUDIO;
|
||||
break;
|
||||
|
||||
case HDMI_INFOFRAME_TYPE_AVI:
|
||||
mask = LT9611_INFOFRAME_AVI;
|
||||
break;
|
||||
@ -891,11 +899,6 @@ static int lt9611_hdmi_write_infoframe(struct drm_bridge *bridge,
|
||||
int i;
|
||||
|
||||
switch (type) {
|
||||
case HDMI_INFOFRAME_TYPE_AUDIO:
|
||||
mask = LT9611_INFOFRAME_AUDIO;
|
||||
addr = 0x84b2;
|
||||
break;
|
||||
|
||||
case HDMI_INFOFRAME_TYPE_AVI:
|
||||
mask = LT9611_INFOFRAME_AVI;
|
||||
addr = 0x8440;
|
||||
@ -939,55 +942,6 @@ lt9611_hdmi_tmds_char_rate_valid(const struct drm_bridge *bridge,
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_audio_startup(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x82d6, 0x8c);
|
||||
regmap_write(lt9611->regmap, 0x82d7, 0x04);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x08);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x10);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8434, 0xd5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_audio_prepare(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
|
||||
if (hparms->sample_rate == 48000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0x2b);
|
||||
else if (hparms->sample_rate == 96000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0xab);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8435, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8436, 0x18);
|
||||
regmap_write(lt9611->regmap, 0x8437, 0x00);
|
||||
|
||||
return drm_atomic_helper_connector_hdmi_update_audio_infoframe(connector,
|
||||
&hparms->cea);
|
||||
}
|
||||
|
||||
static void lt9611_hdmi_audio_shutdown(struct drm_connector *connector,
|
||||
struct drm_bridge *bridge)
|
||||
{
|
||||
struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
|
||||
|
||||
drm_atomic_helper_connector_hdmi_clear_audio_infoframe(connector);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x00);
|
||||
}
|
||||
|
||||
static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
||||
.attach = lt9611_bridge_attach,
|
||||
.mode_valid = lt9611_bridge_mode_valid,
|
||||
@ -995,6 +949,7 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
||||
.edid_read = lt9611_bridge_edid_read,
|
||||
.hpd_enable = lt9611_bridge_hpd_enable,
|
||||
|
||||
.atomic_check = lt9611_bridge_atomic_check,
|
||||
.atomic_pre_enable = lt9611_bridge_atomic_pre_enable,
|
||||
.atomic_enable = lt9611_bridge_atomic_enable,
|
||||
.atomic_disable = lt9611_bridge_atomic_disable,
|
||||
@ -1007,10 +962,6 @@ static const struct drm_bridge_funcs lt9611_bridge_funcs = {
|
||||
.hdmi_tmds_char_rate_valid = lt9611_hdmi_tmds_char_rate_valid,
|
||||
.hdmi_write_infoframe = lt9611_hdmi_write_infoframe,
|
||||
.hdmi_clear_infoframe = lt9611_hdmi_clear_infoframe,
|
||||
|
||||
.hdmi_audio_startup = lt9611_hdmi_audio_startup,
|
||||
.hdmi_audio_prepare = lt9611_hdmi_audio_prepare,
|
||||
.hdmi_audio_shutdown = lt9611_hdmi_audio_shutdown,
|
||||
};
|
||||
|
||||
static int lt9611_parse_dt(struct device *dev,
|
||||
@ -1064,6 +1015,102 @@ static int lt9611_read_device_rev(struct lt9611 *lt9611)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_hw_params(struct device *dev, void *data,
|
||||
struct hdmi_codec_daifmt *fmt,
|
||||
struct hdmi_codec_params *hparms)
|
||||
{
|
||||
struct lt9611 *lt9611 = data;
|
||||
|
||||
if (hparms->sample_rate == 48000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0x2b);
|
||||
else if (hparms->sample_rate == 96000)
|
||||
regmap_write(lt9611->regmap, 0x840f, 0xab);
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8435, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8436, 0x18);
|
||||
regmap_write(lt9611->regmap, 0x8437, 0x00);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lt9611_audio_startup(struct device *dev, void *data)
|
||||
{
|
||||
struct lt9611 *lt9611 = data;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x82d6, 0x8c);
|
||||
regmap_write(lt9611->regmap, 0x82d7, 0x04);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x08);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x10);
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8434, 0xd5);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lt9611_audio_shutdown(struct device *dev, void *data)
|
||||
{
|
||||
struct lt9611 *lt9611 = data;
|
||||
|
||||
regmap_write(lt9611->regmap, 0x8406, 0x00);
|
||||
regmap_write(lt9611->regmap, 0x8407, 0x00);
|
||||
}
|
||||
|
||||
static int lt9611_hdmi_i2s_get_dai_id(struct snd_soc_component *component,
|
||||
struct device_node *endpoint,
|
||||
void *data)
|
||||
{
|
||||
struct of_endpoint of_ep;
|
||||
int ret;
|
||||
|
||||
ret = of_graph_parse_endpoint(endpoint, &of_ep);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* HDMI sound should be located as reg = <2>
|
||||
* Then, it is sound port 0
|
||||
*/
|
||||
if (of_ep.port == 2)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct hdmi_codec_ops lt9611_codec_ops = {
|
||||
.hw_params = lt9611_hdmi_hw_params,
|
||||
.audio_shutdown = lt9611_audio_shutdown,
|
||||
.audio_startup = lt9611_audio_startup,
|
||||
.get_dai_id = lt9611_hdmi_i2s_get_dai_id,
|
||||
};
|
||||
|
||||
static struct hdmi_codec_pdata codec_data = {
|
||||
.ops = <9611_codec_ops,
|
||||
.max_i2s_channels = 8,
|
||||
.i2s = 1,
|
||||
};
|
||||
|
||||
static int lt9611_audio_init(struct device *dev, struct lt9611 *lt9611)
|
||||
{
|
||||
codec_data.data = lt9611;
|
||||
lt9611->audio_pdev =
|
||||
platform_device_register_data(dev, HDMI_CODEC_DRV_NAME,
|
||||
PLATFORM_DEVID_AUTO,
|
||||
&codec_data, sizeof(codec_data));
|
||||
|
||||
return PTR_ERR_OR_ZERO(lt9611->audio_pdev);
|
||||
}
|
||||
|
||||
static void lt9611_audio_exit(struct lt9611 *lt9611)
|
||||
{
|
||||
if (lt9611->audio_pdev) {
|
||||
platform_device_unregister(lt9611->audio_pdev);
|
||||
lt9611->audio_pdev = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int lt9611_probe(struct i2c_client *client)
|
||||
{
|
||||
struct lt9611 *lt9611;
|
||||
@ -1127,9 +1174,6 @@ static int lt9611_probe(struct i2c_client *client)
|
||||
|
||||
i2c_set_clientdata(client, lt9611);
|
||||
|
||||
/* Disable Audio InfoFrame, enabled by default */
|
||||
regmap_update_bits(lt9611->regmap, 0x843d, LT9611_INFOFRAME_AUDIO, 0);
|
||||
|
||||
lt9611->bridge.funcs = <9611_bridge_funcs;
|
||||
lt9611->bridge.of_node = client->dev.of_node;
|
||||
lt9611->bridge.ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID |
|
||||
@ -1138,9 +1182,6 @@ static int lt9611_probe(struct i2c_client *client)
|
||||
lt9611->bridge.type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
lt9611->bridge.vendor = "Lontium";
|
||||
lt9611->bridge.product = "LT9611";
|
||||
lt9611->bridge.hdmi_audio_dev = dev;
|
||||
lt9611->bridge.hdmi_audio_max_i2s_playback_channels = 8;
|
||||
lt9611->bridge.hdmi_audio_dai_port = 2;
|
||||
|
||||
drm_bridge_add(<9611->bridge);
|
||||
|
||||
@ -1162,6 +1203,10 @@ static int lt9611_probe(struct i2c_client *client)
|
||||
|
||||
lt9611_enable_hpd_interrupts(lt9611);
|
||||
|
||||
ret = lt9611_audio_init(dev, lt9611);
|
||||
if (ret)
|
||||
goto err_remove_bridge;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_bridge:
|
||||
@ -1182,6 +1227,7 @@ static void lt9611_remove(struct i2c_client *client)
|
||||
struct lt9611 *lt9611 = i2c_get_clientdata(client);
|
||||
|
||||
disable_irq(client->irq);
|
||||
lt9611_audio_exit(lt9611);
|
||||
drm_bridge_remove(<9611->bridge);
|
||||
|
||||
regulator_bulk_disable(ARRAY_SIZE(lt9611->supplies), lt9611->supplies);
|
||||
@ -1190,8 +1236,8 @@ static void lt9611_remove(struct i2c_client *client)
|
||||
of_node_put(lt9611->dsi0_node);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id lt9611_id[] = {
|
||||
{ "lontium,lt9611" },
|
||||
static struct i2c_device_id lt9611_id[] = {
|
||||
{ "lontium,lt9611", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, lt9611_id);
|
||||
|
@ -10,6 +10,5 @@
|
||||
#define AIF3_PB 4
|
||||
#define AIF3_CAP 5
|
||||
#define AIF4_PB 6
|
||||
#define NUM_CODEC_DAIS 7
|
||||
|
||||
#endif
|
||||
|
160
include/linux/firmware/cirrus/cs_dsp_test_utils.h
Normal file
160
include/linux/firmware/cirrus/cs_dsp_test_utils.h
Normal 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);
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -681,6 +681,17 @@ struct snd_soc_dai_link_component {
|
||||
struct device_node *of_node;
|
||||
const char *dai_name;
|
||||
const struct of_phandle_args *dai_args;
|
||||
|
||||
/*
|
||||
* Extra format = SND_SOC_DAIFMT_Bx_Fx
|
||||
*
|
||||
* [Note] it is Bx_Fx base, not CBx_CFx
|
||||
*
|
||||
* It will be used with dai_link->dai_fmt
|
||||
* see
|
||||
* snd_soc_runtime_set_dai_fmt()
|
||||
*/
|
||||
unsigned int ext_fmt;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -1118,7 +1129,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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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[] = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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, ®_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,
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -26,8 +26,7 @@ struct peb2466_lookup {
|
||||
unsigned int count;
|
||||
};
|
||||
|
||||
#define PEB2466_TLV_SIZE (sizeof((unsigned int []){TLV_DB_SCALE_ITEM(0, 0, 0)}) / \
|
||||
sizeof(unsigned int))
|
||||
#define PEB2466_TLV_SIZE ARRAY_SIZE(((unsigned int[]){TLV_DB_SCALE_ITEM(0, 0, 0)}))
|
||||
|
||||
struct peb2466_lkup_ctrl {
|
||||
int reg;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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__ */
|
||||
|
@ -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" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)) {
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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 */
|
||||
|
727
sound/soc/fsl/fsl_asrc_m2m.c
Normal file
727
sound/soc/fsl/fsl_asrc_m2m.c
Normal 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), ®);
|
||||
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");
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user