sound updates for 6.14-rc1
This was a relatively calm cycle, and most of changes are rather small device-specific fixes. Here are highlights: * Core: - Further enhancements of ALSA rawmidi and sequencer APIs for MIDI 2.0 - compress-offload API extensions for ASRC support * ASoC: - Allow clocking on each DAI in an audio graph card to be configured separately - Improved power management for Renesas RZ-SSI - KUnit testing for the Cirrus DSP framework - Memory to meory operation support for Freescale/NXP platforms - Support for pause operations in SOF - Support for Allwinner suinv F1C100s, Awinc AW88083, Realtek ALC5682I-VE * HD- and USB-audio: - Add support for Focusrite Scarlett 4th Gen 16i16, 18i16, and 18i20 interfaces via new FCP driver - TAS2781 SPI HD-audio sub-codec support - Various device-specific quirks as usual -----BEGIN PGP SIGNATURE----- iQJCBAABCAAsFiEEIXTw5fNLNI7mMiVaLtJE4w1nLE8FAmeSRrwOHHRpd2FpQHN1 c2UuZGUACgkQLtJE4w1nLE+mWw//QbiwfdfjNjXVTOBpJOI7auFj408sB2IgoOcM wj3h94ONG+45DZSbzpAB5t8sNN2EBrx7KGtdLcSqlPoehLq3KduIaZhDjnjtYKVL 2GujsSqhsBRr/dr4Qr+48idtlcv1HJsJZGkf/nSHdyHgqVkk9jd42okHOOsOHF6g hn3zocPSVBoAH6UTPnxNmX9jD4sKFu+jBayxTmWXMY+qS+iD9dCRa8kyeLyERqAO uB5IFQdaNM7y4DHmDSgH3sBohGZ/38x/hNpS6sspoMrkVNoLLa7W2g7DL2r2qMtg JMMHlyx1VGqFpQC6TPgFWSb2WJpxo18cVGD5NuXGnT5SjTzKMrHOtp9dmVt+p9Am mqveyEMijV/5lmKo4I515xJM5AllQkYYFHhlEOySFx0iUchNCogEBHwMG8ufTAk6 WwdxR8YPzzE2lxJk5UrVvliA1s/B4pwHtbZEOR29QnfOzQuKttaES+sWisZXHaEG Nqyy+xta4oiixdneeTEp8Dr4bpHBlUaO4gA/f/iJ0apJK+3KeNAuC57Xi5yBJS5p LtbUd+hRYNLQ5mUtRYIiOLLeiKnD62bYRSYgoRC0MOLZiT49G3Kr1BuyUqj2x0OW vLY/Tl4D2c+4aNwPwhsq5tSiTmQqZis9njT7FJL+OFpIHlKXANubZaMZnU+COM1V aa9Mye0= =IBfz -----END PGP SIGNATURE----- Merge tag 'sound-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound updates from Takashi Iwai: "This was a relatively calm cycle, and most of changes are rather small device-specific fixes. Here are highlights: Core: - Further enhancements of ALSA rawmidi and sequencer APIs for MIDI 2.0 - compress-offload API extensions for ASRC support ASoC: - Allow clocking on each DAI in an audio graph card to be configured separately - Improved power management for Renesas RZ-SSI - KUnit testing for the Cirrus DSP framework - Memory to meory operation support for Freescale/NXP platforms - Support for pause operations in SOF - Support for Allwinner suinv F1C100s, Awinc AW88083, Realtek ALC5682I-VE HD- and USB-audio: - Add support for Focusrite Scarlett 4th Gen 16i16, 18i16, and 18i20 interfaces via new FCP driver - TAS2781 SPI HD-audio sub-codec support - Various device-specific quirks as usual" * tag 'sound-6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (235 commits) ALSA: hda: tas2781-spi: Fix bogus error handling in tas2781_hda_spi_probe() ALSA: hda: tas2781-spi: Fix error code in tas2781_read_acpi() ALSA: hda: tas2781-spi: Delete some dead code ALSA: usb: fcp: Fix return code from poll ops ALSA: usb: fcp: Fix incorrect resp->opcode retrieval ALSA: usb: fcp: Fix meter_levels type to __le32 ALSA: hda/realtek: Enable Mute LED on HP Laptop 14s-fq1xxx ALSA: hda: tas2781-spi: Fix -Wsometimes-uninitialized in tasdevice_spi_switch_book() ALSA: ctxfi: Simplify dao_clear_{left,right}_input() functions ALSA: hda: tas2781-spi: select CRC32 instead of CRC32_SARWATE ALSA: usb: fcp: Fix hwdep read ops types ALSA: scarlett2: Add device_setup option to use FCP driver ALSA: FCP: Add Focusrite Control Protocol driver ALSA: hda/tas2781: Add tas2781 hda SPI driver ALSA: hda/realtek - Fixed headphone distorted sound on Acer Aspire A115-31 laptop ASoC: xilinx: xlnx_spdif: Simpify using devm_clk_get_enabled() ALSA: hda: Support for Ideapad hotkey mute LEDs ASoC: Intel: sof_sdw: Fix DMI match for Lenovo 83JX, 83MC and 83NM ASoC: Intel: sof_sdw: Fix DMI match for Lenovo 83LC ASoC: dapm: add support for preparing streams ...
This commit is contained in:
commit
2c8d2a510c
233 changed files with 27086 additions and 1793 deletions
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -25,6 +25,7 @@ properties:
|
|||
- fsl,imx8mm-micfil
|
||||
- fsl,imx8mp-micfil
|
||||
- fsl,imx93-micfil
|
||||
- fsl,imx943-micfil
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -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>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
|
|
|
@ -293,6 +293,17 @@ Rawmidi API Extensions
|
|||
status 0x05). When UMP core receives such a message, it updates the
|
||||
UMP EP info and the corresponding sequencer clients as well.
|
||||
|
||||
* The legacy rawmidi device number is found in the new `tied_device`
|
||||
field of the rawmidi info.
|
||||
On the other hand, the UMP rawmidi device number is found in
|
||||
`tied_device` field of the legacy rawmidi info, too.
|
||||
|
||||
* Each substream of the legacy rawmidi may be enabled / disabled
|
||||
dynamically depending on the UMP FB state.
|
||||
When the selected substream is inactive, it's indicated by the bit
|
||||
0x10 (`SNDRV_RAWMIDI_INFO_STREAM_INACTIVE`) in the `flags` field of
|
||||
the legacy rawmidi info.
|
||||
|
||||
|
||||
Control API Extensions
|
||||
======================
|
||||
|
@ -377,6 +388,13 @@ Sequencer API Extensions
|
|||
announcement to the ALSA sequencer system port, similarly like the
|
||||
normal port change notification.
|
||||
|
||||
* There are two extended event types for notifying the UMP Endpoint and
|
||||
Function Block changes via the system announcement port:
|
||||
type 68 (`SNDRV_SEQ_EVENT_UMP_EP_CHANGE`) and type 69
|
||||
(`SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE`). They take the new type,
|
||||
`snd_seq_ev_ump_notify` in the payload, indicating the client number
|
||||
and the FB number that are changed.
|
||||
|
||||
|
||||
MIDI2 USB Gadget Function Driver
|
||||
================================
|
||||
|
|
14
MAINTAINERS
14
MAINTAINERS
|
@ -5536,8 +5536,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>
|
||||
|
@ -9007,14 +9007,16 @@ L: linux-input@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/input/joystick/fsia6b.c
|
||||
|
||||
FOCUSRITE SCARLETT2 MIXER DRIVER (Scarlett Gen 2+ and Clarett)
|
||||
FOCUSRITE CONTROL PROTOCOL/SCARLETT2 MIXER DRIVERS (Scarlett Gen 2+, Clarett, and Vocaster)
|
||||
M: Geoffrey D. Bennett <g@b4.vu>
|
||||
L: linux-sound@vger.kernel.org
|
||||
S: Maintained
|
||||
W: https://github.com/geoffreybennett/scarlett-gen2
|
||||
B: https://github.com/geoffreybennett/scarlett-gen2/issues
|
||||
T: git https://github.com/geoffreybennett/scarlett-gen2.git
|
||||
W: https://github.com/geoffreybennett/linux-fcp
|
||||
B: https://github.com/geoffreybennett/linux-fcp/issues
|
||||
T: git https://github.com/geoffreybennett/linux-fcp.git
|
||||
F: include/uapi/sound/fcp.h
|
||||
F: include/uapi/sound/scarlett2.h
|
||||
F: sound/usb/fcp.c
|
||||
F: sound/usb/mixer_scarlett2.c
|
||||
|
||||
FORCEDETH GIGABIT ETHERNET DRIVER
|
||||
|
|
|
@ -1767,6 +1767,7 @@ static bool acpi_device_enumeration_by_parent(struct acpi_device *device)
|
|||
{"CSC3557", },
|
||||
{"INT33FE", },
|
||||
{"INT3515", },
|
||||
{"TXNW2781", },
|
||||
/* Non-conforming _HID for Cirrus Logic already released */
|
||||
{"CLSA0100", },
|
||||
{"CLSA0101", },
|
||||
|
|
|
@ -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");
|
|
@ -384,6 +384,17 @@ static const struct smi_node cs35l57_hda = {
|
|||
.bus_type = SMI_AUTO_DETECT,
|
||||
};
|
||||
|
||||
static const struct smi_node tas2781_hda = {
|
||||
.instances = {
|
||||
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
|
||||
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
|
||||
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
|
||||
{ "tas2781-hda", IRQ_RESOURCE_AUTO, 0 },
|
||||
{}
|
||||
},
|
||||
.bus_type = SMI_AUTO_DETECT,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note new device-ids must also be added to ignore_serial_bus_ids in
|
||||
* drivers/acpi/scan.c: acpi_device_enumeration_by_parent().
|
||||
|
@ -396,6 +407,7 @@ static const struct acpi_device_id smi_acpi_ids[] = {
|
|||
{ "CSC3556", (unsigned long)&cs35l56_hda },
|
||||
{ "CSC3557", (unsigned long)&cs35l57_hda },
|
||||
{ "INT3515", (unsigned long)&int3515_data },
|
||||
{ "TXNW2781", (unsigned long)&tas2781_hda },
|
||||
/* Non-conforming _HID for Cirrus Logic already released */
|
||||
{ "CLSA0100", (unsigned long)&cs35l41_hda },
|
||||
{ "CLSA0101", (unsigned long)&cs35l41_hda },
|
||||
|
|
|
@ -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);
|
|
@ -2,8 +2,6 @@
|
|||
#ifndef __SOUND_HDAUDIO_EXT_H
|
||||
#define __SOUND_HDAUDIO_EXT_H
|
||||
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <sound/hdaudio.h>
|
||||
|
||||
int snd_hdac_ext_bus_init(struct hdac_bus *bus, struct device *dev,
|
||||
|
@ -119,49 +117,6 @@ int snd_hdac_ext_bus_link_put(struct hdac_bus *bus, struct hdac_ext_link *hlink)
|
|||
|
||||
void snd_hdac_ext_bus_link_power(struct hdac_device *codec, bool enable);
|
||||
|
||||
#define snd_hdac_adsp_writeb(chip, reg, value) \
|
||||
snd_hdac_reg_writeb(chip, (chip)->dsp_ba + (reg), value)
|
||||
#define snd_hdac_adsp_readb(chip, reg) \
|
||||
snd_hdac_reg_readb(chip, (chip)->dsp_ba + (reg))
|
||||
#define snd_hdac_adsp_writew(chip, reg, value) \
|
||||
snd_hdac_reg_writew(chip, (chip)->dsp_ba + (reg), value)
|
||||
#define snd_hdac_adsp_readw(chip, reg) \
|
||||
snd_hdac_reg_readw(chip, (chip)->dsp_ba + (reg))
|
||||
#define snd_hdac_adsp_writel(chip, reg, value) \
|
||||
snd_hdac_reg_writel(chip, (chip)->dsp_ba + (reg), value)
|
||||
#define snd_hdac_adsp_readl(chip, reg) \
|
||||
snd_hdac_reg_readl(chip, (chip)->dsp_ba + (reg))
|
||||
#define snd_hdac_adsp_writeq(chip, reg, value) \
|
||||
snd_hdac_reg_writeq(chip, (chip)->dsp_ba + (reg), value)
|
||||
#define snd_hdac_adsp_readq(chip, reg) \
|
||||
snd_hdac_reg_readq(chip, (chip)->dsp_ba + (reg))
|
||||
|
||||
#define snd_hdac_adsp_updateb(chip, reg, mask, val) \
|
||||
snd_hdac_adsp_writeb(chip, reg, \
|
||||
(snd_hdac_adsp_readb(chip, reg) & ~(mask)) | (val))
|
||||
#define snd_hdac_adsp_updatew(chip, reg, mask, val) \
|
||||
snd_hdac_adsp_writew(chip, reg, \
|
||||
(snd_hdac_adsp_readw(chip, reg) & ~(mask)) | (val))
|
||||
#define snd_hdac_adsp_updatel(chip, reg, mask, val) \
|
||||
snd_hdac_adsp_writel(chip, reg, \
|
||||
(snd_hdac_adsp_readl(chip, reg) & ~(mask)) | (val))
|
||||
#define snd_hdac_adsp_updateq(chip, reg, mask, val) \
|
||||
snd_hdac_adsp_writeq(chip, reg, \
|
||||
(snd_hdac_adsp_readq(chip, reg) & ~(mask)) | (val))
|
||||
|
||||
#define snd_hdac_adsp_readb_poll(chip, reg, val, cond, delay_us, timeout_us) \
|
||||
readb_poll_timeout((chip)->dsp_ba + (reg), val, cond, \
|
||||
delay_us, timeout_us)
|
||||
#define snd_hdac_adsp_readw_poll(chip, reg, val, cond, delay_us, timeout_us) \
|
||||
readw_poll_timeout((chip)->dsp_ba + (reg), val, cond, \
|
||||
delay_us, timeout_us)
|
||||
#define snd_hdac_adsp_readl_poll(chip, reg, val, cond, delay_us, timeout_us) \
|
||||
readl_poll_timeout((chip)->dsp_ba + (reg), val, cond, \
|
||||
delay_us, timeout_us)
|
||||
#define snd_hdac_adsp_readq_poll(chip, reg, val, cond, delay_us, timeout_us) \
|
||||
readq_poll_timeout((chip)->dsp_ba + (reg), val, cond, \
|
||||
delay_us, timeout_us)
|
||||
|
||||
struct hdac_ext_device;
|
||||
|
||||
/* ops common to all codec drivers */
|
||||
|
|
|
@ -1532,9 +1532,10 @@ static inline u64 pcm_format_to_bits(snd_pcm_format_t pcm_format)
|
|||
dev_dbg((pcm)->card->dev, fmt, ##args)
|
||||
|
||||
/* helpers for copying between iov_iter and iomem */
|
||||
int copy_to_iter_fromio(struct iov_iter *itert, const void __iomem *src,
|
||||
size_t count);
|
||||
int copy_from_iter_toio(void __iomem *dst, struct iov_iter *iter, size_t count);
|
||||
size_t copy_to_iter_fromio(const void __iomem *src, size_t bytes,
|
||||
struct iov_iter *iter) __must_check;
|
||||
size_t copy_from_iter_toio(void __iomem *dst, size_t bytes,
|
||||
struct iov_iter *iter) __must_check;
|
||||
|
||||
struct snd_pcm_status64 {
|
||||
snd_pcm_state_t state; /* stream state */
|
||||
|
|
|
@ -89,6 +89,7 @@ struct snd_rawmidi_substream {
|
|||
unsigned int framing; /* whether to frame input data */
|
||||
unsigned int clock_type; /* clock source to use for input framing */
|
||||
int use_count; /* use counter (for output) */
|
||||
bool inactive; /* inactive substream (for UMP legacy) */
|
||||
size_t bytes;
|
||||
spinlock_t lock;
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
@ -118,6 +119,7 @@ struct snd_rawmidi {
|
|||
struct list_head list;
|
||||
unsigned int device; /* device number */
|
||||
unsigned int info_flags; /* SNDRV_RAWMIDI_INFO_XXXX */
|
||||
unsigned int tied_device;
|
||||
char id[64];
|
||||
char name[80];
|
||||
|
||||
|
@ -189,4 +191,13 @@ long snd_rawmidi_kernel_read(struct snd_rawmidi_substream *substream,
|
|||
long snd_rawmidi_kernel_write(struct snd_rawmidi_substream *substream,
|
||||
const unsigned char *buf, long count);
|
||||
|
||||
/* set up the tied devices */
|
||||
static inline void snd_rawmidi_tie_devices(struct snd_rawmidi *r1,
|
||||
struct snd_rawmidi *r2)
|
||||
{
|
||||
/* tied_device field keeps the device+1 (so that 0 being unknown) */
|
||||
r1->tied_device = r2->device + 1;
|
||||
r2->tied_device = r1->device + 1;
|
||||
}
|
||||
|
||||
#endif /* __SOUND_RAWMIDI_H */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -89,6 +89,13 @@ struct simple_util_priv {
|
|||
#define simple_props_to_dai_codec(props, i) ((props)->codec_dai + i)
|
||||
#define simple_props_to_codec_conf(props, i) ((props)->codec_conf + i)
|
||||
|
||||
/* has the same effect as simple_priv_to_props(). Preferred over
|
||||
* simple_priv_to_props() when dealing with PCM runtime data as
|
||||
* the ID stored in rtd->id may not be a valid array index.
|
||||
*/
|
||||
#define runtime_simple_priv_to_props(priv, rtd) \
|
||||
((priv)->dai_props + ((rtd)->dai_link - (priv)->dai_link))
|
||||
|
||||
#define for_each_prop_dlc_cpus(props, i, cpu) \
|
||||
for ((i) = 0; \
|
||||
((i) < (props)->num.cpus) && \
|
||||
|
@ -264,9 +271,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)
|
||||
|
|
|
@ -193,6 +193,9 @@ int snd_soc_dai_set_channel_map(struct snd_soc_dai *dai,
|
|||
|
||||
int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate);
|
||||
|
||||
int snd_soc_dai_prepare(struct snd_soc_dai *dai,
|
||||
struct snd_pcm_substream *substream);
|
||||
|
||||
/* Digital Audio Interface mute */
|
||||
int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
|
||||
int direction);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -83,6 +83,7 @@ struct snd_ump_ops {
|
|||
struct snd_seq_ump_ops {
|
||||
void (*input_receive)(struct snd_ump_endpoint *ump,
|
||||
const u32 *data, int words);
|
||||
int (*notify_ep_change)(struct snd_ump_endpoint *ump);
|
||||
int (*notify_fb_change)(struct snd_ump_endpoint *ump,
|
||||
struct snd_ump_block *fb);
|
||||
int (*switch_protocol)(struct snd_ump_endpoint *ump);
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#include <sound/asound.h>
|
||||
|
||||
/** version of the sequencer */
|
||||
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 4)
|
||||
#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 5)
|
||||
|
||||
/**
|
||||
* definition of sequencer event types
|
||||
|
@ -92,6 +92,9 @@
|
|||
#define SNDRV_SEQ_EVENT_PORT_SUBSCRIBED 66 /* ports connected */
|
||||
#define SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED 67 /* ports disconnected */
|
||||
|
||||
#define SNDRV_SEQ_EVENT_UMP_EP_CHANGE 68 /* UMP EP info has changed */
|
||||
#define SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE 69 /* UMP block info has changed */
|
||||
|
||||
/* 70-89: synthesizer events - obsoleted */
|
||||
|
||||
/** user-defined events with fixed length
|
||||
|
@ -253,6 +256,12 @@ struct snd_seq_ev_quote {
|
|||
struct snd_seq_event *event; /* quoted event */
|
||||
} __packed;
|
||||
|
||||
/* UMP info change notify */
|
||||
struct snd_seq_ev_ump_notify {
|
||||
unsigned char client; /**< Client number */
|
||||
unsigned char block; /**< Block number (optional) */
|
||||
};
|
||||
|
||||
union snd_seq_event_data { /* event data... */
|
||||
struct snd_seq_ev_note note;
|
||||
struct snd_seq_ev_ctrl control;
|
||||
|
@ -265,6 +274,7 @@ union snd_seq_event_data { /* event data... */
|
|||
struct snd_seq_connect connect;
|
||||
struct snd_seq_result result;
|
||||
struct snd_seq_ev_quote quote;
|
||||
struct snd_seq_ev_ump_notify ump_notify;
|
||||
};
|
||||
|
||||
/* sequencer event */
|
||||
|
|
|
@ -716,7 +716,7 @@ enum {
|
|||
* Raw MIDI section - /dev/snd/midi??
|
||||
*/
|
||||
|
||||
#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 4)
|
||||
#define SNDRV_RAWMIDI_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 5)
|
||||
|
||||
enum {
|
||||
SNDRV_RAWMIDI_STREAM_OUTPUT = 0,
|
||||
|
@ -728,6 +728,9 @@ enum {
|
|||
#define SNDRV_RAWMIDI_INFO_INPUT 0x00000002
|
||||
#define SNDRV_RAWMIDI_INFO_DUPLEX 0x00000004
|
||||
#define SNDRV_RAWMIDI_INFO_UMP 0x00000008
|
||||
#define SNDRV_RAWMIDI_INFO_STREAM_INACTIVE 0x00000010
|
||||
|
||||
#define SNDRV_RAWMIDI_DEVICE_UNKNOWN 0
|
||||
|
||||
struct snd_rawmidi_info {
|
||||
unsigned int device; /* RO/WR (control): device number */
|
||||
|
@ -740,7 +743,8 @@ struct snd_rawmidi_info {
|
|||
unsigned char subname[32]; /* name of active or selected subdevice */
|
||||
unsigned int subdevices_count;
|
||||
unsigned int subdevices_avail;
|
||||
unsigned char reserved[64]; /* reserved for future use */
|
||||
int tied_device; /* R: tied rawmidi device (UMP/legacy) */
|
||||
unsigned char reserved[60]; /* reserved for future use */
|
||||
};
|
||||
|
||||
#define SNDRV_RAWMIDI_MODE_FRAMING_MASK (7<<0)
|
||||
|
|
|
@ -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
|
||||
|
|
120
include/uapi/sound/fcp.h
Normal file
120
include/uapi/sound/fcp.h
Normal file
|
@ -0,0 +1,120 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
|
||||
/*
|
||||
* Focusrite Control Protocol Driver for ALSA
|
||||
*
|
||||
* Copyright (c) 2024-2025 by Geoffrey D. Bennett <g at b4.vu>
|
||||
*/
|
||||
/*
|
||||
* DOC: FCP (Focusrite Control Protocol) User-Space API
|
||||
*
|
||||
* This header defines the interface between the FCP kernel driver and
|
||||
* user-space programs to enable the use of the proprietary features
|
||||
* available in Focusrite USB audio interfaces. This includes Scarlett
|
||||
* 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, Clarett+, and Vocaster
|
||||
* series devices.
|
||||
*
|
||||
* The interface is provided via ALSA's hwdep interface. Opening the
|
||||
* hwdep device requires CAP_SYS_RAWIO privileges as this interface
|
||||
* provides near-direct access.
|
||||
*
|
||||
* For details on the FCP protocol, refer to the kernel scarlett2
|
||||
* driver in sound/usb/mixer_scarlett2.c and the fcp-support project
|
||||
* at https://github.com/geoffreybennett/fcp-support
|
||||
*
|
||||
* For examples of using these IOCTLs, see the fcp-server source in
|
||||
* the fcp-support project.
|
||||
*
|
||||
* IOCTL Interface
|
||||
* --------------
|
||||
* FCP_IOCTL_PVERSION:
|
||||
* Returns the protocol version supported by the driver.
|
||||
*
|
||||
* FCP_IOCTL_INIT:
|
||||
* Initialises the protocol and synchronises sequence numbers
|
||||
* between the driver and device. Must be called at least once
|
||||
* before sending commands. Can be safely called again at any time.
|
||||
*
|
||||
* FCP_IOCTL_CMD:
|
||||
* Sends an FCP command to the device and returns the response.
|
||||
* Requires prior initialisation via FCP_IOCTL_INIT.
|
||||
*
|
||||
* FCP_IOCTL_SET_METER_MAP:
|
||||
* Configures the Level Meter control's mapping between device
|
||||
* meters and control channels. Requires FCP_IOCTL_INIT to have been
|
||||
* called first. The map size and number of slots cannot be changed
|
||||
* after initial configuration, although the map itself can be
|
||||
* updated. Once configured, the Level Meter remains functional even
|
||||
* after the hwdep device is closed.
|
||||
*
|
||||
* FCP_IOCTL_SET_METER_LABELS:
|
||||
* Set the labels for the Level Meter control. Requires
|
||||
* FCP_IOCTL_SET_METER_MAP to have been called first. labels[]
|
||||
* should contain a sequence of null-terminated labels corresponding
|
||||
* to the control's channels.
|
||||
*/
|
||||
#ifndef __UAPI_SOUND_FCP_H
|
||||
#define __UAPI_SOUND_FCP_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#define FCP_HWDEP_MAJOR 2
|
||||
#define FCP_HWDEP_MINOR 0
|
||||
#define FCP_HWDEP_SUBMINOR 0
|
||||
|
||||
#define FCP_HWDEP_VERSION \
|
||||
((FCP_HWDEP_MAJOR << 16) | \
|
||||
(FCP_HWDEP_MINOR << 8) | \
|
||||
FCP_HWDEP_SUBMINOR)
|
||||
|
||||
#define FCP_HWDEP_VERSION_MAJOR(v) (((v) >> 16) & 0xFF)
|
||||
#define FCP_HWDEP_VERSION_MINOR(v) (((v) >> 8) & 0xFF)
|
||||
#define FCP_HWDEP_VERSION_SUBMINOR(v) ((v) & 0xFF)
|
||||
|
||||
/* Get protocol version */
|
||||
#define FCP_IOCTL_PVERSION _IOR('S', 0x60, int)
|
||||
|
||||
/* Start the protocol */
|
||||
|
||||
/* Step 0 and step 2 responses are variable length and placed in
|
||||
* resp[] one after the other.
|
||||
*/
|
||||
struct fcp_init {
|
||||
__u16 step0_resp_size;
|
||||
__u16 step2_resp_size;
|
||||
__u32 init1_opcode;
|
||||
__u32 init2_opcode;
|
||||
__u8 resp[];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define FCP_IOCTL_INIT _IOWR('S', 0x64, struct fcp_init)
|
||||
|
||||
/* Perform a command */
|
||||
|
||||
/* The request data is placed in data[] and the response data will
|
||||
* overwrite it.
|
||||
*/
|
||||
struct fcp_cmd {
|
||||
__u32 opcode;
|
||||
__u16 req_size;
|
||||
__u16 resp_size;
|
||||
__u8 data[];
|
||||
} __attribute__((packed));
|
||||
#define FCP_IOCTL_CMD _IOWR('S', 0x65, struct fcp_cmd)
|
||||
|
||||
/* Set the meter map */
|
||||
struct fcp_meter_map {
|
||||
__u16 map_size;
|
||||
__u16 meter_slots;
|
||||
__s16 map[];
|
||||
} __attribute__((packed));
|
||||
#define FCP_IOCTL_SET_METER_MAP _IOW('S', 0x66, struct fcp_meter_map)
|
||||
|
||||
/* Set the meter labels */
|
||||
struct fcp_meter_labels {
|
||||
__u16 labels_size;
|
||||
char labels[];
|
||||
} __attribute__((packed));
|
||||
#define FCP_IOCTL_SET_METER_LABELS _IOW('S', 0x67, struct fcp_meter_labels)
|
||||
|
||||
#endif /* __UAPI_SOUND_FCP_H */
|
|
@ -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
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */
|
||||
#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */
|
||||
|
||||
#define SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS 0x110 /* channel labels */
|
||||
|
||||
/*
|
||||
* TLV structure is right behind the struct snd_ctl_tlv:
|
||||
* unsigned int type - see SNDRV_CTL_TLVT_*
|
||||
|
|
|
@ -27,38 +27,43 @@ int copy_to_user_fromio(void __user *dst, const volatile void __iomem *src, size
|
|||
|
||||
if (import_ubuf(ITER_DEST, dst, count, &iter))
|
||||
return -EFAULT;
|
||||
return copy_to_iter_fromio(&iter, (const void __iomem *)src, count);
|
||||
if (copy_to_iter_fromio((const void __iomem *)src, count, &iter) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(copy_to_user_fromio);
|
||||
|
||||
/**
|
||||
* copy_to_iter_fromio - copy data from mmio-space to iov_iter
|
||||
* @dst: the destination iov_iter
|
||||
* @src: the source pointer on mmio
|
||||
* @count: the data size to copy in bytes
|
||||
* @dst: the destination iov_iter
|
||||
*
|
||||
* Copies the data from mmio-space to iov_iter.
|
||||
*
|
||||
* Return: Zero if successful, or non-zero on failure.
|
||||
* Return: number of bytes to be copied
|
||||
*/
|
||||
int copy_to_iter_fromio(struct iov_iter *dst, const void __iomem *src,
|
||||
size_t count)
|
||||
size_t copy_to_iter_fromio(const void __iomem *src, size_t count,
|
||||
struct iov_iter *dst)
|
||||
{
|
||||
#if defined(__i386__) || defined(CONFIG_SPARC32)
|
||||
return copy_to_iter((const void __force *)src, count, dst) == count ? 0 : -EFAULT;
|
||||
return copy_to_iter((const void __force *)src, count, dst);
|
||||
#else
|
||||
char buf[256];
|
||||
size_t res = 0;
|
||||
|
||||
while (count) {
|
||||
size_t c = count;
|
||||
if (c > sizeof(buf))
|
||||
c = sizeof(buf);
|
||||
memcpy_fromio(buf, (void __iomem *)src, c);
|
||||
if (copy_to_iter(buf, c, dst) != c)
|
||||
return -EFAULT;
|
||||
return res;
|
||||
count -= c;
|
||||
src += c;
|
||||
res += c;
|
||||
}
|
||||
return 0;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(copy_to_iter_fromio);
|
||||
|
@ -79,37 +84,43 @@ int copy_from_user_toio(volatile void __iomem *dst, const void __user *src, size
|
|||
|
||||
if (import_ubuf(ITER_SOURCE, (void __user *)src, count, &iter))
|
||||
return -EFAULT;
|
||||
return copy_from_iter_toio((void __iomem *)dst, &iter, count);
|
||||
if (copy_from_iter_toio((void __iomem *)dst, count, &iter) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(copy_from_user_toio);
|
||||
|
||||
/**
|
||||
* copy_from_iter_toio - copy data from iov_iter to mmio-space
|
||||
* @dst: the destination pointer on mmio-space
|
||||
* @src: the source iov_iter
|
||||
* @count: the data size to copy in bytes
|
||||
* @src: the source iov_iter
|
||||
*
|
||||
* Copies the data from iov_iter to mmio-space.
|
||||
*
|
||||
* Return: Zero if successful, or non-zero on failure.
|
||||
* Return: number of bytes to be copied
|
||||
*/
|
||||
int copy_from_iter_toio(void __iomem *dst, struct iov_iter *src, size_t count)
|
||||
size_t copy_from_iter_toio(void __iomem *dst, size_t count,
|
||||
struct iov_iter *src)
|
||||
{
|
||||
#if defined(__i386__) || defined(CONFIG_SPARC32)
|
||||
return copy_from_iter((void __force *)dst, count, src) == count ? 0 : -EFAULT;
|
||||
return copy_from_iter((void __force *)dst, count, src);
|
||||
#else
|
||||
char buf[256];
|
||||
size_t res = 0;
|
||||
|
||||
while (count) {
|
||||
size_t c = count;
|
||||
if (c > sizeof(buf))
|
||||
c = sizeof(buf);
|
||||
if (copy_from_iter(buf, c, src) != c)
|
||||
return -EFAULT;
|
||||
return res;
|
||||
memcpy_toio(dst, buf, c);
|
||||
count -= c;
|
||||
dst += c;
|
||||
res += c;
|
||||
}
|
||||
return 0;
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(copy_from_iter_toio);
|
||||
|
|
|
@ -629,12 +629,15 @@ static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
|
|||
info->subdevice = substream->number;
|
||||
info->stream = substream->stream;
|
||||
info->flags = rmidi->info_flags;
|
||||
if (substream->inactive)
|
||||
info->flags |= SNDRV_RAWMIDI_INFO_STREAM_INACTIVE;
|
||||
strcpy(info->id, rmidi->id);
|
||||
strcpy(info->name, rmidi->name);
|
||||
strcpy(info->subname, substream->name);
|
||||
info->subdevices_count = substream->pstr->substream_count;
|
||||
info->subdevices_avail = (substream->pstr->substream_count -
|
||||
substream->pstr->substream_opened);
|
||||
info->tied_device = rmidi->tied_device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ config SND_SEQ_VIRMIDI
|
|||
|
||||
config SND_SEQ_UMP
|
||||
bool "Support for UMP events"
|
||||
default y if SND_SEQ_UMP_CLIENT
|
||||
default SND_UMP
|
||||
help
|
||||
Say Y here to enable the support for handling UMP (Universal MIDI
|
||||
Packet) events via ALSA sequencer infrastructure, which is an
|
||||
|
@ -71,6 +71,6 @@ config SND_SEQ_UMP
|
|||
among legacy and UMP clients.
|
||||
|
||||
config SND_SEQ_UMP_CLIENT
|
||||
def_tristate SND_UMP
|
||||
def_tristate SND_UMP && SND_SEQ_UMP
|
||||
|
||||
endif # SND_SEQUENCER
|
||||
|
|
|
@ -55,7 +55,6 @@ struct seq_oss_chinfo {
|
|||
struct seq_oss_synthinfo {
|
||||
struct snd_seq_oss_arg arg;
|
||||
struct seq_oss_chinfo *ch;
|
||||
struct seq_oss_synth_sysex *sysex;
|
||||
int nr_voices;
|
||||
int opened;
|
||||
int is_midi;
|
||||
|
@ -157,8 +156,4 @@ snd_seq_oss_fill_addr(struct seq_oss_devinfo *dp, struct snd_seq_event *ev,
|
|||
ev->dest.port = dest_port;
|
||||
}
|
||||
|
||||
|
||||
/* misc. functions for proc interface */
|
||||
char *enabled_str(bool b);
|
||||
|
||||
#endif /* __SEQ_OSS_DEVICE_H */
|
||||
|
|
|
@ -111,7 +111,7 @@ snd_seq_oss_create_client(void)
|
|||
|
||||
|
||||
/*
|
||||
* receive annoucement from system port, and check the midi device
|
||||
* receive announcement from system port, and check the midi device
|
||||
*/
|
||||
static int
|
||||
receive_announce(struct snd_seq_event *ev, int direct, void *private, int atomic, int hop)
|
||||
|
@ -449,12 +449,6 @@ snd_seq_oss_reset(struct seq_oss_devinfo *dp)
|
|||
/*
|
||||
* misc. functions for proc interface
|
||||
*/
|
||||
char *
|
||||
enabled_str(bool b)
|
||||
{
|
||||
return b ? "enabled" : "disabled";
|
||||
}
|
||||
|
||||
static const char *
|
||||
filemode_str(int val)
|
||||
{
|
||||
|
|
|
@ -26,13 +26,6 @@
|
|||
* definition of synth info records
|
||||
*/
|
||||
|
||||
/* sysex buffer */
|
||||
struct seq_oss_synth_sysex {
|
||||
int len;
|
||||
int skip;
|
||||
unsigned char buf[MAX_SYSEX_BUFLEN];
|
||||
};
|
||||
|
||||
/* synth info */
|
||||
struct seq_oss_synth {
|
||||
int seq_device;
|
||||
|
@ -66,7 +59,6 @@ static struct seq_oss_synth midi_synth_dev = {
|
|||
};
|
||||
|
||||
static DEFINE_SPINLOCK(register_lock);
|
||||
static DEFINE_MUTEX(sysex_mutex);
|
||||
|
||||
/*
|
||||
* prototypes
|
||||
|
@ -319,8 +311,6 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
|
|||
}
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
kfree(info->sysex);
|
||||
info->sysex = NULL;
|
||||
kfree(info->ch);
|
||||
info->ch = NULL;
|
||||
}
|
||||
|
@ -396,8 +386,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
|
|||
info = get_synthinfo_nospec(dp, dev);
|
||||
if (!info || !info->opened)
|
||||
return;
|
||||
if (info->sysex)
|
||||
info->sysex->len = 0; /* reset sysex */
|
||||
reset_channels(info);
|
||||
if (info->is_midi) {
|
||||
if (midi_synth_dev.opened <= 0)
|
||||
|
@ -409,8 +397,6 @@ snd_seq_oss_synth_reset(struct seq_oss_devinfo *dp, int dev)
|
|||
dp->file_mode) < 0) {
|
||||
midi_synth_dev.opened--;
|
||||
info->opened = 0;
|
||||
kfree(info->sysex);
|
||||
info->sysex = NULL;
|
||||
kfree(info->ch);
|
||||
info->ch = NULL;
|
||||
}
|
||||
|
@ -483,64 +469,26 @@ snd_seq_oss_synth_info(struct seq_oss_devinfo *dp, int dev)
|
|||
|
||||
/*
|
||||
* receive OSS 6 byte sysex packet:
|
||||
* the full sysex message will be sent if it reaches to the end of data
|
||||
* (0xff).
|
||||
* the event is filled and prepared for sending immediately
|
||||
* (i.e. sysex messages are fragmented)
|
||||
*/
|
||||
int
|
||||
snd_seq_oss_synth_sysex(struct seq_oss_devinfo *dp, int dev, unsigned char *buf, struct snd_seq_event *ev)
|
||||
{
|
||||
int i, send;
|
||||
unsigned char *dest;
|
||||
struct seq_oss_synth_sysex *sysex;
|
||||
struct seq_oss_synthinfo *info;
|
||||
unsigned char *p;
|
||||
int len = 6;
|
||||
|
||||
info = snd_seq_oss_synth_info(dp, dev);
|
||||
if (!info)
|
||||
return -ENXIO;
|
||||
p = memchr(buf, 0xff, 6);
|
||||
if (p)
|
||||
len = p - buf + 1;
|
||||
|
||||
guard(mutex)(&sysex_mutex);
|
||||
sysex = info->sysex;
|
||||
if (sysex == NULL) {
|
||||
sysex = kzalloc(sizeof(*sysex), GFP_KERNEL);
|
||||
if (sysex == NULL)
|
||||
return -ENOMEM;
|
||||
info->sysex = sysex;
|
||||
}
|
||||
|
||||
send = 0;
|
||||
dest = sysex->buf + sysex->len;
|
||||
/* copy 6 byte packet to the buffer */
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (buf[i] == 0xff) {
|
||||
send = 1;
|
||||
break;
|
||||
}
|
||||
dest[i] = buf[i];
|
||||
sysex->len++;
|
||||
if (sysex->len >= MAX_SYSEX_BUFLEN) {
|
||||
sysex->len = 0;
|
||||
sysex->skip = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sysex->len && send) {
|
||||
if (sysex->skip) {
|
||||
sysex->skip = 0;
|
||||
sysex->len = 0;
|
||||
return -EINVAL; /* skip */
|
||||
}
|
||||
/* copy the data to event record and send it */
|
||||
ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
|
||||
if (snd_seq_oss_synth_addr(dp, dev, ev))
|
||||
return -EINVAL;
|
||||
ev->data.ext.len = sysex->len;
|
||||
ev->data.ext.ptr = sysex->buf;
|
||||
sysex->len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL; /* skip */
|
||||
/* copy the data to event record and send it */
|
||||
if (snd_seq_oss_synth_addr(dp, dev, ev))
|
||||
return -EINVAL;
|
||||
ev->flags = SNDRV_SEQ_EVENT_LENGTH_VARIABLE;
|
||||
ev->data.ext.len = len;
|
||||
ev->data.ext.ptr = buf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -660,8 +608,8 @@ snd_seq_oss_synth_info_read(struct snd_info_buffer *buf)
|
|||
rec->synth_type, rec->synth_subtype,
|
||||
rec->nr_voices);
|
||||
snd_iprintf(buf, " capabilities : ioctl %s / load_patch %s\n",
|
||||
enabled_str((long)rec->oper.ioctl),
|
||||
enabled_str((long)rec->oper.load_patch));
|
||||
str_enabled_disabled((long)rec->oper.ioctl),
|
||||
str_enabled_disabled((long)rec->oper.load_patch));
|
||||
snd_use_lock_free(&rec->use_lock);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1296,6 +1296,10 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
|
|||
client->midi_version = client_info->midi_version;
|
||||
memcpy(client->event_filter, client_info->event_filter, 32);
|
||||
client->group_filter = client_info->group_filter;
|
||||
|
||||
/* notify the change */
|
||||
snd_seq_system_client_ev_client_change(client->number);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1419,6 +1423,9 @@ static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
|
|||
if (port) {
|
||||
snd_seq_set_port_info(port, info);
|
||||
snd_seq_port_unlock(port);
|
||||
/* notify the change */
|
||||
snd_seq_system_client_ev_port_change(info->addr.client,
|
||||
info->addr.port);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1475,7 +1482,7 @@ int snd_seq_client_notify_subscription(int client, int port,
|
|||
event.data.connect.dest = info->dest;
|
||||
event.data.connect.sender = info->sender;
|
||||
|
||||
return snd_seq_system_notify(client, port, &event); /* non-atomic */
|
||||
return snd_seq_system_notify(client, port, &event, false); /* non-atomic */
|
||||
}
|
||||
|
||||
|
||||
|
@ -2229,6 +2236,16 @@ static int snd_seq_ioctl_client_ump_info(struct snd_seq_client *caller,
|
|||
error:
|
||||
mutex_unlock(&cptr->ioctl_mutex);
|
||||
snd_seq_client_unlock(cptr);
|
||||
if (!err && cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_UMP_INFO) {
|
||||
if (type == SNDRV_SEQ_CLIENT_UMP_INFO_ENDPOINT)
|
||||
snd_seq_system_ump_notify(client, 0,
|
||||
SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
|
||||
false);
|
||||
else
|
||||
snd_seq_system_ump_notify(client, type - 1,
|
||||
SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE,
|
||||
false);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -49,12 +49,14 @@ static int sysclient = -1;
|
|||
/* port id numbers for this client */
|
||||
static int announce_port = -1;
|
||||
|
||||
/* number of subscriptions to announce port */
|
||||
static int announce_subscribed;
|
||||
|
||||
|
||||
/* fill standard header data, source port & channel are filled in */
|
||||
static int setheader(struct snd_seq_event * ev, int client, int port)
|
||||
{
|
||||
if (announce_port < 0)
|
||||
if (announce_port < 0 || !announce_subscribed)
|
||||
return -ENODEV;
|
||||
|
||||
memset(ev, 0, sizeof(struct snd_seq_event));
|
||||
|
@ -76,26 +78,27 @@ static int setheader(struct snd_seq_event * ev, int client, int port)
|
|||
|
||||
|
||||
/* entry points for broadcasting system events */
|
||||
void snd_seq_system_broadcast(int client, int port, int type)
|
||||
void snd_seq_system_broadcast(int client, int port, int type, bool atomic)
|
||||
{
|
||||
struct snd_seq_event ev;
|
||||
|
||||
if (setheader(&ev, client, port) < 0)
|
||||
return;
|
||||
ev.type = type;
|
||||
snd_seq_kernel_client_dispatch(sysclient, &ev, 0, 0);
|
||||
snd_seq_kernel_client_dispatch(sysclient, &ev, atomic, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_seq_system_broadcast);
|
||||
|
||||
/* entry points for broadcasting system events */
|
||||
int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev)
|
||||
int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
|
||||
bool atomic)
|
||||
{
|
||||
ev->flags = SNDRV_SEQ_EVENT_LENGTH_FIXED;
|
||||
ev->source.client = sysclient;
|
||||
ev->source.port = announce_port;
|
||||
ev->dest.client = client;
|
||||
ev->dest.port = port;
|
||||
return snd_seq_kernel_client_dispatch(sysclient, ev, 0, 0);
|
||||
return snd_seq_kernel_client_dispatch(sysclient, ev, atomic, 0);
|
||||
}
|
||||
|
||||
/* call-back handler for timer events */
|
||||
|
@ -104,6 +107,22 @@ static int event_input_timer(struct snd_seq_event * ev, int direct, void *privat
|
|||
return snd_seq_control_queue(ev, atomic, hop);
|
||||
}
|
||||
|
||||
static int sys_announce_subscribe(void *private_data,
|
||||
struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
announce_subscribed++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sys_announce_unsubscribe(void *private_data,
|
||||
struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
if (snd_BUG_ON(!announce_subscribed))
|
||||
return 0;
|
||||
announce_subscribed--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* register our internal client */
|
||||
int __init snd_seq_system_client_init(void)
|
||||
{
|
||||
|
@ -143,7 +162,10 @@ int __init snd_seq_system_client_init(void)
|
|||
/* register announcement port */
|
||||
strcpy(port->name, "Announce");
|
||||
port->capability = SNDRV_SEQ_PORT_CAP_READ|SNDRV_SEQ_PORT_CAP_SUBS_READ; /* for broadcast only */
|
||||
port->kernel = NULL;
|
||||
pcallbacks.event_input = NULL;
|
||||
pcallbacks.subscribe = sys_announce_subscribe;
|
||||
pcallbacks.unsubscribe = sys_announce_unsubscribe;
|
||||
port->kernel = &pcallbacks;
|
||||
port->type = 0;
|
||||
port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT;
|
||||
port->addr.client = sysclient;
|
||||
|
|
|
@ -10,16 +10,31 @@
|
|||
|
||||
|
||||
/* entry points for broadcasting system events */
|
||||
void snd_seq_system_broadcast(int client, int port, int type);
|
||||
void snd_seq_system_broadcast(int client, int port, int type, bool atomic);
|
||||
|
||||
#define snd_seq_system_client_ev_client_start(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
|
||||
#define snd_seq_system_client_ev_client_exit(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
|
||||
#define snd_seq_system_client_ev_client_change(client) snd_seq_system_broadcast(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
|
||||
#define snd_seq_system_client_ev_port_start(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_START)
|
||||
#define snd_seq_system_client_ev_port_exit(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
|
||||
#define snd_seq_system_client_ev_port_change(client, port) snd_seq_system_broadcast(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
|
||||
/* normal system notification event broadcast */
|
||||
#define notify_event(client, port, type) \
|
||||
snd_seq_system_broadcast(client, port, type, false)
|
||||
|
||||
int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev);
|
||||
/* notify UMP EP/FB change event */
|
||||
static inline void snd_seq_system_ump_notify(int client, int block, int type,
|
||||
bool atomic)
|
||||
{
|
||||
/* reuse the existing snd_seq_system_broadcast():
|
||||
* struct snd_seq_ev_ump_notify is compatible with struct snd_seq_addr
|
||||
*/
|
||||
snd_seq_system_broadcast(client, block, type, atomic);
|
||||
}
|
||||
|
||||
#define snd_seq_system_client_ev_client_start(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_START)
|
||||
#define snd_seq_system_client_ev_client_exit(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_EXIT)
|
||||
#define snd_seq_system_client_ev_client_change(client) notify_event(client, 0, SNDRV_SEQ_EVENT_CLIENT_CHANGE)
|
||||
#define snd_seq_system_client_ev_port_start(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_START)
|
||||
#define snd_seq_system_client_ev_port_exit(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_EXIT)
|
||||
#define snd_seq_system_client_ev_port_change(client, port) notify_event(client, port, SNDRV_SEQ_EVENT_PORT_CHANGE)
|
||||
|
||||
int snd_seq_system_notify(int client, int port, struct snd_seq_event *ev,
|
||||
bool atomic);
|
||||
|
||||
/* register our internal client */
|
||||
int snd_seq_system_client_init(void);
|
||||
|
|
|
@ -272,8 +272,6 @@ static void update_port_infos(struct seq_ump_client *client)
|
|||
new);
|
||||
if (err < 0)
|
||||
continue;
|
||||
/* notify to system port */
|
||||
snd_seq_system_client_ev_port_change(client->seq_client, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,6 +388,33 @@ static void handle_group_notify(struct work_struct *work)
|
|||
setup_client_group_filter(client);
|
||||
}
|
||||
|
||||
/* UMP EP change notification */
|
||||
static int seq_ump_notify_ep_change(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
struct seq_ump_client *client = ump->seq_client;
|
||||
struct snd_seq_client *cptr;
|
||||
int client_id;
|
||||
|
||||
if (!client)
|
||||
return -ENODEV;
|
||||
client_id = client->seq_client;
|
||||
cptr = snd_seq_kernel_client_get(client_id);
|
||||
if (!cptr)
|
||||
return -ENODEV;
|
||||
|
||||
snd_seq_system_ump_notify(client_id, 0, SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
|
||||
true);
|
||||
|
||||
/* update sequencer client name if needed */
|
||||
if (*ump->core.name && strcmp(ump->core.name, cptr->name)) {
|
||||
strscpy(cptr->name, ump->core.name, sizeof(cptr->name));
|
||||
snd_seq_system_client_ev_client_change(client_id);
|
||||
}
|
||||
|
||||
snd_seq_kernel_client_put(cptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* UMP FB change notification */
|
||||
static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump,
|
||||
struct snd_ump_block *fb)
|
||||
|
@ -399,20 +424,29 @@ static int seq_ump_notify_fb_change(struct snd_ump_endpoint *ump,
|
|||
if (!client)
|
||||
return -ENODEV;
|
||||
schedule_work(&client->group_notify_work);
|
||||
snd_seq_system_ump_notify(client->seq_client, fb->info.block_id,
|
||||
SNDRV_SEQ_EVENT_UMP_BLOCK_CHANGE,
|
||||
true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* UMP protocol change notification; just update the midi_version field */
|
||||
static int seq_ump_switch_protocol(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
if (!ump->seq_client)
|
||||
struct seq_ump_client *client = ump->seq_client;
|
||||
|
||||
if (!client)
|
||||
return -ENODEV;
|
||||
setup_client_midi_version(ump->seq_client);
|
||||
setup_client_midi_version(client);
|
||||
snd_seq_system_ump_notify(client->seq_client, 0,
|
||||
SNDRV_SEQ_EVENT_UMP_EP_CHANGE,
|
||||
true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_seq_ump_ops seq_ump_ops = {
|
||||
.input_receive = seq_ump_input_receive,
|
||||
.notify_ep_change = seq_ump_notify_ep_change,
|
||||
.notify_fb_change = seq_ump_notify_fb_change,
|
||||
.switch_protocol = seq_ump_switch_protocol,
|
||||
};
|
||||
|
|
112
sound/core/ump.c
112
sound/core/ump.c
|
@ -37,6 +37,7 @@ static int process_legacy_output(struct snd_ump_endpoint *ump,
|
|||
u32 *buffer, int count);
|
||||
static void process_legacy_input(struct snd_ump_endpoint *ump, const u32 *src,
|
||||
int words);
|
||||
static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump);
|
||||
static void update_legacy_names(struct snd_ump_endpoint *ump);
|
||||
#else
|
||||
static inline int process_legacy_output(struct snd_ump_endpoint *ump,
|
||||
|
@ -48,11 +49,42 @@ static inline void process_legacy_input(struct snd_ump_endpoint *ump,
|
|||
const u32 *src, int words)
|
||||
{
|
||||
}
|
||||
static inline void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
}
|
||||
static inline void update_legacy_names(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* copy a string safely with stripping non-printable letters */
|
||||
static void safe_copy_string(void *dst, size_t max_dst_size,
|
||||
const void *src, size_t max_src_size)
|
||||
{
|
||||
const unsigned char *s = src;
|
||||
unsigned char *d = dst;
|
||||
|
||||
if (!max_dst_size--)
|
||||
return;
|
||||
for (s = src; max_dst_size && *s && max_src_size--; s++) {
|
||||
if (!isascii(*s) || !isprint(*s))
|
||||
continue;
|
||||
*d++ = *s;
|
||||
max_dst_size--;
|
||||
}
|
||||
*d = 0;
|
||||
}
|
||||
|
||||
/* append a string safely with stripping non-printable letters */
|
||||
static void safe_append_string(void *dst, size_t max_dst_size,
|
||||
const void *src, size_t max_src_size)
|
||||
{
|
||||
unsigned char *d = dst;
|
||||
size_t len = strlen(d);
|
||||
|
||||
safe_copy_string(d + len, max_dst_size - len, src, max_src_size);
|
||||
}
|
||||
|
||||
static const struct snd_rawmidi_global_ops snd_ump_rawmidi_ops = {
|
||||
.dev_register = snd_ump_dev_register,
|
||||
.dev_unregister = snd_ump_dev_unregister,
|
||||
|
@ -565,16 +597,10 @@ void snd_ump_update_group_attrs(struct snd_ump_endpoint *ump)
|
|||
}
|
||||
if (!*fb->info.name)
|
||||
continue;
|
||||
if (!*group->name) {
|
||||
/* store the first matching name */
|
||||
strscpy(group->name, fb->info.name,
|
||||
sizeof(group->name));
|
||||
} else {
|
||||
/* when overlapping, concat names */
|
||||
if (*group->name)
|
||||
strlcat(group->name, ", ", sizeof(group->name));
|
||||
strlcat(group->name, fb->info.name,
|
||||
sizeof(group->name));
|
||||
}
|
||||
safe_append_string(group->name, sizeof(group->name),
|
||||
fb->info.name, sizeof(fb->info.name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -669,6 +695,15 @@ static void choose_default_protocol(struct snd_ump_endpoint *ump)
|
|||
ump->info.protocol |= SNDRV_UMP_EP_INFO_PROTO_MIDI1;
|
||||
}
|
||||
|
||||
/* notify the EP info/name change to sequencer */
|
||||
static void seq_notify_ep_change(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SND_SEQUENCER)
|
||||
if (ump->parsed && ump->seq_ops && ump->seq_ops->notify_ep_change)
|
||||
ump->seq_ops->notify_ep_change(ump);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* handle EP info stream message; update the UMP attributes */
|
||||
static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
|
||||
const union snd_ump_stream_msg *buf)
|
||||
|
@ -693,6 +728,7 @@ static int ump_handle_ep_info_msg(struct snd_ump_endpoint *ump,
|
|||
|
||||
ump->info.protocol &= ump->info.protocol_caps;
|
||||
choose_default_protocol(ump);
|
||||
seq_notify_ep_change(ump);
|
||||
|
||||
return 1; /* finished */
|
||||
}
|
||||
|
@ -715,24 +751,46 @@ static int ump_handle_device_info_msg(struct snd_ump_endpoint *ump,
|
|||
ump->info.family_id,
|
||||
ump->info.model_id,
|
||||
ump->info.sw_revision);
|
||||
seq_notify_ep_change(ump);
|
||||
return 1; /* finished */
|
||||
}
|
||||
|
||||
/* set up the core rawmidi name from UMP EP name string */
|
||||
static void ump_set_rawmidi_name(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
safe_copy_string(ump->core.name, sizeof(ump->core.name),
|
||||
ump->info.name, sizeof(ump->info.name));
|
||||
}
|
||||
|
||||
/* handle EP name stream message; update the UMP name string */
|
||||
static int ump_handle_ep_name_msg(struct snd_ump_endpoint *ump,
|
||||
const union snd_ump_stream_msg *buf)
|
||||
{
|
||||
return ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
|
||||
buf->raw, 2);
|
||||
int ret;
|
||||
|
||||
ret = ump_append_string(ump, ump->info.name, sizeof(ump->info.name),
|
||||
buf->raw, 2);
|
||||
if (ret && ump->parsed) {
|
||||
ump_set_rawmidi_name(ump);
|
||||
ump_legacy_set_rawmidi_name(ump);
|
||||
seq_notify_ep_change(ump);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* handle EP product id stream message; update the UMP product_id string */
|
||||
static int ump_handle_product_id_msg(struct snd_ump_endpoint *ump,
|
||||
const union snd_ump_stream_msg *buf)
|
||||
{
|
||||
return ump_append_string(ump, ump->info.product_id,
|
||||
sizeof(ump->info.product_id),
|
||||
buf->raw, 2);
|
||||
int ret;
|
||||
|
||||
ret = ump_append_string(ump, ump->info.product_id,
|
||||
sizeof(ump->info.product_id),
|
||||
buf->raw, 2);
|
||||
if (ret)
|
||||
seq_notify_ep_change(ump);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* notify the protocol change to sequencer */
|
||||
|
@ -1045,6 +1103,8 @@ int snd_ump_parse_endpoint(struct snd_ump_endpoint *ump)
|
|||
if (err < 0)
|
||||
ump_dbg(ump, "Unable to get UMP EP name string\n");
|
||||
|
||||
ump_set_rawmidi_name(ump);
|
||||
|
||||
/* Request Endpoint Product ID */
|
||||
err = ump_req_msg(ump, msg, UMP_STREAM_MSG_REQUEST_PRODUCT_ID,
|
||||
UMP_STREAM_MSG_STATUS_PRODUCT_ID);
|
||||
|
@ -1250,8 +1310,8 @@ static int fill_legacy_mapping(struct snd_ump_endpoint *ump)
|
|||
return num;
|
||||
}
|
||||
|
||||
static void fill_substream_names(struct snd_ump_endpoint *ump,
|
||||
struct snd_rawmidi *rmidi, int dir)
|
||||
static void update_legacy_substreams(struct snd_ump_endpoint *ump,
|
||||
struct snd_rawmidi *rmidi, int dir)
|
||||
{
|
||||
struct snd_rawmidi_substream *s;
|
||||
const char *name;
|
||||
|
@ -1261,10 +1321,11 @@ static void fill_substream_names(struct snd_ump_endpoint *ump,
|
|||
idx = ump->legacy_mapping[s->number];
|
||||
name = ump->groups[idx].name;
|
||||
if (!*name)
|
||||
name = ump->info.name;
|
||||
name = ump->core.name;
|
||||
scnprintf(s->name, sizeof(s->name), "Group %d (%.16s)%s",
|
||||
idx + 1, name,
|
||||
ump->groups[idx].active ? "" : " [Inactive]");
|
||||
s->inactive = !ump->groups[idx].active;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1272,8 +1333,16 @@ static void update_legacy_names(struct snd_ump_endpoint *ump)
|
|||
{
|
||||
struct snd_rawmidi *rmidi = ump->legacy_rmidi;
|
||||
|
||||
fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
|
||||
fill_substream_names(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
|
||||
update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_INPUT);
|
||||
update_legacy_substreams(ump, rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT);
|
||||
}
|
||||
|
||||
static void ump_legacy_set_rawmidi_name(struct snd_ump_endpoint *ump)
|
||||
{
|
||||
struct snd_rawmidi *rmidi = ump->legacy_rmidi;
|
||||
|
||||
snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
|
||||
ump->core.name);
|
||||
}
|
||||
|
||||
int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
|
||||
|
@ -1306,14 +1375,15 @@ int snd_ump_attach_legacy_rawmidi(struct snd_ump_endpoint *ump,
|
|||
if (output)
|
||||
snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
|
||||
&snd_ump_legacy_output_ops);
|
||||
snprintf(rmidi->name, sizeof(rmidi->name), "%.68s (MIDI 1.0)",
|
||||
ump->info.name);
|
||||
rmidi->info_flags = ump->core.info_flags & ~SNDRV_RAWMIDI_INFO_UMP;
|
||||
rmidi->ops = &snd_ump_legacy_ops;
|
||||
rmidi->private_data = ump;
|
||||
ump->legacy_rmidi = rmidi;
|
||||
ump_legacy_set_rawmidi_name(ump);
|
||||
update_legacy_names(ump);
|
||||
|
||||
snd_rawmidi_tie_devices(rmidi, &ump->core);
|
||||
|
||||
ump_dbg(ump, "Created a legacy rawmidi #%d (%s)\n", device, id);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -135,13 +135,13 @@ static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
|
|||
|
||||
snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
|
||||
(data & 0x00000020) ? "Professional" : "Consumer",
|
||||
(data & 0x00000040) ? "on" : "off");
|
||||
str_on_off(data & 0x00000040));
|
||||
|
||||
snd_iprintf(buffer, "Optical output interface format: %s\n",
|
||||
(data & 0x00000100) ? "S/PDIF" : "ADAT");
|
||||
|
||||
snd_iprintf(buffer, "Word output single speed: %s\n",
|
||||
(data & 0x00002000) ? "on" : "off");
|
||||
str_on_off(data & 0x00002000));
|
||||
|
||||
snd_iprintf(buffer, "S/PDIF input interface: %s\n",
|
||||
(data & 0x00000200) ? "Optical" : "Coaxial");
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/hdaudio.h>
|
||||
#include <sound/hda_component.h>
|
||||
|
@ -42,8 +43,7 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
|
|||
if (!acomp->ops->codec_wake_override)
|
||||
return 0;
|
||||
|
||||
dev_dbg(bus->dev, "%s codec wakeup\n",
|
||||
enable ? "enable" : "disable");
|
||||
dev_dbg(bus->dev, "%s codec wakeup\n", str_enable_disable(enable));
|
||||
|
||||
acomp->ops->codec_wake_override(acomp->dev, enable);
|
||||
|
||||
|
@ -67,8 +67,7 @@ void snd_hdac_display_power(struct hdac_bus *bus, unsigned int idx, bool enable)
|
|||
{
|
||||
struct drm_audio_component *acomp = bus->audio_component;
|
||||
|
||||
dev_dbg(bus->dev, "display power %s\n",
|
||||
enable ? "enable" : "disable");
|
||||
dev_dbg(bus->dev, "display power %s\n", str_enable_disable(enable));
|
||||
|
||||
mutex_lock(&bus->lock);
|
||||
if (enable)
|
||||
|
|
|
@ -492,32 +492,21 @@ static int setup_bdle(struct hdac_bus *bus,
|
|||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_stream_setup_periods - set up BDL entries
|
||||
* snd_hdac_stream_setup_bdle - set up BDL entries
|
||||
* @azx_dev: HD-audio core stream to set up
|
||||
* @dmab: allocated DMA buffer
|
||||
* @runtime: substream runtime, optional
|
||||
*
|
||||
* Set up the buffer descriptor table of the given stream based on the
|
||||
* period and buffer sizes of the assigned PCM substream.
|
||||
*/
|
||||
int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
static int snd_hdac_stream_setup_bdle(struct hdac_stream *azx_dev, struct snd_dma_buffer *dmab,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
struct hdac_bus *bus = azx_dev->bus;
|
||||
struct snd_pcm_substream *substream = azx_dev->substream;
|
||||
struct snd_compr_stream *cstream = azx_dev->cstream;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
struct snd_dma_buffer *dmab;
|
||||
__le32 *bdl;
|
||||
int i, ofs, periods, period_bytes;
|
||||
int pos_adj, pos_align;
|
||||
|
||||
if (substream) {
|
||||
runtime = substream->runtime;
|
||||
dmab = snd_pcm_get_dma_buf(substream);
|
||||
} else if (cstream) {
|
||||
dmab = snd_pcm_get_dma_buf(cstream);
|
||||
} else {
|
||||
WARN(1, "No substream or cstream assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
__le32 *bdl;
|
||||
|
||||
/* reset BDL address */
|
||||
snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
|
||||
|
@ -571,6 +560,33 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
|||
azx_dev->bufsize, period_bytes);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_hdac_stream_setup_periods - set up BDL entries
|
||||
* @azx_dev: HD-audio core stream to set up
|
||||
*
|
||||
* Set up the buffer descriptor table of the given stream based on the
|
||||
* period and buffer sizes of the assigned PCM substream.
|
||||
*/
|
||||
int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
|
||||
{
|
||||
struct snd_pcm_substream *substream = azx_dev->substream;
|
||||
struct snd_compr_stream *cstream = azx_dev->cstream;
|
||||
struct snd_pcm_runtime *runtime = NULL;
|
||||
struct snd_dma_buffer *dmab;
|
||||
|
||||
if (substream) {
|
||||
runtime = substream->runtime;
|
||||
dmab = snd_pcm_get_dma_buf(substream);
|
||||
} else if (cstream) {
|
||||
dmab = snd_pcm_get_dma_buf(cstream);
|
||||
} else {
|
||||
WARN(1, "No substream or cstream assigned\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return snd_hdac_stream_setup_bdle(azx_dev, dmab, runtime);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
|
||||
|
||||
/**
|
||||
|
@ -923,7 +939,6 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
|
|||
unsigned int byte_size, struct snd_dma_buffer *bufp)
|
||||
{
|
||||
struct hdac_bus *bus = azx_dev->bus;
|
||||
__le32 *bdl;
|
||||
int err;
|
||||
|
||||
snd_hdac_dsp_lock(azx_dev);
|
||||
|
@ -943,18 +958,14 @@ int snd_hdac_dsp_prepare(struct hdac_stream *azx_dev, unsigned int format,
|
|||
|
||||
azx_dev->substream = NULL;
|
||||
azx_dev->bufsize = byte_size;
|
||||
azx_dev->period_bytes = byte_size;
|
||||
/* It is recommended to transfer the firmware in two or more chunks. */
|
||||
azx_dev->period_bytes = byte_size / 2;
|
||||
azx_dev->format_val = format;
|
||||
azx_dev->no_period_wakeup = 1;
|
||||
|
||||
snd_hdac_stream_reset(azx_dev);
|
||||
|
||||
/* reset BDL address */
|
||||
snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0);
|
||||
snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0);
|
||||
|
||||
azx_dev->frags = 0;
|
||||
bdl = (__le32 *)azx_dev->bdl.area;
|
||||
err = setup_bdle(bus, bufp, azx_dev, &bdl, 0, byte_size, 0);
|
||||
err = snd_hdac_stream_setup_bdle(azx_dev, bufp, NULL);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/control.h>
|
||||
#include <sound/info.h>
|
||||
|
@ -1157,8 +1158,8 @@ static void info_read(struct snd_info_entry *entry, struct snd_info_buffer *buff
|
|||
((p->acc_rates & SNDRV_SB_CSP_RATE_44100) ? "44100Hz" : ""));
|
||||
}
|
||||
if (p->mode == SNDRV_SB_CSP_MODE_QSOUND) {
|
||||
snd_iprintf(buffer, "QSound decoder %sabled\n",
|
||||
p->q_enabled ? "en" : "dis");
|
||||
snd_iprintf(buffer, "QSound decoder %s\n",
|
||||
str_enabled_disabled(p->q_enabled));
|
||||
} else {
|
||||
snd_iprintf(buffer, "PCM format ID: 0x%x (%s/%s) [%s/%s] [%s/%s]\n",
|
||||
p->acc_format,
|
||||
|
|
|
@ -1864,7 +1864,7 @@ void snd_ac97_get_name(struct snd_ac97 *ac97, unsigned int id, char *name, int m
|
|||
strcat(name, " ");
|
||||
strcat(name, pid->name);
|
||||
if (pid->mask != 0xffffffff)
|
||||
sprintf(name + strlen(name), " rev %d", id & ~pid->mask);
|
||||
sprintf(name + strlen(name), " rev %u", id & ~pid->mask);
|
||||
if (ac97 && pid->patch) {
|
||||
if ((modem && (pid->flags & AC97_MODEM_PATCH)) ||
|
||||
(! modem && ! (pid->flags & AC97_MODEM_PATCH)))
|
||||
|
|
|
@ -161,12 +161,12 @@ static void snd_ac97_proc_read_main(struct snd_ac97 *ac97, struct snd_info_buffe
|
|||
"Mic select : %s\n"
|
||||
"ADC/DAC loopback : %s\n",
|
||||
val & 0x8000 ? "post" : "pre",
|
||||
val & 0x4000 ? "on" : "off",
|
||||
val & 0x2000 ? "on" : "off",
|
||||
val & 0x1000 ? "on" : "off",
|
||||
str_on_off(val & 0x4000),
|
||||
str_on_off(val & 0x2000),
|
||||
str_on_off(val & 0x1000),
|
||||
val & 0x0200 ? "Mic" : "MIX",
|
||||
val & 0x0100 ? "Mic2" : "Mic1",
|
||||
val & 0x0080 ? "on" : "off");
|
||||
str_on_off(val & 0x0080));
|
||||
if (ac97->ext_id & AC97_EI_DRA)
|
||||
snd_iprintf(buffer, "Double rate slots: %s\n",
|
||||
double_rate_slots[(val >> 10) & 3]);
|
||||
|
|
|
@ -626,7 +626,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
|
|||
|
||||
reg = ad1889_readw(chip, AD_DS_WSMC);
|
||||
snd_iprintf(buffer, "Wave output: %s\n",
|
||||
(reg & AD_DS_WSMC_WAEN) ? "enabled" : "disabled");
|
||||
str_enabled_disabled(reg & AD_DS_WSMC_WAEN));
|
||||
snd_iprintf(buffer, "Wave Channels: %s\n",
|
||||
(reg & AD_DS_WSMC_WAST) ? "stereo" : "mono");
|
||||
snd_iprintf(buffer, "Wave Quality: %d-bit linear\n",
|
||||
|
@ -642,7 +642,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
|
|||
|
||||
|
||||
snd_iprintf(buffer, "Synthesis output: %s\n",
|
||||
reg & AD_DS_WSMC_SYEN ? "enabled" : "disabled");
|
||||
str_enabled_disabled(reg & AD_DS_WSMC_SYEN));
|
||||
|
||||
/* SYRQ is at offset 4 */
|
||||
tmp = (reg & AD_DS_WSMC_SYRQ) ?
|
||||
|
@ -654,7 +654,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
|
|||
|
||||
reg = ad1889_readw(chip, AD_DS_RAMC);
|
||||
snd_iprintf(buffer, "ADC input: %s\n",
|
||||
(reg & AD_DS_RAMC_ADEN) ? "enabled" : "disabled");
|
||||
str_enabled_disabled(reg & AD_DS_RAMC_ADEN));
|
||||
snd_iprintf(buffer, "ADC Channels: %s\n",
|
||||
(reg & AD_DS_RAMC_ADST) ? "stereo" : "mono");
|
||||
snd_iprintf(buffer, "ADC Quality: %d-bit linear\n",
|
||||
|
@ -669,7 +669,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
|
|||
(reg & AD_DS_RAMC_ADST) ? "stereo" : "mono");
|
||||
|
||||
snd_iprintf(buffer, "Resampler input: %s\n",
|
||||
reg & AD_DS_RAMC_REEN ? "enabled" : "disabled");
|
||||
str_enabled_disabled(reg & AD_DS_RAMC_REEN));
|
||||
|
||||
/* RERQ is at offset 12 */
|
||||
tmp = (reg & AD_DS_RAMC_RERQ) ?
|
||||
|
|
|
@ -3084,7 +3084,7 @@ static int snd_cmipci_create(struct snd_card *card, struct pci_dev *pci,
|
|||
}
|
||||
}
|
||||
}
|
||||
sprintf(card->shortname, "C-Media CMI%d", val);
|
||||
sprintf(card->shortname, "C-Media CMI%u", val);
|
||||
if (cm->chip_version < 68)
|
||||
scnprintf(modelstr, sizeof(modelstr),
|
||||
" (model %d)", cm->chip_version);
|
||||
|
|
|
@ -211,52 +211,30 @@ static int dao_set_right_input(struct dao *dao, struct rsc *input)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dao_clear_left_input(struct dao *dao)
|
||||
static int dao_clear_input(struct dao *dao, unsigned int start, unsigned int end)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
unsigned int i;
|
||||
|
||||
if (!dao->imappers[0])
|
||||
if (!dao->imappers[start])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[0];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
/* Program conjugate resources */
|
||||
for (i = 1; i < daio->rscl.msr; i++) {
|
||||
entry = dao->imappers[i];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
for (i = start; i < end; i++) {
|
||||
dao->mgr->imap_delete(dao->mgr, dao->imappers[i]);
|
||||
dao->imappers[i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dao->imappers[0]);
|
||||
dao->imappers[0] = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int dao_clear_left_input(struct dao *dao)
|
||||
{
|
||||
return dao_clear_input(dao, 0, dao->daio.rscl.msr);
|
||||
}
|
||||
|
||||
static int dao_clear_right_input(struct dao *dao)
|
||||
{
|
||||
struct imapper *entry;
|
||||
struct daio *daio = &dao->daio;
|
||||
int i;
|
||||
|
||||
if (!dao->imappers[daio->rscl.msr])
|
||||
return 0;
|
||||
|
||||
entry = dao->imappers[daio->rscl.msr];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
/* Program conjugate resources */
|
||||
for (i = 1; i < daio->rscr.msr; i++) {
|
||||
entry = dao->imappers[daio->rscl.msr + i];
|
||||
dao->mgr->imap_delete(dao->mgr, entry);
|
||||
dao->imappers[daio->rscl.msr + i] = NULL;
|
||||
}
|
||||
|
||||
kfree(dao->imappers[daio->rscl.msr]);
|
||||
dao->imappers[daio->rscl.msr] = NULL;
|
||||
|
||||
return 0;
|
||||
return dao_clear_input(dao, dao->daio.rscl.msr,
|
||||
dao->daio.rscl.msr + dao->daio.rscr.msr);
|
||||
}
|
||||
|
||||
static const struct dao_rsc_ops dao_ops = {
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/string_choices.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/emu10k1.h>
|
||||
#include "p16v.h"
|
||||
|
@ -32,9 +33,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
|
|||
snd_iprintf(buffer, "\n%s\n", title);
|
||||
|
||||
if (status != 0xffffffff) {
|
||||
snd_iprintf(buffer, "Professional Mode : %s\n", (status & SPCS_PROFESSIONAL) ? "yes" : "no");
|
||||
snd_iprintf(buffer, "Not Audio Data : %s\n", (status & SPCS_NOTAUDIODATA) ? "yes" : "no");
|
||||
snd_iprintf(buffer, "Copyright : %s\n", (status & SPCS_COPYRIGHT) ? "yes" : "no");
|
||||
snd_iprintf(buffer, "Professional Mode : %s\n", str_yes_no(status & SPCS_PROFESSIONAL));
|
||||
snd_iprintf(buffer, "Not Audio Data : %s\n", str_yes_no(status & SPCS_NOTAUDIODATA));
|
||||
snd_iprintf(buffer, "Copyright : %s\n", str_yes_no(status & SPCS_COPYRIGHT));
|
||||
snd_iprintf(buffer, "Emphasis : %s\n", emphasis[(status & SPCS_EMPHASISMASK) >> 3]);
|
||||
snd_iprintf(buffer, "Mode : %i\n", (status & SPCS_MODEMASK) >> 6);
|
||||
snd_iprintf(buffer, "Category Code : 0x%x\n", (status & SPCS_CATEGORYCODEMASK) >> 8);
|
||||
|
@ -46,9 +47,9 @@ static void snd_emu10k1_proc_spdif_status(struct snd_emu10k1 * emu,
|
|||
|
||||
if (rate_reg > 0) {
|
||||
rate = snd_emu10k1_ptr_read(emu, rate_reg, 0);
|
||||
snd_iprintf(buffer, "S/PDIF Valid : %s\n", rate & SRCS_SPDIFVALID ? "on" : "off");
|
||||
snd_iprintf(buffer, "S/PDIF Locked : %s\n", rate & SRCS_SPDIFLOCKED ? "on" : "off");
|
||||
snd_iprintf(buffer, "Rate Locked : %s\n", rate & SRCS_RATELOCKED ? "on" : "off");
|
||||
snd_iprintf(buffer, "S/PDIF Valid : %s\n", str_on_off(rate & SRCS_SPDIFVALID));
|
||||
snd_iprintf(buffer, "S/PDIF Locked : %s\n", str_on_off(rate & SRCS_SPDIFLOCKED));
|
||||
snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(rate & SRCS_RATELOCKED));
|
||||
/* From ((Rate * 48000 ) / 262144); */
|
||||
snd_iprintf(buffer, "Estimated Sample Rate : %d\n", ((rate & 0xFFFFF ) * 375) >> 11);
|
||||
}
|
||||
|
@ -208,7 +209,7 @@ static void snd_emu10k1_proc_spdif_read(struct snd_info_entry *entry,
|
|||
#if 0
|
||||
val = snd_emu10k1_ptr_read(emu, ZVSRCS, 0);
|
||||
snd_iprintf(buffer, "\nZoomed Video\n");
|
||||
snd_iprintf(buffer, "Rate Locked : %s\n", val & SRCS_RATELOCKED ? "on" : "off");
|
||||
snd_iprintf(buffer, "Rate Locked : %s\n", str_on_off(val & SRCS_RATELOCKED));
|
||||
snd_iprintf(buffer, "Estimated Sample Rate : 0x%x\n", val & SRCS_ESTSAMPLERATE);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1850,12 +1850,12 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry,
|
|||
|
||||
snd_iprintf(buffer, "Ensoniq AudioPCI " CHIP_NAME "\n\n");
|
||||
snd_iprintf(buffer, "Joystick enable : %s\n",
|
||||
ensoniq->ctrl & ES_JYSTK_EN ? "on" : "off");
|
||||
str_on_off(ensoniq->ctrl & ES_JYSTK_EN));
|
||||
#ifdef CHIP1370
|
||||
snd_iprintf(buffer, "MIC +5V bias : %s\n",
|
||||
ensoniq->ctrl & ES_1370_XCTL1 ? "on" : "off");
|
||||
str_on_off(ensoniq->ctrl & ES_1370_XCTL1));
|
||||
snd_iprintf(buffer, "Line In to AOUT : %s\n",
|
||||
ensoniq->ctrl & ES_1370_XCTL0 ? "on" : "off");
|
||||
str_on_off(ensoniq->ctrl & ES_1370_XCTL0));
|
||||
#else
|
||||
snd_iprintf(buffer, "Joystick port : 0x%x\n",
|
||||
(ES_1371_JOY_ASELI(ensoniq->ctrl) * 8) + 0x200);
|
||||
|
|
|
@ -206,6 +206,20 @@ config SND_HDA_SCODEC_TAS2781_I2C
|
|||
comment "Set to Y if you want auto-loading the side codec driver"
|
||||
depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_I2C=m
|
||||
|
||||
config SND_HDA_SCODEC_TAS2781_SPI
|
||||
tristate "Build TAS2781 HD-audio side codec support for SPI Bus"
|
||||
depends on SPI_MASTER
|
||||
depends on ACPI
|
||||
depends on EFI
|
||||
depends on SND_SOC
|
||||
select CRC32
|
||||
help
|
||||
Say Y or M here to include TAS2781 SPI HD-audio side codec support
|
||||
in snd-hda-intel driver, such as ALC287.
|
||||
|
||||
comment "Set to Y if you want auto-loading the side codec driver"
|
||||
depends on SND_HDA=y && SND_HDA_SCODEC_TAS2781_SPI=m
|
||||
|
||||
config SND_HDA_CODEC_REALTEK
|
||||
tristate "Build Realtek HD-audio codec support"
|
||||
select SND_HDA_GENERIC
|
||||
|
|
|
@ -40,6 +40,7 @@ snd-hda-scodec-cs35l56-spi-y := cs35l56_hda_spi.o
|
|||
snd-hda-cs-dsp-ctls-y := hda_cs_dsp_ctl.o
|
||||
snd-hda-scodec-component-y := hda_component.o
|
||||
snd-hda-scodec-tas2781-i2c-y := tas2781_hda_i2c.o
|
||||
snd-hda-scodec-tas2781-spi-y := tas2781_hda_spi.o tas2781_spi_fwlib.o
|
||||
|
||||
# common driver
|
||||
obj-$(CONFIG_SND_HDA) := snd-hda-codec.o
|
||||
|
@ -72,6 +73,7 @@ obj-$(CONFIG_SND_HDA_SCODEC_CS35L56_SPI) += snd-hda-scodec-cs35l56-spi.o
|
|||
obj-$(CONFIG_SND_HDA_CS_DSP_CONTROLS) += snd-hda-cs-dsp-ctls.o
|
||||
obj-$(CONFIG_SND_HDA_SCODEC_COMPONENT) += snd-hda-scodec-component.o
|
||||
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_I2C) += snd-hda-scodec-tas2781-i2c.o
|
||||
obj-$(CONFIG_SND_HDA_SCODEC_TAS2781_SPI) += snd-hda-scodec-tas2781-spi.o
|
||||
|
||||
# this must be the last entry after codec drivers;
|
||||
# otherwise the codec patches won't be hooked before the PCI probe
|
||||
|
|
|
@ -84,10 +84,8 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
|
|||
|
||||
static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file)
|
||||
{
|
||||
#ifndef CONFIG_SND_DEBUG_VERBOSE
|
||||
if (!capable(CAP_SYS_RAWIO))
|
||||
return -EACCES;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -2738,9 +2738,9 @@ static const struct pci_device_id azx_ids[] = {
|
|||
{ PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
|
||||
/* Loongson HDAudio*/
|
||||
{ PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA),
|
||||
.driver_data = AZX_DRIVER_LOONGSON },
|
||||
.driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
|
||||
{ PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
|
||||
.driver_data = AZX_DRIVER_LOONGSON },
|
||||
.driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
|
||||
{ 0, }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, azx_ids);
|
||||
|
|
|
@ -648,7 +648,7 @@ static const struct hda_patch_item patch_items[NUM_LINE_MODES] = {
|
|||
},
|
||||
};
|
||||
|
||||
/* check the line starting with '[' -- change the parser mode accodingly */
|
||||
/* check the line starting with '[' -- change the parser mode accordingly */
|
||||
static int parse_line_mode(char *buf, struct hda_bus *bus)
|
||||
{
|
||||
int i;
|
||||
|
|
36
sound/pci/hda/ideapad_hotkey_led_helper.c
Normal file
36
sound/pci/hda/ideapad_hotkey_led_helper.c
Normal file
|
@ -0,0 +1,36 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Ideapad helper functions for Lenovo Ideapad LED control,
|
||||
* It should be included from codec driver.
|
||||
*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_IDEAPAD_LAPTOP)
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/leds.h>
|
||||
|
||||
static bool is_ideapad(struct hda_codec *codec)
|
||||
{
|
||||
return (codec->core.subsystem_id >> 16 == 0x17aa) &&
|
||||
(acpi_dev_found("LHK2019") || acpi_dev_found("VPC2004"));
|
||||
}
|
||||
|
||||
static void hda_fixup_ideapad_acpi(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
|
||||
if (!is_ideapad(codec))
|
||||
return;
|
||||
snd_hda_gen_add_mute_led_cdev(codec, NULL);
|
||||
snd_hda_gen_add_micmute_led_cdev(codec, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
#else /* CONFIG_IDEAPAD_LAPTOP */
|
||||
|
||||
static void hda_fixup_ideapad_acpi(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IDEAPAD_LAPTOP */
|
|
@ -291,6 +291,7 @@ enum {
|
|||
CXT_FIXUP_GPIO1,
|
||||
CXT_FIXUP_ASPIRE_DMIC,
|
||||
CXT_FIXUP_THINKPAD_ACPI,
|
||||
CXT_FIXUP_LENOVO_XPAD_ACPI,
|
||||
CXT_FIXUP_OLPC_XO,
|
||||
CXT_FIXUP_CAP_MIX_AMP,
|
||||
CXT_FIXUP_TOSHIBA_P105,
|
||||
|
@ -313,6 +314,9 @@ enum {
|
|||
/* for hda_fixup_thinkpad_acpi() */
|
||||
#include "thinkpad_helper.c"
|
||||
|
||||
/* for hda_fixup_ideapad_acpi() */
|
||||
#include "ideapad_hotkey_led_helper.c"
|
||||
|
||||
static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
|
@ -928,6 +932,12 @@ static const struct hda_fixup cxt_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = hda_fixup_thinkpad_acpi,
|
||||
},
|
||||
[CXT_FIXUP_LENOVO_XPAD_ACPI] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = hda_fixup_ideapad_acpi,
|
||||
.chained = true,
|
||||
.chain_id = CXT_FIXUP_THINKPAD_ACPI,
|
||||
},
|
||||
[CXT_FIXUP_OLPC_XO] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cxt_fixup_olpc_xo,
|
||||
|
@ -1119,7 +1129,7 @@ static const struct hda_quirk cxt5066_fixups[] = {
|
|||
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK(0x17aa, 0x3978, "Lenovo G50-70", CXT_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
|
||||
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", CXT_FIXUP_THINKPAD_ACPI),
|
||||
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad/Ideapad", CXT_FIXUP_LENOVO_XPAD_ACPI),
|
||||
SND_PCI_QUIRK(0x1c06, 0x2011, "Lemote A1004", CXT_PINCFG_LEMOTE_A1004),
|
||||
SND_PCI_QUIRK(0x1c06, 0x2012, "Lemote A1205", CXT_PINCFG_LEMOTE_A1205),
|
||||
HDA_CODEC_QUIRK(0x2782, 0x12c3, "Sirius Gen1", CXT_PINCFG_TOP_SPEAKER),
|
||||
|
@ -1133,6 +1143,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
|
|||
{ .id = CXT_FIXUP_HEADPHONE_MIC_PIN, .name = "headphone-mic-pin" },
|
||||
{ .id = CXT_PINCFG_LENOVO_TP410, .name = "tp410" },
|
||||
{ .id = CXT_FIXUP_THINKPAD_ACPI, .name = "thinkpad" },
|
||||
{ .id = CXT_FIXUP_LENOVO_XPAD_ACPI, .name = "thinkpad-ideapad" },
|
||||
{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
|
||||
{ .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
|
||||
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
|
||||
|
|
|
@ -890,9 +890,7 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
*/
|
||||
|
||||
/* inverted digital-mic */
|
||||
static void alc_fixup_inv_dmic(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
|
@ -5902,7 +5900,7 @@ static void alc_determine_headset_type(struct hda_codec *codec)
|
|||
}
|
||||
|
||||
codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
|
||||
is_ctia ? "yes" : "no");
|
||||
str_yes_no(is_ctia));
|
||||
spec->current_headset_type = is_ctia ? ALC_HEADSET_TYPE_CTIA : ALC_HEADSET_TYPE_OMTP;
|
||||
}
|
||||
|
||||
|
@ -6934,6 +6932,15 @@ static void alc_fixup_thinkpad_acpi(struct hda_codec *codec,
|
|||
hda_fixup_thinkpad_acpi(codec, fix, action);
|
||||
}
|
||||
|
||||
/* for hda_fixup_ideapad_acpi() */
|
||||
#include "ideapad_hotkey_led_helper.c"
|
||||
|
||||
static void alc_fixup_ideapad_acpi(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
hda_fixup_ideapad_acpi(codec, fix, action);
|
||||
}
|
||||
|
||||
/* Fixup for Lenovo Legion 15IMHg05 speaker output on headset removal. */
|
||||
static void alc287_fixup_legion_15imhg05_speakers(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix,
|
||||
|
@ -7128,6 +7135,11 @@ static void tas2781_fixup_i2c(struct hda_codec *cdc,
|
|||
comp_generic_fixup(cdc, action, "i2c", "TIAS2781", "-%s:00", 1);
|
||||
}
|
||||
|
||||
static void tas2781_fixup_spi(struct hda_codec *cdc, const struct hda_fixup *fix, int action)
|
||||
{
|
||||
comp_generic_fixup(cdc, action, "spi", "TXNW2781", "-%s:00-tas2781-hda.%d", 2);
|
||||
}
|
||||
|
||||
static void yoga7_14arb7_fixup_i2c(struct hda_codec *cdc,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
|
@ -7556,6 +7568,7 @@ enum {
|
|||
ALC290_FIXUP_SUBWOOFER,
|
||||
ALC290_FIXUP_SUBWOOFER_HSJACK,
|
||||
ALC269_FIXUP_THINKPAD_ACPI,
|
||||
ALC269_FIXUP_LENOVO_XPAD_ACPI,
|
||||
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
|
||||
ALC269VB_FIXUP_INFINIX_ZERO_BOOK_13,
|
||||
ALC269VC_FIXUP_INFINIX_Y4_MAX,
|
||||
|
@ -7762,6 +7775,7 @@ enum {
|
|||
ALC236_FIXUP_DELL_DUAL_CODECS,
|
||||
ALC287_FIXUP_CS35L41_I2C_2_THINKPAD_ACPI,
|
||||
ALC287_FIXUP_TAS2781_I2C,
|
||||
ALC245_FIXUP_TAS2781_SPI_2,
|
||||
ALC287_FIXUP_YOGA7_14ARB7_I2C,
|
||||
ALC245_FIXUP_HP_MUTE_LED_COEFBIT,
|
||||
ALC245_FIXUP_HP_X360_MUTE_LEDS,
|
||||
|
@ -8327,6 +8341,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_SKU_IGNORE,
|
||||
},
|
||||
[ALC269_FIXUP_LENOVO_XPAD_ACPI] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_ideapad_acpi,
|
||||
.chained = true,
|
||||
.chain_id = ALC269_FIXUP_THINKPAD_ACPI,
|
||||
},
|
||||
[ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = alc_fixup_inv_dmic,
|
||||
|
@ -9986,6 +10006,12 @@ static const struct hda_fixup alc269_fixups[] = {
|
|||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_THINKPAD_HEADSET_JACK,
|
||||
},
|
||||
[ALC245_FIXUP_TAS2781_SPI_2] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = tas2781_fixup_spi,
|
||||
.chained = true,
|
||||
.chain_id = ALC285_FIXUP_HP_GPIO_LED,
|
||||
},
|
||||
[ALC287_FIXUP_YOGA7_14ARB7_I2C] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = yoga7_14arb7_fixup_i2c,
|
||||
|
@ -10158,6 +10184,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x1025, 0x1308, "Acer Aspire Z24-890", ALC286_FIXUP_ACER_AIO_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x132a, "Acer TravelMate B114-21", ALC233_FIXUP_ACER_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x1330, "Acer TravelMate X514-51T", ALC255_FIXUP_ACER_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1025, 0x1360, "Acer Aspire A115", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x141f, "Acer Spin SP513-54N", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x142b, "Acer Swift SF314-42", ALC255_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1025, 0x1430, "Acer TravelMate B311R-31", ALC256_FIXUP_ACER_MIC_NO_PRESENCE),
|
||||
|
@ -10388,6 +10415,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x8870, "HP ZBook Fury 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
|
||||
SND_PCI_QUIRK(0x103c, 0x8873, "HP ZBook Studio 15.6 Inch G8 Mobile Workstation PC", ALC285_FIXUP_HP_GPIO_AMP_INIT),
|
||||
SND_PCI_QUIRK(0x103c, 0x887a, "HP Laptop 15s-eq2xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
|
||||
SND_PCI_QUIRK(0x103c, 0x887c, "HP Laptop 14s-fq1xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
|
||||
SND_PCI_QUIRK(0x103c, 0x888a, "HP ENVY x360 Convertible 15-eu0xxx", ALC245_FIXUP_HP_X360_MUTE_LEDS),
|
||||
SND_PCI_QUIRK(0x103c, 0x888d, "HP ZBook Power 15.6 inch G8 Mobile Workstation PC", ALC236_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8895, "HP EliteBook 855 G8 Notebook PC", ALC285_FIXUP_HP_SPEAKERS_MICMUTE_LED),
|
||||
|
@ -10548,6 +10576,8 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x103c, 0x8d84, "HP EliteBook X G1i", ALC285_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8d91, "HP ZBook Firefly 14 G12", ALC285_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8d92, "HP ZBook Firefly 16 G12", ALC285_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8de8, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8de9, "HP Gemtree", ALC245_FIXUP_TAS2781_SPI_2),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e18, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e19, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED),
|
||||
SND_PCI_QUIRK(0x103c, 0x8e1a, "HP ZBook Firefly 14 G12A", ALC285_FIXUP_HP_GPIO_LED),
|
||||
|
@ -10963,6 +10993,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
|
|||
SND_PCI_QUIRK(0x17aa, 0x511f, "Thinkpad", ALC298_FIXUP_TPT470_DOCK),
|
||||
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
|
||||
SND_PCI_QUIRK(0x17aa, 0x9e56, "Lenovo ZhaoYang CF4620Z", ALC286_FIXUP_SONY_MIC_NO_PRESENCE),
|
||||
SND_PCI_QUIRK(0x1849, 0x0269, "Positivo Master C6400", ALC269VB_FIXUP_ASUS_ZENBOOK),
|
||||
SND_PCI_QUIRK(0x1849, 0x1233, "ASRock NUC Box 1100", ALC233_FIXUP_NO_AUDIO_JACK),
|
||||
SND_PCI_QUIRK(0x1849, 0xa233, "Positivo Master C6300", ALC269_FIXUP_HEADSET_MIC),
|
||||
SND_PCI_QUIRK(0x1854, 0x0440, "LG CQ6", ALC256_FIXUP_HEADPHONE_AMP_VOL),
|
||||
|
@ -11069,7 +11100,7 @@ static const struct hda_quirk alc269_fixup_vendor_tbl[] = {
|
|||
SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
|
||||
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
|
||||
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
|
||||
SND_PCI_QUIRK_VENDOR(0x17aa, "Thinkpad", ALC269_FIXUP_THINKPAD_ACPI),
|
||||
SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo XPAD", ALC269_FIXUP_LENOVO_XPAD_ACPI),
|
||||
SND_PCI_QUIRK_VENDOR(0x19e5, "Huawei Matebook", ALC255_FIXUP_MIC_MUTE_LED),
|
||||
{}
|
||||
};
|
||||
|
@ -11134,6 +11165,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
|
|||
{.id = ALC290_FIXUP_MONO_SPEAKERS_HSJACK, .name = "mono-speakers"},
|
||||
{.id = ALC290_FIXUP_SUBWOOFER_HSJACK, .name = "alc290-subwoofer"},
|
||||
{.id = ALC269_FIXUP_THINKPAD_ACPI, .name = "thinkpad"},
|
||||
{.id = ALC269_FIXUP_LENOVO_XPAD_ACPI, .name = "lenovo-xpad-led"},
|
||||
{.id = ALC269_FIXUP_DMIC_THINKPAD_ACPI, .name = "dmic-thinkpad"},
|
||||
{.id = ALC255_FIXUP_ACER_MIC_NO_PRESENCE, .name = "alc255-acer"},
|
||||
{.id = ALC255_FIXUP_ASUS_MIC_NO_PRESENCE, .name = "alc255-asus"},
|
||||
|
|
158
sound/pci/hda/tas2781-spi.h
Normal file
158
sound/pci/hda/tas2781-spi.h
Normal file
|
@ -0,0 +1,158 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
//
|
||||
// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier
|
||||
//
|
||||
// Copyright (C) 2024 Texas Instruments Incorporated
|
||||
// https://www.ti.com
|
||||
//
|
||||
// The TAS2781 driver implements a flexible and configurable
|
||||
// algo coefficient setting for TAS2781 chips.
|
||||
//
|
||||
// Author: Baojun Xu <baojun.xu@ti.com>
|
||||
//
|
||||
|
||||
#ifndef __TAS2781_SPI_H__
|
||||
#define __TAS2781_SPI_H__
|
||||
|
||||
#define TASDEVICE_RATES \
|
||||
(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
|
||||
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
|
||||
|
||||
#define TASDEVICE_FORMATS \
|
||||
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
|
||||
SNDRV_PCM_FMTBIT_S32_LE)
|
||||
|
||||
#define TASDEVICE_MAX_BOOK_NUM 256
|
||||
#define TASDEVICE_MAX_PAGE 256
|
||||
|
||||
#define TASDEVICE_MAX_SIZE (TASDEVICE_MAX_BOOK_NUM * TASDEVICE_MAX_PAGE)
|
||||
|
||||
/* PAGE Control Register (available in page0 of each book) */
|
||||
#define TASDEVICE_PAGE_SELECT 0x00
|
||||
#define TASDEVICE_BOOKCTL_PAGE 0x00
|
||||
#define TASDEVICE_BOOKCTL_REG GENMASK(7, 1)
|
||||
#define TASDEVICE_BOOK_ID(reg) (((reg) & GENMASK(24, 16)) >> 16)
|
||||
#define TASDEVICE_PAGE_ID(reg) (((reg) & GENMASK(15, 8)) >> 8)
|
||||
#define TASDEVICE_REG_ID(reg) (((reg) & GENMASK(7, 1)) >> 1)
|
||||
#define TASDEVICE_PAGE_REG(reg) ((reg) & GENMASK(15, 1))
|
||||
#define TASDEVICE_REG(book, page, reg) \
|
||||
(((book) << 16) | ((page) << 8) | ((reg) << 1))
|
||||
|
||||
/* Software Reset */
|
||||
#define TAS2781_REG_SWRESET TASDEVICE_REG(0x0, 0x0, 0x01)
|
||||
#define TAS2781_REG_SWRESET_RESET BIT(0)
|
||||
|
||||
/* System Reset Check Register */
|
||||
#define TAS2781_REG_CLK_CONFIG TASDEVICE_REG(0x0, 0x0, 0x5c)
|
||||
#define TAS2781_REG_CLK_CONFIG_RESET (0x19)
|
||||
#define TAS2781_PRE_POST_RESET_CFG 3
|
||||
|
||||
/* Block Checksum */
|
||||
#define TASDEVICE_CHECKSUM TASDEVICE_REG(0x0, 0x0, 0x7e)
|
||||
|
||||
/* Volume control */
|
||||
#define TAS2781_DVC_LVL TASDEVICE_REG(0x0, 0x0, 0x1a)
|
||||
#define TAS2781_AMP_LEVEL TASDEVICE_REG(0x0, 0x0, 0x03)
|
||||
#define TAS2781_AMP_LEVEL_MASK GENMASK(5, 1)
|
||||
|
||||
#define TASDEVICE_CMD_SING_W 0x1
|
||||
#define TASDEVICE_CMD_BURST 0x2
|
||||
#define TASDEVICE_CMD_DELAY 0x3
|
||||
#define TASDEVICE_CMD_FIELD_W 0x4
|
||||
|
||||
#define TAS2781_SPI_MAX_FREQ (4 * HZ_PER_MHZ)
|
||||
|
||||
#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
|
||||
#define TASDEVICE_SPEAKER_CALIBRATION_SIZE 20
|
||||
|
||||
/* Flag of calibration registers address. */
|
||||
#define TASDEVICE_CALIBRATION_REG_ADDRESS BIT(7)
|
||||
|
||||
#define TASDEVICE_CALIBRATION_DATA_NAME L"CALI_DATA"
|
||||
#define TASDEVICE_CALIBRATION_DATA_SIZE 256
|
||||
|
||||
enum calib_data {
|
||||
R0_VAL = 0,
|
||||
INV_R0,
|
||||
R0LOW,
|
||||
POWER,
|
||||
TLIM,
|
||||
CALIB_MAX
|
||||
};
|
||||
|
||||
struct tasdevice_priv {
|
||||
struct tasdevice_fw *cali_data_fmw;
|
||||
struct tasdevice_rca rcabin;
|
||||
struct tasdevice_fw *fmw;
|
||||
struct gpio_desc *reset;
|
||||
struct mutex codec_lock;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
struct tm tm;
|
||||
|
||||
unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
|
||||
unsigned char coef_binaryname[64];
|
||||
unsigned char rca_binaryname[64];
|
||||
unsigned char dev_name[32];
|
||||
|
||||
bool force_fwload_status;
|
||||
bool playback_started;
|
||||
bool is_loading;
|
||||
bool is_loaderr;
|
||||
unsigned int cali_reg_array[CALIB_MAX];
|
||||
unsigned int cali_data[CALIB_MAX];
|
||||
unsigned int err_code;
|
||||
void *codec;
|
||||
int cur_book;
|
||||
int cur_prog;
|
||||
int cur_conf;
|
||||
int fw_state;
|
||||
int index;
|
||||
int irq;
|
||||
|
||||
int (*fw_parse_variable_header)(struct tasdevice_priv *tas_priv,
|
||||
const struct firmware *fmw,
|
||||
int offset);
|
||||
int (*fw_parse_program_data)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdevice_fw *tas_fmw,
|
||||
const struct firmware *fmw, int offset);
|
||||
int (*fw_parse_configuration_data)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdevice_fw *tas_fmw,
|
||||
const struct firmware *fmw,
|
||||
int offset);
|
||||
int (*tasdevice_load_block)(struct tasdevice_priv *tas_priv,
|
||||
struct tasdev_blk *block);
|
||||
|
||||
int (*save_calibration)(struct tasdevice_priv *tas_priv);
|
||||
void (*apply_calibration)(struct tasdevice_priv *tas_priv);
|
||||
};
|
||||
|
||||
int tasdevice_spi_dev_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned int *value);
|
||||
int tasdevice_spi_dev_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned int value);
|
||||
int tasdevice_spi_dev_bulk_write(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int tasdevice_spi_dev_bulk_read(struct tasdevice_priv *tas_priv,
|
||||
unsigned int reg, unsigned char *p_data,
|
||||
unsigned int n_length);
|
||||
int tasdevice_spi_dev_update_bits(struct tasdevice_priv *tasdevice,
|
||||
unsigned int reg, unsigned int mask,
|
||||
unsigned int value);
|
||||
|
||||
void tasdevice_spi_select_cfg_blk(void *context, int conf_no,
|
||||
unsigned char block_type);
|
||||
void tasdevice_spi_config_info_remove(void *context);
|
||||
int tasdevice_spi_dsp_parser(void *context);
|
||||
int tasdevice_spi_rca_parser(void *context, const struct firmware *fmw);
|
||||
void tasdevice_spi_dsp_remove(void *context);
|
||||
void tasdevice_spi_calbin_remove(void *context);
|
||||
int tasdevice_spi_select_tuningprm_cfg(void *context, int prm, int cfg_no,
|
||||
int rca_conf_no);
|
||||
int tasdevice_spi_prmg_load(void *context, int prm_no);
|
||||
int tasdevice_spi_prmg_calibdata_load(void *context, int prm_no);
|
||||
void tasdevice_spi_tuning_switch(void *context, int state);
|
||||
int tas2781_spi_load_calibration(void *context, char *file_name,
|
||||
unsigned short i);
|
||||
#endif /* __TAS2781_SPI_H__ */
|
1265
sound/pci/hda/tas2781_hda_spi.c
Normal file
1265
sound/pci/hda/tas2781_hda_spi.c
Normal file
File diff suppressed because it is too large
Load diff
2006
sound/pci/hda/tas2781_spi_fwlib.c
Normal file
2006
sound/pci/hda/tas2781_spi_fwlib.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -35,7 +35,7 @@ unsigned int lola_sample_rate_convert(unsigned int coded)
|
|||
default: return 0; /* error */
|
||||
}
|
||||
|
||||
/* ajustement */
|
||||
/* adjustement */
|
||||
switch (coded & 0x60) {
|
||||
case (0 << 5): break;
|
||||
case (1 << 5): freq = (freq * 999) / 1000; break;
|
||||
|
|
|
@ -696,7 +696,9 @@ snd_nm256_playback_copy(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct nm256_stream *s = runtime->private_data;
|
||||
|
||||
return copy_from_iter_toio(s->bufptr + pos, src, count);
|
||||
if (copy_from_iter_toio(s->bufptr + pos, count, src) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -710,7 +712,9 @@ snd_nm256_capture_copy(struct snd_pcm_substream *substream,
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct nm256_stream *s = runtime->private_data;
|
||||
|
||||
return copy_to_iter_fromio(dst, s->bufptr + pos, count);
|
||||
if (copy_to_iter_fromio(s->bufptr + pos, count, dst) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* !__i386__ */
|
||||
|
|
|
@ -256,8 +256,10 @@ static int snd_rme32_playback_copy(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
|
||||
|
||||
return copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
|
||||
src, count);
|
||||
if (copy_from_iter_toio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
|
||||
count, src) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* copy callback for halfduplex mode */
|
||||
|
@ -267,9 +269,10 @@ static int snd_rme32_capture_copy(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct rme32 *rme32 = snd_pcm_substream_chip(substream);
|
||||
|
||||
return copy_to_iter_fromio(dst,
|
||||
rme32->iobase + RME32_IO_DATA_BUFFER + pos,
|
||||
count);
|
||||
if (copy_to_iter_fromio(rme32->iobase + RME32_IO_DATA_BUFFER + pos,
|
||||
count, dst) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -322,8 +322,10 @@ snd_rme96_playback_copy(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
||||
|
||||
return copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
|
||||
src, count);
|
||||
if (copy_from_iter_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
|
||||
count, src) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -333,9 +335,10 @@ snd_rme96_capture_copy(struct snd_pcm_substream *substream,
|
|||
{
|
||||
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
||||
|
||||
return copy_to_iter_fromio(dst,
|
||||
rme96->iobase + RME96_IO_REC_BUFFER + pos,
|
||||
count);
|
||||
if (copy_to_iter_fromio(rme96->iobase + RME96_IO_REC_BUFFER + pos,
|
||||
count, dst) != count)
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -3444,7 +3444,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0));
|
||||
snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1));
|
||||
snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1));
|
||||
snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_work ? "on" : "off");
|
||||
snd_iprintf(buffer, "Use Midi Tasklet: %s\n", str_on_off(hdsp->use_midi_work));
|
||||
|
||||
snd_iprintf(buffer, "\n");
|
||||
|
||||
|
@ -3452,8 +3452,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
|
||||
snd_iprintf(buffer, "Buffer Size (Latency): %d samples (2 periods of %lu bytes)\n", x, (unsigned long) hdsp->period_bytes);
|
||||
snd_iprintf(buffer, "Hardware pointer (frames): %ld\n", hdsp_hw_pointer(hdsp));
|
||||
snd_iprintf(buffer, "Precise pointer: %s\n", hdsp->precise_ptr ? "on" : "off");
|
||||
snd_iprintf(buffer, "Line out: %s\n", (hdsp->control_register & HDSP_LineOut) ? "on" : "off");
|
||||
snd_iprintf(buffer, "Precise pointer: %s\n", str_on_off(hdsp->precise_ptr));
|
||||
snd_iprintf(buffer, "Line out: %s\n", str_on_off(hdsp->control_register & HDSP_LineOut));
|
||||
|
||||
snd_iprintf(buffer, "Firmware version: %d\n", (status2&HDSP_version0)|(status2&HDSP_version1)<<1|(status2&HDSP_version2)<<2);
|
||||
|
||||
|
@ -3750,8 +3750,8 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
|||
snd_iprintf(buffer, "Phones Gain : %s\n", tmp);
|
||||
|
||||
snd_iprintf(buffer, "XLR Breakout Cable : %s\n",
|
||||
hdsp_toggle_setting(hdsp, HDSP_XLRBreakoutCable) ?
|
||||
"yes" : "no");
|
||||
str_yes_no(hdsp_toggle_setting(hdsp,
|
||||
HDSP_XLRBreakoutCable)));
|
||||
|
||||
if (hdsp->control_register & HDSP_AnalogExtensionBoard)
|
||||
snd_iprintf(buffer, "AEB : on (ADAT1 internal)\n");
|
||||
|
|
|
@ -4927,14 +4927,14 @@ snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
|
|||
x, (unsigned long) hdspm->period_bytes);
|
||||
|
||||
snd_iprintf(buffer, "Line out: %s\n",
|
||||
(hdspm->control_register & HDSPM_LineOut) ? "on " : "off");
|
||||
str_on_off(hdspm->control_register & HDSPM_LineOut));
|
||||
|
||||
snd_iprintf(buffer,
|
||||
"ClearTrackMarker = %s, Transmit in %s Channel Mode, "
|
||||
"Auto Input %s\n",
|
||||
(hdspm->control_register & HDSPM_clr_tms) ? "on" : "off",
|
||||
str_on_off(hdspm->control_register & HDSPM_clr_tms),
|
||||
(hdspm->control_register & HDSPM_TX_64ch) ? "64" : "56",
|
||||
(hdspm->control_register & HDSPM_AutoInp) ? "on" : "off");
|
||||
str_on_off(hdspm->control_register & HDSPM_AutoInp));
|
||||
|
||||
|
||||
if (!(hdspm->control_register & HDSPM_ClockModeMaster))
|
||||
|
@ -5088,12 +5088,9 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
|
|||
|
||||
snd_iprintf(buffer,
|
||||
"ClearTrackMarker %s, Emphasis %s, Dolby %s\n",
|
||||
(hdspm->
|
||||
control_register & HDSPM_clr_tms) ? "on" : "off",
|
||||
(hdspm->
|
||||
control_register & HDSPM_Emphasis) ? "on" : "off",
|
||||
(hdspm->
|
||||
control_register & HDSPM_Dolby) ? "on" : "off");
|
||||
str_on_off(hdspm->control_register & HDSPM_clr_tms),
|
||||
str_on_off(hdspm->control_register & HDSPM_Emphasis),
|
||||
str_on_off(hdspm->control_register & HDSPM_Dolby));
|
||||
|
||||
|
||||
pref_syncref = hdspm_pref_sync_ref(hdspm);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue