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

This commit is contained in:
Stephen Rothwell 2024-12-20 12:23:25 +11:00
commit dca381d2cc
106 changed files with 20643 additions and 663 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
snd-soc-fsl-audmix-y := fsl_audmix.o
snd-soc-fsl-asoc-card-y := fsl-asoc-card.o
snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o
snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o
snd-soc-fsl-sai-y := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -885,7 +885,7 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream,
sai->dma_params_rx.maxburst);
ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &fsl_sai_rate_constraints);
SNDRV_PCM_HW_PARAM_RATE, &sai->constraint_rates);
return ret;
}
@ -1442,6 +1442,11 @@ static int fsl_sai_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(&pdev->dev, &sai->pll8k_clk,
&sai->pll11k_clk);
fsl_asoc_constrain_rates(&sai->constraint_rates,
&fsl_sai_rate_constraints,
sai->pll8k_clk, sai->pll11k_clk, NULL,
sai->constraint_rates_list);
/* Use Multi FIFO mode depending on the support from SDMA script */
ret = of_property_read_u32_array(np, "dmas", dmas, 4);
if (!sai->soc_data->use_edma && !ret && dmas[2] == IMX_DMATYPE_MULTI_SAI)

View File

@ -9,6 +9,7 @@
#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#define FAL_SAI_NUM_RATES 20
#define FSL_SAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE |\
@ -309,6 +310,8 @@ struct fsl_sai {
struct pinctrl *pinctrl;
struct pinctrl_state *pins_state;
struct sdma_peripheral_config audio_config[2];
struct snd_pcm_hw_constraint_list constraint_rates;
unsigned int constraint_rates_list[FAL_SAI_NUM_RATES];
};
#define TX 1

View File

@ -152,6 +152,51 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
}
EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
/**
* fsl_asoc_constrain_rates - constrain rates according to clocks
*
* @target_constr: target constraint
* @original_constr: original constraint
* @pll8k_clk: PLL clock pointer for 8kHz
* @pll11k_clk: PLL clock pointer for 11kHz
* @ext_clk: External clock pointer
* @target_rates: target rates array
*
* This function constrain rates according to clocks
*/
void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
const struct snd_pcm_hw_constraint_list *original_constr,
struct clk *pll8k_clk, struct clk *pll11k_clk,
struct clk *ext_clk, int *target_rates)
{
int i, j, k = 0;
u64 clk_rate[3];
*target_constr = *original_constr;
if (pll8k_clk || pll11k_clk || ext_clk) {
target_constr->list = target_rates;
target_constr->count = 0;
for (i = 0; i < original_constr->count; i++) {
clk_rate[0] = clk_get_rate(pll8k_clk);
clk_rate[1] = clk_get_rate(pll11k_clk);
clk_rate[2] = clk_get_rate(ext_clk);
for (j = 0; j < 3; j++) {
if (clk_rate[j] != 0 &&
do_div(clk_rate[j], original_constr->list[i]) == 0) {
target_rates[k++] = original_constr->list[i];
target_constr->count++;
break;
}
}
}
/* protection for if there is no proper rate found*/
if (!target_constr->count)
*target_constr = *original_constr;
}
}
EXPORT_SYMBOL(fsl_asoc_constrain_rates);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale ASoC utility code");
MODULE_LICENSE("GPL v2");

View File

@ -26,4 +26,9 @@ void fsl_asoc_get_pll_clocks(struct device *dev, struct clk **pll8k_clk,
void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
struct clk *pll8k_clk,
struct clk *pll11k_clk, u64 ratio);
void fsl_asoc_constrain_rates(struct snd_pcm_hw_constraint_list *target_constr,
const struct snd_pcm_hw_constraint_list *original_constr,
struct clk *pll8k_clk, struct clk *pll11k_clk,
struct clk *ext_clk, int *target_rates);
#endif /* _FSL_UTILS_H */

View File

@ -19,6 +19,7 @@
#include "imx-pcm.h"
#define FSL_XCVR_CAPDS_SIZE 256
#define SPDIF_NUM_RATES 7
enum fsl_xcvr_pll_verison {
PLL_MX8MP,
@ -37,6 +38,8 @@ struct fsl_xcvr {
const struct fsl_xcvr_soc_data *soc_data;
struct platform_device *pdev;
struct regmap *regmap;
struct regmap *regmap_phy;
struct regmap *regmap_pll;
struct clk *ipg_clk;
struct clk *pll_ipg_clk;
struct clk *phy_clk;
@ -55,6 +58,8 @@ struct fsl_xcvr {
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
struct work_struct work_rst;
spinlock_t lock; /* Protect hw_reset and trigger */
struct snd_pcm_hw_constraint_list spdif_constr_rates;
u32 spdif_constr_rates_list[SPDIF_NUM_RATES];
};
static const struct fsl_xcvr_pll_conf {
@ -257,7 +262,7 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy)
idx = BIT(phy ? 26 : 24);
tidx = BIT(phy ? 27 : 25);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_WDATA, data);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
@ -271,6 +276,59 @@ static int fsl_xcvr_ai_write(struct fsl_xcvr *xcvr, u8 reg, u32 data, bool phy)
return ret;
}
static int fsl_xcvr_ai_read(struct fsl_xcvr *xcvr, u8 reg, u32 *data, bool phy)
{
struct device *dev = &xcvr->pdev->dev;
u32 val, idx, tidx;
int ret;
idx = BIT(phy ? 26 : 24);
tidx = BIT(phy ? 27 : 25);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_CLR, 0xFF | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET, reg | FSL_XCVR_PHY_AI_CTRL_AI_RWB);
regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_TOG, idx);
ret = regmap_read_poll_timeout(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL, val,
(val & idx) == ((val & tidx) >> 1),
10, 10000);
if (ret)
dev_err(dev, "AI timeout: failed to read %s reg 0x%02x\n",
phy ? "PHY" : "PLL", reg);
regmap_read(xcvr->regmap, FSL_XCVR_PHY_AI_RDATA, data);
return ret;
}
static int fsl_xcvr_phy_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct fsl_xcvr *xcvr = context;
return fsl_xcvr_ai_read(xcvr, reg, val, 1);
}
static int fsl_xcvr_phy_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct fsl_xcvr *xcvr = context;
return fsl_xcvr_ai_write(xcvr, reg, val, 1);
}
static int fsl_xcvr_pll_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct fsl_xcvr *xcvr = context;
return fsl_xcvr_ai_read(xcvr, reg, val, 0);
}
static int fsl_xcvr_pll_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct fsl_xcvr *xcvr = context;
return fsl_xcvr_ai_write(xcvr, reg, val, 0);
}
static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
{
struct device *dev = &xcvr->pdev->dev;
@ -303,55 +361,55 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
switch (xcvr->soc_data->pll_ver) {
case PLL_MX8MP:
/* PLL: BANDGAP_SET: EN_VBG (enable bandgap) */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_BANDGAP_SET,
FSL_XCVR_PLL_BANDGAP_EN_VBG, 0);
regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_BANDGAP,
FSL_XCVR_PLL_BANDGAP_EN_VBG);
/* PLL: CTRL0: DIV_INTEGER */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi, 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0, fsl_xcvr_pll_cfg[i].mfi);
/* PLL: NUMERATOR: MFN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn, 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_NUM, fsl_xcvr_pll_cfg[i].mfn);
/* PLL: DENOMINATOR: MFD */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd, 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_DEN, fsl_xcvr_pll_cfg[i].mfd);
/* PLL: CTRL0_SET: HOLD_RING_OFF, POWER_UP */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP, 0);
regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
FSL_XCVR_PLL_CTRL0_HROFF | FSL_XCVR_PLL_CTRL0_PWP);
udelay(25);
/* PLL: CTRL0: Clear Hold Ring Off */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_CLR,
FSL_XCVR_PLL_CTRL0_HROFF, 0);
regmap_clear_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
FSL_XCVR_PLL_CTRL0_HROFF);
udelay(100);
if (tx) { /* TX is enabled for SPDIF only */
/* PLL: POSTDIV: PDIV0 */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV,
FSL_XCVR_PLL_PDIVx(log2, 0), 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
FSL_XCVR_PLL_PDIVx(log2, 0));
/* PLL: CTRL_SET: CLKMUX0_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
FSL_XCVR_PLL_CTRL0_CM0_EN, 0);
regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
FSL_XCVR_PLL_CTRL0_CM0_EN);
} else if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC RX */
/* PLL: POSTDIV: PDIV1 */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV,
FSL_XCVR_PLL_PDIVx(log2, 1), 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
FSL_XCVR_PLL_PDIVx(log2, 1));
/* PLL: CTRL_SET: CLKMUX1_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
FSL_XCVR_PLL_CTRL0_CM1_EN, 0);
regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
FSL_XCVR_PLL_CTRL0_CM1_EN);
} else { /* SPDIF / ARC RX */
/* PLL: POSTDIV: PDIV2 */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_PDIV,
FSL_XCVR_PLL_PDIVx(log2, 2), 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_PLL_PDIV,
FSL_XCVR_PLL_PDIVx(log2, 2));
/* PLL: CTRL_SET: CLKMUX2_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PLL_CTRL0_SET,
FSL_XCVR_PLL_CTRL0_CM2_EN, 0);
regmap_set_bits(xcvr->regmap_pll, FSL_XCVR_PLL_CTRL0,
FSL_XCVR_PLL_CTRL0_CM2_EN);
}
break;
case PLL_MX95:
val = fsl_xcvr_pll_cfg[i].mfi << FSL_XCVR_GP_PLL_DIV_MFI_SHIFT | div;
fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DIV, val, 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DIV, val);
val = fsl_xcvr_pll_cfg[i].mfn << FSL_XCVR_GP_PLL_NUMERATOR_MFN_SHIFT;
fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_NUMERATOR, val, 0);
fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_DENOMINATOR,
fsl_xcvr_pll_cfg[i].mfd, 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_NUMERATOR, val);
regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_DENOMINATOR,
fsl_xcvr_pll_cfg[i].mfd);
val = FSL_XCVR_GP_PLL_CTRL_POWERUP | FSL_XCVR_GP_PLL_CTRL_CLKMUX_EN;
fsl_xcvr_ai_write(xcvr, FSL_XCVR_GP_PLL_CTRL, val, 0);
regmap_write(xcvr->regmap_pll, FSL_XCVR_GP_PLL_CTRL, val);
break;
default:
dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver);
@ -360,22 +418,22 @@ static int fsl_xcvr_en_phy_pll(struct fsl_xcvr *xcvr, u32 freq, bool tx)
if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */
/* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
FSL_XCVR_PHY_CTRL_TSDIFF_OE |
FSL_XCVR_PHY_CTRL_PHY_EN, 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
FSL_XCVR_PHY_CTRL_TSDIFF_OE |
FSL_XCVR_PHY_CTRL_PHY_EN);
/* PHY: CTRL2_SET: EARC_TX_MODE */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET,
FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2,
FSL_XCVR_PHY_CTRL2_EARC_TXMS);
} else if (!tx) { /* SPDIF / ARC RX mode */
if (xcvr->mode == FSL_XCVR_MODE_SPDIF)
/* PHY: CTRL_SET: SPDIF_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
FSL_XCVR_PHY_CTRL_SPDIF_EN, 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
FSL_XCVR_PHY_CTRL_SPDIF_EN);
else /* PHY: CTRL_SET: ARC RX setup */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
FSL_XCVR_PHY_CTRL_PHY_EN |
FSL_XCVR_PHY_CTRL_RX_CM_EN |
fsl_xcvr_phy_arc_cfg[xcvr->arc_mode], 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
FSL_XCVR_PHY_CTRL_PHY_EN |
FSL_XCVR_PHY_CTRL_RX_CM_EN |
fsl_xcvr_phy_arc_cfg[xcvr->arc_mode]);
}
dev_dbg(dev, "PLL Fexp: %u, Fout: %u, mfi: %u, mfn: %u, mfd: %d, div: %u, pdiv0: %u\n",
@ -416,17 +474,17 @@ static int fsl_xcvr_en_aud_pll(struct fsl_xcvr *xcvr, u32 freq)
if (xcvr->mode == FSL_XCVR_MODE_EARC) { /* eARC mode */
/* PHY: CTRL_SET: TX_DIFF_OE, PHY_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
FSL_XCVR_PHY_CTRL_TSDIFF_OE |
FSL_XCVR_PHY_CTRL_PHY_EN, 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
FSL_XCVR_PHY_CTRL_TSDIFF_OE |
FSL_XCVR_PHY_CTRL_PHY_EN);
/* PHY: CTRL2_SET: EARC_TX_MODE */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL2_SET,
FSL_XCVR_PHY_CTRL2_EARC_TXMS, 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL2,
FSL_XCVR_PHY_CTRL2_EARC_TXMS);
} else { /* SPDIF mode */
/* PHY: CTRL_SET: TX_CLK_AUD_SS | SPDIF_EN */
fsl_xcvr_ai_write(xcvr, FSL_XCVR_PHY_CTRL_SET,
FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS |
FSL_XCVR_PHY_CTRL_SPDIF_EN, 1);
regmap_set_bits(xcvr->regmap_phy, FSL_XCVR_PHY_CTRL,
FSL_XCVR_PHY_CTRL_TX_CLK_AUD_SS |
FSL_XCVR_PHY_CTRL_SPDIF_EN);
}
dev_dbg(dev, "PLL Fexp: %u\n", freq);
@ -448,7 +506,7 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
if (xcvr->soc_data->spdif_only && tx) {
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET,
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM,
FSL_XCVR_TX_DPTH_CTRL_BYPASS_FEM);
if (ret < 0) {
@ -466,8 +524,8 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
return ret;
}
ret = regmap_write(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL_SET,
FSL_XCVR_TX_DPTH_CTRL_FRM_FMT);
ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_FRM_FMT);
if (ret < 0) {
dev_err(dai->dev, "Failed to set TX_DPTH: %d\n", ret);
return ret;
@ -484,11 +542,11 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
* Clear RX FIFO, flip RX FIFO bits,
* disable eARC related HW mode detects
*/
ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET,
FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO |
FSL_XCVR_RX_DPTH_CTRL_COMP |
FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO |
FSL_XCVR_RX_DPTH_CTRL_COMP |
FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
if (ret < 0) {
dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret);
return ret;
@ -505,18 +563,18 @@ static int fsl_xcvr_prepare(struct snd_pcm_substream *substream,
case FSL_XCVR_MODE_EARC:
if (!tx) {
/** Clear RX FIFO, flip RX FIFO bits */
ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_SET,
FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO);
ret = regmap_set_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
FSL_XCVR_RX_DPTH_CTRL_STORE_FMT |
FSL_XCVR_RX_DPTH_CTRL_CLR_RX_FIFO);
if (ret < 0) {
dev_err(dai->dev, "Failed to set RX_DPTH: %d\n", ret);
return ret;
}
/** Enable eARC related HW mode detects */
ret = regmap_write(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL_CLR,
FSL_XCVR_RX_DPTH_CTRL_COMP |
FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
ret = regmap_clear_bits(xcvr->regmap, FSL_XCVR_RX_DPTH_CTRL,
FSL_XCVR_RX_DPTH_CTRL_COMP |
FSL_XCVR_RX_DPTH_CTRL_LAYB_CTRL);
if (ret < 0) {
dev_err(dai->dev, "Failed to clr TX_DPTH: %d\n", ret);
return ret;
@ -585,8 +643,12 @@ static int fsl_xcvr_startup(struct snd_pcm_substream *substream,
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
case FSL_XCVR_MODE_ARC:
ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
&fsl_xcvr_spdif_rates_constr);
if (xcvr->soc_data->spdif_only && tx)
ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
&xcvr->spdif_constr_rates);
else
ret = fsl_xcvr_constr(substream, &fsl_xcvr_spdif_channels_constr,
&fsl_xcvr_spdif_rates_constr);
break;
case FSL_XCVR_MODE_EARC:
ret = fsl_xcvr_constr(substream, &fsl_xcvr_earc_channels_constr,
@ -696,9 +758,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
}
fallthrough;
case FSL_XCVR_MODE_SPDIF:
ret = regmap_write(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL_SET,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
ret = regmap_set_bits(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to start DATA_TX: %d\n", ret);
goto release_lock;
@ -754,9 +816,9 @@ static int fsl_xcvr_trigger(struct snd_pcm_substream *substream, int cmd,
if (tx) {
switch (xcvr->mode) {
case FSL_XCVR_MODE_SPDIF:
ret = regmap_write(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL_CLR,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
ret = regmap_clear_bits(xcvr->regmap,
FSL_XCVR_TX_DPTH_CTRL,
FSL_XCVR_TX_DPTH_CTRL_STRT_DATA_TX);
if (ret < 0) {
dev_err(dai->dev, "Failed to stop DATA_TX: %d\n", ret);
goto release_lock;
@ -1169,6 +1231,7 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
case FSL_XCVR_TX_DPTH_CTRL:
case FSL_XCVR_TX_DPTH_CTRL_SET:
case FSL_XCVR_TX_DPTH_CTRL_CLR:
case FSL_XCVR_TX_DPTH_CTRL_TOG:
@ -1190,7 +1253,49 @@ static bool fsl_xcvr_writeable_reg(struct device *dev, unsigned int reg)
static bool fsl_xcvr_volatile_reg(struct device *dev, unsigned int reg)
{
return fsl_xcvr_readable_reg(dev, reg);
switch (reg) {
case FSL_XCVR_EXT_STATUS:
case FSL_XCVR_EXT_ISR:
case FSL_XCVR_EXT_ISR_SET:
case FSL_XCVR_EXT_ISR_CLR:
case FSL_XCVR_EXT_ISR_TOG:
case FSL_XCVR_ISR:
case FSL_XCVR_ISR_SET:
case FSL_XCVR_ISR_CLR:
case FSL_XCVR_ISR_TOG:
case FSL_XCVR_PHY_AI_CTRL:
case FSL_XCVR_PHY_AI_CTRL_SET:
case FSL_XCVR_PHY_AI_CTRL_CLR:
case FSL_XCVR_PHY_AI_CTRL_TOG:
case FSL_XCVR_PHY_AI_RDATA:
case FSL_XCVR_RX_CS_DATA_0:
case FSL_XCVR_RX_CS_DATA_1:
case FSL_XCVR_RX_CS_DATA_2:
case FSL_XCVR_RX_CS_DATA_3:
case FSL_XCVR_RX_CS_DATA_4:
case FSL_XCVR_RX_CS_DATA_5:
case FSL_XCVR_RX_DPTH_CNTR_CTRL:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_SET:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_CLR:
case FSL_XCVR_RX_DPTH_CNTR_CTRL_TOG:
case FSL_XCVR_RX_DPTH_TSCR:
case FSL_XCVR_RX_DPTH_BCR:
case FSL_XCVR_RX_DPTH_BCTR:
case FSL_XCVR_RX_DPTH_BCRR:
case FSL_XCVR_TX_DPTH_CNTR_CTRL:
case FSL_XCVR_TX_DPTH_CNTR_CTRL_SET:
case FSL_XCVR_TX_DPTH_CNTR_CTRL_CLR:
case FSL_XCVR_TX_DPTH_CNTR_CTRL_TOG:
case FSL_XCVR_TX_DPTH_TSCR:
case FSL_XCVR_TX_DPTH_BCR:
case FSL_XCVR_TX_DPTH_BCTR:
case FSL_XCVR_TX_DPTH_BCRR:
case FSL_XCVR_DEBUG_REG_0:
case FSL_XCVR_DEBUG_REG_1:
return true;
default:
return false;
}
}
static const struct regmap_config fsl_xcvr_regmap_cfg = {
@ -1206,6 +1311,49 @@ static const struct regmap_config fsl_xcvr_regmap_cfg = {
.cache_type = REGCACHE_FLAT,
};
static const struct reg_default fsl_xcvr_phy_reg_defaults[] = {
{ FSL_XCVR_PHY_CTRL, 0x58200804 },
{ FSL_XCVR_PHY_STATUS, 0x00000000 },
{ FSL_XCVR_PHY_ANALOG_TRIM, 0x00260F13 },
{ FSL_XCVR_PHY_SLEW_RATE_TRIM, 0x00000411 },
{ FSL_XCVR_PHY_DATA_TEST_DELAY, 0x00990000 },
{ FSL_XCVR_PHY_TEST_CTRL, 0x00000000 },
{ FSL_XCVR_PHY_DIFF_CDR_CTRL, 0x016D0009 },
{ FSL_XCVR_PHY_CTRL2, 0x80000000 },
};
static const struct regmap_config fsl_xcvr_regmap_phy_cfg = {
.reg_bits = 8,
.reg_stride = 4,
.val_bits = 32,
.max_register = FSL_XCVR_PHY_CTRL2_TOG,
.reg_defaults = fsl_xcvr_phy_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(fsl_xcvr_phy_reg_defaults),
.cache_type = REGCACHE_FLAT,
.reg_read = fsl_xcvr_phy_reg_read,
.reg_write = fsl_xcvr_phy_reg_write,
};
static const struct regmap_config fsl_xcvr_regmap_pllv0_cfg = {
.reg_bits = 8,
.reg_stride = 4,
.val_bits = 32,
.max_register = FSL_XCVR_PLL_STAT0_TOG,
.cache_type = REGCACHE_FLAT,
.reg_read = fsl_xcvr_pll_reg_read,
.reg_write = fsl_xcvr_pll_reg_write,
};
static const struct regmap_config fsl_xcvr_regmap_pllv1_cfg = {
.reg_bits = 8,
.reg_stride = 4,
.val_bits = 32,
.max_register = FSL_XCVR_GP_PLL_STATUS_TOG,
.cache_type = REGCACHE_FLAT,
.reg_read = fsl_xcvr_pll_reg_read,
.reg_write = fsl_xcvr_pll_reg_write,
};
static void reset_rx_work(struct work_struct *work)
{
struct fsl_xcvr *xcvr = container_of(work, struct fsl_xcvr, work_rst);
@ -1405,6 +1553,15 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
fsl_asoc_get_pll_clocks(dev, &xcvr->pll8k_clk,
&xcvr->pll11k_clk);
if (xcvr->soc_data->spdif_only) {
if (!(xcvr->pll8k_clk || xcvr->pll11k_clk))
xcvr->pll8k_clk = xcvr->phy_clk;
fsl_asoc_constrain_rates(&xcvr->spdif_constr_rates,
&fsl_xcvr_spdif_rates_constr,
xcvr->pll8k_clk, xcvr->pll11k_clk, NULL,
xcvr->spdif_constr_rates_list);
}
xcvr->ram_addr = devm_platform_ioremap_resource_byname(pdev, "ram");
if (IS_ERR(xcvr->ram_addr))
return PTR_ERR(xcvr->ram_addr);
@ -1421,6 +1578,40 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
return PTR_ERR(xcvr->regmap);
}
if (xcvr->soc_data->use_phy) {
xcvr->regmap_phy = devm_regmap_init(dev, NULL, xcvr,
&fsl_xcvr_regmap_phy_cfg);
if (IS_ERR(xcvr->regmap_phy)) {
dev_err(dev, "failed to init XCVR PHY regmap: %ld\n",
PTR_ERR(xcvr->regmap_phy));
return PTR_ERR(xcvr->regmap_phy);
}
switch (xcvr->soc_data->pll_ver) {
case PLL_MX8MP:
xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
&fsl_xcvr_regmap_pllv0_cfg);
if (IS_ERR(xcvr->regmap_pll)) {
dev_err(dev, "failed to init XCVR PLL regmap: %ld\n",
PTR_ERR(xcvr->regmap_pll));
return PTR_ERR(xcvr->regmap_pll);
}
break;
case PLL_MX95:
xcvr->regmap_pll = devm_regmap_init(dev, NULL, xcvr,
&fsl_xcvr_regmap_pllv1_cfg);
if (IS_ERR(xcvr->regmap_pll)) {
dev_err(dev, "failed to init XCVR PLL regmap: %ld\n",
PTR_ERR(xcvr->regmap_pll));
return PTR_ERR(xcvr->regmap_pll);
}
break;
default:
dev_err(dev, "Error for PLL version %d\n", xcvr->soc_data->pll_ver);
return -EINVAL;
}
}
xcvr->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
if (IS_ERR(xcvr->reset)) {
dev_err(dev, "failed to get XCVR reset control\n");
@ -1454,6 +1645,10 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, xcvr);
pm_runtime_enable(dev);
regcache_cache_only(xcvr->regmap, true);
if (xcvr->soc_data->use_phy) {
regcache_cache_only(xcvr->regmap_phy, true);
regcache_cache_only(xcvr->regmap_pll, true);
}
/*
* Register platform component before registering cpu dai for there
@ -1492,7 +1687,8 @@ static int fsl_xcvr_runtime_suspend(struct device *dev)
struct fsl_xcvr *xcvr = dev_get_drvdata(dev);
int ret;
if (!xcvr->soc_data->spdif_only) {
if (!xcvr->soc_data->spdif_only &&
xcvr->mode == FSL_XCVR_MODE_EARC) {
/* Assert M0+ reset */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_CORE_RESET,
@ -1502,6 +1698,10 @@ static int fsl_xcvr_runtime_suspend(struct device *dev)
}
regcache_cache_only(xcvr->regmap, true);
if (xcvr->soc_data->use_phy) {
regcache_cache_only(xcvr->regmap_phy, true);
regcache_cache_only(xcvr->regmap_pll, true);
}
clk_disable_unprepare(xcvr->spba_clk);
clk_disable_unprepare(xcvr->phy_clk);
@ -1546,6 +1746,12 @@ static int fsl_xcvr_runtime_resume(struct device *dev)
goto stop_phy_clk;
}
ret = reset_control_deassert(xcvr->reset);
if (ret) {
dev_err(dev, "failed to deassert M0+ reset.\n");
goto stop_spba_clk;
}
regcache_cache_only(xcvr->regmap, false);
regcache_mark_dirty(xcvr->regmap);
ret = regcache_sync(xcvr->regmap);
@ -1555,31 +1761,49 @@ static int fsl_xcvr_runtime_resume(struct device *dev)
goto stop_spba_clk;
}
if (xcvr->soc_data->spdif_only)
return 0;
if (xcvr->soc_data->use_phy) {
ret = regmap_write(xcvr->regmap, FSL_XCVR_PHY_AI_CTRL_SET,
FSL_XCVR_PHY_AI_CTRL_AI_RESETN);
if (ret < 0) {
dev_err(dev, "Error while release PHY reset: %d\n", ret);
goto stop_spba_clk;
}
ret = reset_control_deassert(xcvr->reset);
if (ret) {
dev_err(dev, "failed to deassert M0+ reset.\n");
goto stop_spba_clk;
regcache_cache_only(xcvr->regmap_phy, false);
regcache_mark_dirty(xcvr->regmap_phy);
ret = regcache_sync(xcvr->regmap_phy);
if (ret) {
dev_err(dev, "failed to sync phy regcache.\n");
goto stop_spba_clk;
}
regcache_cache_only(xcvr->regmap_pll, false);
regcache_mark_dirty(xcvr->regmap_pll);
ret = regcache_sync(xcvr->regmap_pll);
if (ret) {
dev_err(dev, "failed to sync pll regcache.\n");
goto stop_spba_clk;
}
}
ret = fsl_xcvr_load_firmware(xcvr);
if (ret) {
dev_err(dev, "failed to load firmware.\n");
goto stop_spba_clk;
}
if (xcvr->mode == FSL_XCVR_MODE_EARC) {
ret = fsl_xcvr_load_firmware(xcvr);
if (ret) {
dev_err(dev, "failed to load firmware.\n");
goto stop_spba_clk;
}
/* Release M0+ reset */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
if (ret < 0) {
dev_err(dev, "M0+ core release failed: %d\n", ret);
goto stop_spba_clk;
}
/* Release M0+ reset */
ret = regmap_update_bits(xcvr->regmap, FSL_XCVR_EXT_CTRL,
FSL_XCVR_EXT_CTRL_CORE_RESET, 0);
if (ret < 0) {
dev_err(dev, "M0+ core release failed: %d\n", ret);
goto stop_spba_clk;
}
/* Let M0+ core complete firmware initialization */
msleep(50);
/* Let M0+ core complete firmware initialization */
msleep(50);
}
return 0;

View File

@ -234,6 +234,7 @@
#define FSL_XCVR_TX_DPTH_CTRL_TM_NO_PRE_BME GENMASK(31, 30)
#define FSL_XCVR_PHY_AI_CTRL_AI_RESETN BIT(15)
#define FSL_XCVR_PHY_AI_CTRL_AI_RWB BIT(31)
#define FSL_XCVR_PLL_CTRL0 0x00
#define FSL_XCVR_PLL_CTRL0_SET 0x04
@ -241,13 +242,25 @@
#define FSL_XCVR_PLL_NUM 0x20
#define FSL_XCVR_PLL_DEN 0x30
#define FSL_XCVR_PLL_PDIV 0x40
#define FSL_XCVR_PLL_BANDGAP 0x50
#define FSL_XCVR_PLL_BANDGAP_SET 0x54
#define FSL_XCVR_PLL_STAT0 0x60
#define FSL_XCVR_PLL_STAT0_TOG 0x6c
#define FSL_XCVR_PHY_CTRL 0x00
#define FSL_XCVR_PHY_CTRL_SET 0x04
#define FSL_XCVR_PHY_CTRL_CLR 0x08
#define FSL_XCVR_PHY_CTRL_TOG 0x0c
#define FSL_XCVR_PHY_STATUS 0x10
#define FSL_XCVR_PHY_ANALOG_TRIM 0x20
#define FSL_XCVR_PHY_SLEW_RATE_TRIM 0x30
#define FSL_XCVR_PHY_DATA_TEST_DELAY 0x40
#define FSL_XCVR_PHY_TEST_CTRL 0x50
#define FSL_XCVR_PHY_DIFF_CDR_CTRL 0x60
#define FSL_XCVR_PHY_CTRL2 0x70
#define FSL_XCVR_PHY_CTRL2_SET 0x74
#define FSL_XCVR_PHY_CTRL2_CLR 0x78
#define FSL_XCVR_PHY_CTRL2_TOG 0x7c
#define FSL_XCVR_PLL_BANDGAP_EN_VBG BIT(0)
#define FSL_XCVR_PLL_CTRL0_HROFF BIT(13)

View File

@ -365,8 +365,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, i);
if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(cpu_dai))
snd_soc_dai_set_sysclk(cpu_dai,
0, 0, SND_SOC_CLOCK_OUT);
snd_soc_dai_set_sysclk(cpu_dai, 0, 0, dai->clk_direction);
simple_clk_disable(dai);
}
@ -374,8 +373,7 @@ void simple_util_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, i);
if (props->mclk_fs && !dai->clk_fixed && !snd_soc_dai_active(codec_dai))
snd_soc_dai_set_sysclk(codec_dai,
0, 0, SND_SOC_CLOCK_IN);
snd_soc_dai_set_sysclk(codec_dai, 0, 0, dai->clk_direction);
simple_clk_disable(dai);
}
@ -483,13 +481,15 @@ int simple_util_hw_params(struct snd_pcm_substream *substream,
}
for_each_rtd_codec_dais(rtd, i, sdai) {
ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_IN);
pdai = simple_props_to_dai_codec(props, i);
ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction);
if (ret && ret != -ENOTSUPP)
return ret;
}
for_each_rtd_cpu_dais(rtd, i, sdai) {
ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, SND_SOC_CLOCK_OUT);
pdai = simple_props_to_dai_cpu(props, i);
ret = snd_soc_dai_set_sysclk(sdai, 0, mclk, pdai->clk_direction);
if (ret && ret != -ENOTSUPP)
return ret;
}
@ -1005,36 +1005,27 @@ EXPORT_SYMBOL_GPL(graph_util_card_probe);
int graph_util_is_ports0(struct device_node *np)
{
struct device_node *port, *ports, *ports0, *top;
int ret;
struct device_node *parent __free(device_node) = of_get_parent(np);
struct device_node *port;
/* np is "endpoint" or "port" */
if (of_node_name_eq(np, "endpoint")) {
port = of_get_parent(np);
} else {
if (of_node_name_eq(np, "endpoint"))
port = parent;
else
port = np;
of_node_get(port);
}
ports = of_get_parent(port);
top = of_get_parent(ports);
ports0 = of_get_child_by_name(top, "ports");
struct device_node *ports __free(device_node) = of_get_parent(port);
struct device_node *top __free(device_node) = of_get_parent(ports);
struct device_node *ports0 __free(device_node) = of_get_child_by_name(top, "ports");
ret = ports0 == ports;
of_node_put(port);
of_node_put(ports);
of_node_put(ports0);
of_node_put(top);
return ret;
return ports0 == ports;
}
EXPORT_SYMBOL_GPL(graph_util_is_ports0);
static int graph_get_dai_id(struct device_node *ep)
{
struct device_node *node;
struct device_node *endpoint;
struct device_node *node __free(device_node) = of_graph_get_port_parent(ep);
struct device_node *port __free(device_node) = of_get_parent(ep);
struct of_endpoint info;
int i, id;
int ret;
@ -1053,16 +1044,16 @@ static int graph_get_dai_id(struct device_node *ep)
* only of_graph_parse_endpoint().
* We need to check "reg" property
*/
if (of_property_present(ep, "reg"))
return info.id;
node = of_get_parent(ep);
ret = of_property_present(node, "reg");
of_node_put(node);
/* check port first */
ret = of_property_present(port, "reg");
if (ret)
return info.port;
/* check endpoint 2nd as backup */
if (of_property_present(ep, "reg"))
return info.id;
}
node = of_graph_get_port_parent(ep);
/*
* Non HDMI sound case, counting port/endpoint on its DT
@ -1070,14 +1061,14 @@ static int graph_get_dai_id(struct device_node *ep)
*/
i = 0;
id = -1;
for_each_endpoint_of_node(node, endpoint) {
if (endpoint == ep)
for_each_of_graph_port(node, p) {
if (port == p) {
id = i;
break;
}
i++;
}
of_node_put(node);
if (id < 0)
return -ENODEV;
@ -1087,7 +1078,6 @@ static int graph_get_dai_id(struct device_node *ep)
int graph_util_parse_dai(struct device *dev, struct device_node *ep,
struct snd_soc_dai_link_component *dlc, int *is_single_link)
{
struct device_node *node;
struct of_phandle_args args = {};
struct snd_soc_dai *dai;
int ret;
@ -1095,7 +1085,7 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep,
if (!ep)
return 0;
node = of_graph_get_port_parent(ep);
struct device_node *node __free(device_node) = of_graph_get_port_parent(ep);
/*
* Try to find from DAI node
@ -1136,10 +1126,8 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep,
* if he unbinded CPU or Codec.
*/
ret = snd_soc_get_dlc(&args, dlc);
if (ret < 0) {
of_node_put(node);
if (ret < 0)
return ret;
}
parse_dai_end:
if (is_single_link)

View File

@ -22,6 +22,8 @@ static int quirk_override = -1;
module_param_named(quirk, quirk_override, int, 0444);
MODULE_PARM_DESC(quirk, "Board-specific quirk override");
#define DMIC_DEFAULT_CHANNELS 2
static void log_quirks(struct device *dev)
{
if (SOC_SDW_JACK_JDSRC(sof_sdw_quirk))
@ -42,6 +44,8 @@ static void log_quirks(struct device *dev)
dev_dbg(dev, "quirk SOC_SDW_CODEC_SPKR enabled\n");
if (sof_sdw_quirk & SOC_SDW_SIDECAR_AMPS)
dev_dbg(dev, "quirk SOC_SDW_SIDECAR_AMPS enabled\n");
if (sof_sdw_quirk & SOC_SDW_CODEC_MIC)
dev_dbg(dev, "quirk SOC_SDW_CODEC_MIC enabled\n");
}
static int sof_sdw_quirk_cb(const struct dmi_system_id *id)
@ -624,9 +628,10 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
.callback = sof_sdw_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "380E")
DMI_MATCH(DMI_PRODUCT_NAME, "83HM")
},
.driver_data = (void *)(SOC_SDW_SIDECAR_AMPS),
.driver_data = (void *)(SOC_SDW_SIDECAR_AMPS |
SOC_SDW_CODEC_MIC),
},
{
.callback = sof_sdw_quirk_cb,
@ -1127,22 +1132,24 @@ static int sof_card_dai_links_create(struct snd_soc_card *card)
hdmi_num = SOF_PRE_TGL_HDMI_COUNT;
/* enable dmic01 & dmic16k */
if (sof_sdw_quirk & SOC_SDW_PCH_DMIC || mach_params->dmic_num) {
if (ctx->ignore_internal_dmic)
dev_warn(dev, "Ignoring PCH DMIC\n");
else
dmic_num = 2;
if (ctx->ignore_internal_dmic) {
dev_dbg(dev, "SoundWire DMIC is used, ignoring internal DMIC\n");
mach_params->dmic_num = 0;
} else if (mach_params->dmic_num) {
dmic_num = 2;
} else if (sof_sdw_quirk & SOC_SDW_PCH_DMIC) {
dmic_num = 2;
/*
* mach_params->dmic_num will be used to set the cfg-mics value of
* card->components string. Set it to the default value.
*/
mach_params->dmic_num = DMIC_DEFAULT_CHANNELS;
}
/*
* mach_params->dmic_num will be used to set the cfg-mics value of card->components
* string. Overwrite it to the actual number of PCH DMICs used in the device.
*/
mach_params->dmic_num = dmic_num;
if (sof_sdw_quirk & SOF_SSP_BT_OFFLOAD_PRESENT)
bt_num = 1;
dev_dbg(dev, "sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n",
dev_dbg(dev, "DAI link numbers: sdw %d, ssp %d, dmic %d, hdmi %d, bt: %d\n",
sdw_be_num, ssp_num, dmic_num,
intel_ctx->hdmi.idisp_codec ? hdmi_num : 0, bt_num);

View File

@ -138,7 +138,7 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_r1_adr[] = {
},
};
static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = {
static const struct snd_soc_acpi_adr_device cs35l56_3_l3_adr[] = {
{
.adr = 0x00033301fa355601ull,
.num_endpoints = 1,
@ -147,6 +147,24 @@ static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = {
},
};
static const struct snd_soc_acpi_adr_device cs35l56_2_r3_adr[] = {
{
.adr = 0x00023301fa355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "AMP2"
},
};
static const struct snd_soc_acpi_adr_device cs35l56_3_l1_adr[] = {
{
.adr = 0x00033101fa355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
};
static const struct snd_soc_acpi_endpoint cs42l43_endpoints[] = {
{ /* Jack Playback Endpoint */
.num = 0,
@ -304,6 +322,25 @@ static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_2_l23[] = {
.num_adr = ARRAY_SIZE(cs35l56_2_r1_adr),
.adr_d = cs35l56_2_r1_adr,
},
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(cs35l56_3_l3_adr),
.adr_d = cs35l56_3_l3_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr arl_cs42l43_l0_cs35l56_3_l23[] = {
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs42l43_0_adr),
.adr_d = cs42l43_0_adr,
},
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(cs35l56_2_r3_adr),
.adr_d = cs35l56_2_r3_adr,
},
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(cs35l56_3_l1_adr),
@ -406,6 +443,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
},
{
.link_mask = BIT(0) | BIT(2) | BIT(3),
.links = arl_cs42l43_l0_cs35l56_3_l23,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
},
{
.link_mask = BIT(0) | BIT(2),
.links = arl_cs42l43_l0_cs35l56_l2,

View File

@ -91,6 +91,23 @@ static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
},
};
static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
/* DMIC Endpoint */
{
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints_endpoints[] = {
/* Jack Endpoint */
{
@ -295,6 +312,24 @@ static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = {
{
.adr = 0x000130025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "rt1320-1"
}
};
static const struct snd_soc_acpi_adr_device rt1320_3_group2_adr[] = {
{
.adr = 0x000330025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "rt1320-2"
}
};
static const struct snd_soc_acpi_adr_device rt713_0_adr[] = {
{
.adr = 0x000031025D071301ull,
@ -304,6 +339,15 @@ static const struct snd_soc_acpi_adr_device rt713_0_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device rt713_vb_2_adr[] = {
{
.adr = 0x000230025d071301ull,
.num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
.endpoints = jack_dmic_endpoints,
.name_prefix = "rt713"
}
};
static const struct snd_soc_acpi_adr_device rt714_0_adr[] = {
{
.adr = 0x000030025D071401ull,
@ -453,6 +497,25 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_l0_rt1318_l1[] = {
{}
};
static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_vb_l2_rt1320_l13[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt713_vb_2_adr),
.adr_d = rt713_vb_2_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
.adr_d = rt1320_1_group2_adr,
},
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(rt1320_3_group2_adr),
.adr_d = rt1320_3_group2_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr lnl_sdw_rt712_vb_l2_rt1320_l1[] = {
{
.mask = BIT(2),
@ -550,6 +613,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
.sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg"
},
{
.link_mask = BIT(1) | BIT(2) | BIT(3),
.links = lnl_sdw_rt713_vb_l2_rt1320_l13,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
.sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg"
},
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines);

View File

@ -441,6 +441,179 @@ static const struct snd_soc_acpi_adr_device cs42l43_0_adr[] = {
}
};
/* CS42L43 - speaker DAI aggregated with 4 amps */
static const struct snd_soc_acpi_endpoint cs42l43_4amp_spkagg_endpoints[] = {
{ /* Jack Playback Endpoint */
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* DMIC Capture Endpoint */
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Jack Capture Endpoint */
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
{ /* Speaker Playback Endpoint */
.num = 3,
.aggregated = 1,
.group_position = 4,
.group_id = 1,
},
};
/* CS42L43 on link3 aggregated with 4 amps */
static const struct snd_soc_acpi_adr_device cs42l43_l3_4amp_spkagg_adr[] = {
{
.adr = 0x00033001FA424301ull,
.num_endpoints = ARRAY_SIZE(cs42l43_4amp_spkagg_endpoints),
.endpoints = cs42l43_4amp_spkagg_endpoints,
.name_prefix = "cs42l43"
}
};
static const struct snd_soc_acpi_endpoint cs35l56_l_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 0,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 0,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_r_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 1,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 1,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_2_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 2,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 2,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_3_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 3,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 3,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_4_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 4,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 4,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_5_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 5,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 5,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_6_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 6,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 6,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 7,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 7,
.group_id = 2,
},
};
static const struct snd_soc_acpi_adr_device cs35l56_0_adr[] = {
{
.adr = 0x00003301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "AMP1"
},
{
.adr = 0x00003201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.name_prefix = "AMP2"
}
};
static const struct snd_soc_acpi_adr_device cs35l56_1_adr[] = {
{
.adr = 0x00013701FA355601ull,
@ -471,17 +644,71 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device cs35l56_0_fb_adr[] = {
{
.adr = 0x00003301FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
.endpoints = cs35l56_l_fb_endpoints,
.name_prefix = "AMP1"
},
{
.adr = 0x00003201FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
.endpoints = cs35l56_2_fb_endpoints,
.name_prefix = "AMP2"
},
{
.adr = 0x00003101FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints),
.endpoints = cs35l56_4_fb_endpoints,
.name_prefix = "AMP3"
},
{
.adr = 0x00003001FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints),
.endpoints = cs35l56_6_fb_endpoints,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs35l56_1_fb_adr[] = {
{
.adr = 0x00013701FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
.endpoints = cs35l56_r_fb_endpoints,
.name_prefix = "AMP8"
},
{
.adr = 0x00013601FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
.endpoints = cs35l56_3_fb_endpoints,
.name_prefix = "AMP7"
},
{
.adr = 0x00013501FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints),
.endpoints = cs35l56_5_fb_endpoints,
.name_prefix = "AMP6"
},
{
.adr = 0x00013401FA355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints),
.endpoints = cs35l56_7_fb_endpoints,
.name_prefix = "AMP5"
},
};
static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = {
{
.adr = 0x00023201FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
.endpoints = cs35l56_r_fb_endpoints,
.name_prefix = "AMP3"
},
{
.adr = 0x00023301FA355601ull,
.num_endpoints = 1,
.endpoints = &spk_3_endpoint,
.num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
.endpoints = cs35l56_3_fb_endpoints,
.name_prefix = "AMP4"
}
@ -490,14 +717,14 @@ static const struct snd_soc_acpi_adr_device cs35l56_2_r_adr[] = {
static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = {
{
.adr = 0x00033001fa355601ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
.endpoints = cs35l56_l_fb_endpoints,
.name_prefix = "AMP1"
},
{
.adr = 0x00033101fa355601ull,
.num_endpoints = 1,
.endpoints = &spk_2_endpoint,
.num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
.endpoints = cs35l56_2_fb_endpoints,
.name_prefix = "AMP2"
}
};
@ -765,6 +992,40 @@ static const struct snd_soc_acpi_link_adr cs42l43_link0_cs35l56_link2_link3[] =
{}
};
static const struct snd_soc_acpi_link_adr cs42l43_link3_cs35l56_x4_link0_link1_spkagg[] = {
/* Expected order: jack -> amp */
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(cs42l43_l3_4amp_spkagg_adr),
.adr_d = cs42l43_l3_4amp_spkagg_adr,
},
{
.mask = BIT(1),
.num_adr = 2,
.adr_d = cs35l56_1_adr,
},
{
.mask = BIT(0),
.num_adr = 2,
.adr_d = cs35l56_0_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr mtl_cs35l56_x8_link0_link1_fb[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56_1_fb_adr),
.adr_d = cs35l56_1_fb_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l56_0_fb_adr),
.adr_d = cs35l56_0_fb_adr,
},
{}
};
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
/* mockup tests need to be first */
@ -841,12 +1102,24 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg",
},
{
.link_mask = BIT(0) | BIT(1) | BIT(3),
.links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg",
},
{
.link_mask = GENMASK(2, 0),
.links = mtl_cs42l43_cs35l56,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg",
},
{
.link_mask = BIT(0) | BIT(1),
.links = mtl_cs35l56_x8_link0_link1_fb,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg"
},
{
.link_mask = BIT(0),
.links = mtl_cs42l43_l0,

View File

@ -8,6 +8,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
#include "soc-acpi-intel-sdca-quirks.h"
#include "soc-acpi-intel-sdw-mockup-match.h"
#include <sound/soc-acpi-intel-ssp-common.h>
@ -35,6 +36,20 @@ static const struct snd_soc_acpi_endpoint single_endpoint = {
.group_id = 0,
};
static const struct snd_soc_acpi_endpoint spk_l_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 0,
.group_id = 1,
};
static const struct snd_soc_acpi_endpoint spk_r_endpoint = {
.num = 0,
.aggregated = 1,
.group_position = 1,
.group_id = 1,
};
/*
* Multi-function codecs with three endpoints created for
* headset, amp and dmic functions.
@ -60,6 +75,47 @@ static const struct snd_soc_acpi_endpoint rt_mf_endpoints[] = {
},
};
static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
/* DMIC Endpoint */
{
.num = 1,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
/* Amp Endpoint, work as spk_l_endpoint */
{
.num = 1,
.aggregated = 1,
.group_position = 0,
.group_id = 1,
},
/* DMIC Endpoint */
{
.num = 2,
.aggregated = 0,
.group_position = 0,
.group_id = 0,
},
};
static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
{
.adr = 0x000030025D071101ull,
@ -69,6 +125,24 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = {
{
.adr = 0x000230025D071201ull,
.num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints_endpoints),
.endpoints = jack_amp_g1_dmic_endpoints_endpoints,
.name_prefix = "rt712"
}
};
static const struct snd_soc_acpi_adr_device rt713_vb_2_adr[] = {
{
.adr = 0x000230025d071301ull,
.num_endpoints = ARRAY_SIZE(jack_dmic_endpoints),
.endpoints = jack_dmic_endpoints,
.name_prefix = "rt713"
}
};
static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = {
{
.adr = 0x000330025d072101ull,
@ -114,6 +188,33 @@ static const struct snd_soc_acpi_adr_device rt722_3_single_adr[] = {
}
};
static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = {
{
.adr = 0x000130025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "rt1320-1"
}
};
static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = {
{
.adr = 0x000130025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_l_endpoint,
.name_prefix = "rt1320-1"
}
};
static const struct snd_soc_acpi_adr_device rt1320_3_group2_adr[] = {
{
.adr = 0x000330025D132001ull,
.num_endpoints = 1,
.endpoints = &spk_r_endpoint,
.name_prefix = "rt1320-2"
}
};
static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = {
{
.mask = BIT(0),
@ -150,6 +251,39 @@ static const struct snd_soc_acpi_link_adr ptl_rvp[] = {
{}
};
static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_vb_l2_rt1320_l13[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt713_vb_2_adr),
.adr_d = rt713_vb_2_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
.adr_d = rt1320_1_group2_adr,
},
{
.mask = BIT(3),
.num_adr = ARRAY_SIZE(rt1320_3_group2_adr),
.adr_d = rt1320_3_group2_adr,
},
{}
};
static const struct snd_soc_acpi_link_adr lnl_sdw_rt712_vb_l2_rt1320_l1[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt712_vb_2_group1_adr),
.adr_d = rt712_vb_2_group1_adr,
},
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(rt1320_1_group1_adr),
.adr_d = rt1320_1_group1_adr,
},
{}
};
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
/* mockup tests need to be first */
@ -201,6 +335,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
},
{
.link_mask = BIT(1) | BIT(2),
.links = lnl_sdw_rt712_vb_l2_rt1320_l1,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
.sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg"
},
{
.link_mask = BIT(1) | BIT(2) | BIT(3),
.links = lnl_sdw_rt713_vb_l2_rt1320_l13,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
.sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg"
},
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_sdw_machines);

View File

@ -536,6 +536,194 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_machines[] = {
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines);
static const struct snd_soc_acpi_endpoint cs35l56_l_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 0,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 0,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_r_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 1,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 1,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_2_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 2,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 2,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_3_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 3,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 3,
.group_id = 2,
},
};
static const struct snd_soc_acpi_endpoint cs35l56_4_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 4,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 4,
.group_id = 2,
}
};
static const struct snd_soc_acpi_endpoint cs35l56_5_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 5,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 5,
.group_id = 2,
}
};
static const struct snd_soc_acpi_endpoint cs35l56_6_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 6,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 6,
.group_id = 2,
}
};
static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = {
{ /* Speaker Playback Endpoint */
.num = 0,
.aggregated = 1,
.group_position = 7,
.group_id = 1,
},
{ /* Feedback Capture Endpoint */
.num = 1,
.aggregated = 1,
.group_position = 7,
.group_id = 2,
}
};
static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_1_4_fb_adr[] = {
{
.adr = 0x00003301fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
.endpoints = cs35l56_l_fb_endpoints,
.name_prefix = "AMP1"
},
{
.adr = 0x00003201fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
.endpoints = cs35l56_2_fb_endpoints,
.name_prefix = "AMP2"
},
{
.adr = 0x00003101fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints),
.endpoints = cs35l56_4_fb_endpoints,
.name_prefix = "AMP3"
},
{
.adr = 0x00003001fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints),
.endpoints = cs35l56_6_fb_endpoints,
.name_prefix = "AMP4"
},
};
static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_5_8_fb_adr[] = {
{
.adr = 0x00013701fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
.endpoints = cs35l56_r_fb_endpoints,
.name_prefix = "AMP8"
},
{
.adr = 0x00013601fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
.endpoints = cs35l56_3_fb_endpoints,
.name_prefix = "AMP7"
},
{
.adr = 0x00013501fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints),
.endpoints = cs35l56_5_fb_endpoints,
.name_prefix = "AMP6"
},
{
.adr = 0x00013401fa355601,
.num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints),
.endpoints = cs35l56_7_fb_endpoints,
.name_prefix = "AMP5"
},
};
static const struct snd_soc_acpi_link_adr up_extreme_cs35l56_sdw_eight[] = {
{
.mask = BIT(1),
.num_adr = ARRAY_SIZE(cs35l56_sdw_eight_5_8_fb_adr),
.adr_d = cs35l56_sdw_eight_5_8_fb_adr,
},
{
.mask = BIT(0),
.num_adr = ARRAY_SIZE(cs35l56_sdw_eight_1_4_fb_adr),
.adr_d = cs35l56_sdw_eight_1_4_fb_adr,
},
{}
};
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = {
/* mockup tests need to be first */
@ -635,6 +823,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = {
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-tgl-rt711.tplg",
},
{
.link_mask = BIT(0) | BIT(1),
.links = up_extreme_cs35l56_sdw_eight,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-tgl-cs35l56-l01-fb8.tplg"
},
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_sdw_machines);

View File

@ -2158,27 +2158,26 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
struct mt8192_afe_private *afe_priv;
struct device *dev;
struct device *dev = &pdev->dev;
struct reset_control *rstc;
int i, ret, irq_id;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(34));
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
if (ret)
return ret;
afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
if (!afe)
return -ENOMEM;
platform_set_drvdata(pdev, afe);
afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
GFP_KERNEL);
if (!afe->platform_priv)
return -ENOMEM;
afe_priv = afe->platform_priv;
afe->dev = &pdev->dev;
dev = afe->dev;
afe->dev = dev;
/* init audio related clock */
ret = mt8192_init_clock(afe);
@ -2196,7 +2195,7 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret)
return dev_err_probe(dev, ret, "failed to trigger audio reset\n");
ret = devm_pm_runtime_enable(&pdev->dev);
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
@ -2212,13 +2211,13 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
/* enable clock for regcache get default value from hw */
afe_priv->pm_runtime_bypass_reg_ctl = true;
pm_runtime_get_sync(&pdev->dev);
pm_runtime_get_sync(dev);
ret = regmap_reinit_cache(afe->regmap, &mt8192_afe_regmap_config);
if (ret)
return dev_err_probe(dev, ret, "regmap_reinit_cache fail\n");
pm_runtime_put_sync(&pdev->dev);
pm_runtime_put_sync(dev);
afe_priv->pm_runtime_bypass_reg_ctl = false;
regcache_cache_only(afe->regmap, true);
@ -2285,7 +2284,7 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
afe->runtime_suspend = mt8192_afe_runtime_suspend;
/* register platform */
ret = devm_snd_soc_register_component(&pdev->dev,
ret = devm_snd_soc_register_component(dev,
&mtk_afe_pcm_platform,
afe->dai_drivers,
afe->num_dai_drivers);

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
# MTK Platform driver
snd-soc-mt8365-pcm-objs := \
snd-soc-mt8365-pcm-y := \
mt8365-afe-clk.o \
mt8365-afe-pcm.o \
mt8365-dai-adda.o \

View File

@ -6,12 +6,19 @@
* Authors: Nicolas Belin <nbelin@baylibre.com>
*/
#include <linux/array_size.h>
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>
#include <linux/types.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "mt8365-afe-common.h"
#include <linux/pinctrl/consumer.h>
#include "../common/mtk-soc-card.h"
#include "../common/mtk-soundcard-driver.h"

View File

@ -215,6 +215,7 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream,
ret = sdm845_slim_snd_hw_params(substream, params);
break;
case QUATERNARY_MI2S_RX:
case SECONDARY_MI2S_RX:
break;
default:
pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id);
@ -356,6 +357,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt);
break;
case SECONDARY_MI2S_RX:
case SECONDARY_MI2S_TX:
codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_I2S;
if (++(data->sec_mi2s_clk_count) == 1) {
@ -371,8 +373,6 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
snd_soc_dai_set_fmt(cpu_dai, fmt);
break;
case QUATERNARY_TDM_RX_0:
@ -441,6 +441,7 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream)
}
break;
case SECONDARY_MI2S_RX:
case SECONDARY_MI2S_TX:
if (--(data->sec_mi2s_clk_count) == 0) {
snd_soc_dai_set_sysclk(cpu_dai,

View File

@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <linux/dmaengine.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@ -71,7 +72,7 @@
#define PREALLOC_BUFFER (SZ_32K)
#define PREALLOC_BUFFER_MAX (SZ_32K)
#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-44.1kHz */
#define SSI_RATES SNDRV_PCM_RATE_8000_48000 /* 8k-48kHz */
#define SSI_FMTS SNDRV_PCM_FMTBIT_S16_LE
#define SSI_CHAN_MIN 2
#define SSI_CHAN_MAX 2
@ -99,7 +100,6 @@ struct rz_ssi_stream {
struct rz_ssi_priv {
void __iomem *base;
struct platform_device *pdev;
struct reset_control *rstc;
struct device *dev;
struct clk *sfr_clk;
@ -163,16 +163,7 @@ static void rz_ssi_reg_mask_setl(struct rz_ssi_priv *priv, uint reg,
writel(val, (priv->base + reg));
}
static inline struct snd_soc_dai *
rz_ssi_get_dai(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
return snd_soc_rtd_to_cpu(rtd, 0);
}
static inline bool rz_ssi_stream_is_play(struct rz_ssi_priv *ssi,
struct snd_pcm_substream *substream)
static inline bool rz_ssi_stream_is_play(struct snd_pcm_substream *substream)
{
return substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
}
@ -244,22 +235,21 @@ static void rz_ssi_stream_init(struct rz_ssi_stream *strm,
static void rz_ssi_stream_quit(struct rz_ssi_priv *ssi,
struct rz_ssi_stream *strm)
{
struct snd_soc_dai *dai = rz_ssi_get_dai(strm->substream);
struct device *dev = ssi->dev;
rz_ssi_set_substream(strm, NULL);
if (strm->oerr_num > 0)
dev_info(dai->dev, "overrun = %d\n", strm->oerr_num);
dev_info(dev, "overrun = %d\n", strm->oerr_num);
if (strm->uerr_num > 0)
dev_info(dai->dev, "underrun = %d\n", strm->uerr_num);
dev_info(dev, "underrun = %d\n", strm->uerr_num);
}
static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
unsigned int channels)
{
static s8 ckdv[16] = { 1, 2, 4, 8, 16, 32, 64, 128,
6, 12, 24, 48, 96, -1, -1, -1 };
static u8 ckdv[] = { 1, 2, 4, 8, 16, 32, 64, 128, 6, 12, 24, 48, 96 };
unsigned int channel_bits = 32; /* System Word Length */
unsigned long bclk_rate = rate * channels * channel_bits;
unsigned int div;
@ -318,7 +308,8 @@ static int rz_ssi_clk_setup(struct rz_ssi_priv *ssi, unsigned int rate,
static void rz_ssi_set_idle(struct rz_ssi_priv *ssi)
{
int timeout;
u32 tmp;
int ret;
/* Disable irqs */
rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TUIEN | SSICR_TOIEN |
@ -331,15 +322,9 @@ static void rz_ssi_set_idle(struct rz_ssi_priv *ssi)
SSISR_RUIRQ), 0);
/* Wait for idle */
timeout = 100;
while (--timeout) {
if (rz_ssi_reg_readl(ssi, SSISR) & SSISR_IIRQ)
break;
udelay(1);
}
if (!timeout)
dev_info(ssi->dev, "timeout waiting for SSI idle\n");
ret = readl_poll_timeout_atomic(ssi->base + SSISR, tmp, (tmp & SSISR_IIRQ), 1, 100);
if (ret)
dev_warn_ratelimited(ssi->dev, "timeout waiting for SSI idle\n");
/* Hold FIFOs in reset */
rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_FIFO_RST);
@ -347,7 +332,7 @@ static void rz_ssi_set_idle(struct rz_ssi_priv *ssi)
static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
{
bool is_play = rz_ssi_stream_is_play(ssi, strm->substream);
bool is_play = rz_ssi_stream_is_play(strm->substream);
bool is_full_duplex;
u32 ssicr, ssifcr;
@ -403,6 +388,15 @@ static int rz_ssi_start(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
return 0;
}
static int rz_ssi_swreset(struct rz_ssi_priv *ssi)
{
u32 tmp;
rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST);
rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0);
return readl_poll_timeout_atomic(ssi->base + SSIFCR, tmp, !(tmp & SSIFCR_SSIRST), 1, 5);
}
static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
{
strm->running = 0;
@ -415,8 +409,12 @@ static int rz_ssi_stop(struct rz_ssi_priv *ssi, struct rz_ssi_stream *strm)
rz_ssi_reg_mask_setl(ssi, SSICR, SSICR_TEN | SSICR_REN, 0);
/* Cancel all remaining DMA transactions */
if (rz_ssi_is_dma_enabled(ssi))
dmaengine_terminate_async(strm->dma_ch);
if (rz_ssi_is_dma_enabled(ssi)) {
if (ssi->playback.dma_ch)
dmaengine_terminate_async(ssi->playback.dma_ch);
if (ssi->capture.dma_ch)
dmaengine_terminate_async(ssi->capture.dma_ch);
}
rz_ssi_set_idle(ssi);
@ -680,7 +678,7 @@ static int rz_ssi_dma_transfer(struct rz_ssi_priv *ssi,
*/
return 0;
dir = rz_ssi_stream_is_play(ssi, substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
dir = rz_ssi_stream_is_play(substream) ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
/* Always transfer 1 period */
amount = runtime->period_size;
@ -784,6 +782,32 @@ static int rz_ssi_dma_request(struct rz_ssi_priv *ssi, struct device *dev)
return -ENODEV;
}
static int rz_ssi_trigger_resume(struct rz_ssi_priv *ssi)
{
int ret;
if (rz_ssi_is_stream_running(&ssi->playback) ||
rz_ssi_is_stream_running(&ssi->capture))
return 0;
ret = rz_ssi_swreset(ssi);
if (ret)
return ret;
return rz_ssi_clk_setup(ssi, ssi->hw_params_cache.rate,
ssi->hw_params_cache.channels);
}
static void rz_ssi_streams_suspend(struct rz_ssi_priv *ssi)
{
if (rz_ssi_is_stream_running(&ssi->playback) ||
rz_ssi_is_stream_running(&ssi->capture))
return;
ssi->playback.dma_buffer_pos = 0;
ssi->capture.dma_buffer_pos = 0;
}
static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
@ -792,21 +816,21 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
int ret = 0, i, num_transfer = 1;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
/* Soft Reset */
if (!rz_ssi_is_stream_running(&ssi->playback) &&
!rz_ssi_is_stream_running(&ssi->capture)) {
rz_ssi_reg_mask_setl(ssi, SSIFCR, 0, SSIFCR_SSIRST);
rz_ssi_reg_mask_setl(ssi, SSIFCR, SSIFCR_SSIRST, 0);
udelay(5);
}
case SNDRV_PCM_TRIGGER_RESUME:
ret = rz_ssi_trigger_resume(ssi);
if (ret)
return ret;
rz_ssi_stream_init(strm, substream);
fallthrough;
case SNDRV_PCM_TRIGGER_START:
if (cmd == SNDRV_PCM_TRIGGER_START)
rz_ssi_stream_init(strm, substream);
if (ssi->dma_rt) {
bool is_playback;
is_playback = rz_ssi_stream_is_play(ssi, substream);
is_playback = rz_ssi_stream_is_play(substream);
ret = rz_ssi_dma_slave_config(ssi, ssi->playback.dma_ch,
is_playback);
/* Fallback to pio */
@ -829,6 +853,12 @@ static int rz_ssi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
ret = rz_ssi_start(ssi, strm);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
rz_ssi_stop(ssi, strm);
rz_ssi_streams_suspend(ssi);
break;
case SNDRV_PCM_TRIGGER_STOP:
rz_ssi_stop(ssi, strm);
rz_ssi_stream_quit(ssi, strm);
@ -925,6 +955,7 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min;
unsigned int channels = params_channels(params);
unsigned int rate = params_rate(params);
int ret;
if (sample_bits != 16) {
dev_err(ssi->dev, "Unsupported sample width: %d\n",
@ -951,6 +982,10 @@ static int rz_ssi_dai_hw_params(struct snd_pcm_substream *substream,
rz_ssi_cache_hw_params(ssi, rate, channels, strm->sample_width,
sample_bits);
ret = rz_ssi_swreset(ssi);
if (ret)
return ret;
return rz_ssi_clk_setup(ssi, rate, channels);
}
@ -963,7 +998,8 @@ static const struct snd_soc_dai_ops rz_ssi_dai_ops = {
static const struct snd_pcm_hardware rz_ssi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID,
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_RESUME,
.buffer_bytes_max = PREALLOC_BUFFER,
.period_bytes_min = 32,
.period_bytes_max = 8192,
@ -986,7 +1022,8 @@ static int rz_ssi_pcm_open(struct snd_soc_component *component,
static snd_pcm_uframes_t rz_ssi_pcm_pointer(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_dai *dai = rz_ssi_get_dai(substream);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
struct rz_ssi_priv *ssi = snd_soc_dai_get_drvdata(dai);
struct rz_ssi_stream *strm = rz_ssi_stream_get(ssi, substream);
@ -1031,37 +1068,37 @@ static const struct snd_soc_component_driver rz_ssi_soc_component = {
static int rz_ssi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rz_ssi_priv *ssi;
struct clk *audio_clk;
struct resource *res;
int ret;
ssi = devm_kzalloc(&pdev->dev, sizeof(*ssi), GFP_KERNEL);
ssi = devm_kzalloc(dev, sizeof(*ssi), GFP_KERNEL);
if (!ssi)
return -ENOMEM;
ssi->pdev = pdev;
ssi->dev = &pdev->dev;
ssi->dev = dev;
ssi->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(ssi->base))
return PTR_ERR(ssi->base);
ssi->phys = res->start;
ssi->clk = devm_clk_get(&pdev->dev, "ssi");
ssi->clk = devm_clk_get(dev, "ssi");
if (IS_ERR(ssi->clk))
return PTR_ERR(ssi->clk);
ssi->sfr_clk = devm_clk_get(&pdev->dev, "ssi_sfr");
ssi->sfr_clk = devm_clk_get(dev, "ssi_sfr");
if (IS_ERR(ssi->sfr_clk))
return PTR_ERR(ssi->sfr_clk);
audio_clk = devm_clk_get(&pdev->dev, "audio_clk1");
audio_clk = devm_clk_get(dev, "audio_clk1");
if (IS_ERR(audio_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
"no audio clk1");
ssi->audio_clk_1 = clk_get_rate(audio_clk);
audio_clk = devm_clk_get(&pdev->dev, "audio_clk2");
audio_clk = devm_clk_get(dev, "audio_clk2");
if (IS_ERR(audio_clk))
return dev_err_probe(&pdev->dev, PTR_ERR(audio_clk),
"no audio clk2");
@ -1074,13 +1111,13 @@ static int rz_ssi_probe(struct platform_device *pdev)
ssi->audio_mck = ssi->audio_clk_1 ? ssi->audio_clk_1 : ssi->audio_clk_2;
/* Detect DMA support */
ret = rz_ssi_dma_request(ssi, &pdev->dev);
ret = rz_ssi_dma_request(ssi, dev);
if (ret < 0) {
dev_warn(&pdev->dev, "DMA not available, using PIO\n");
dev_warn(dev, "DMA not available, using PIO\n");
ssi->playback.transfer = rz_ssi_pio_send;
ssi->capture.transfer = rz_ssi_pio_recv;
} else {
dev_info(&pdev->dev, "DMA enabled");
dev_info(dev, "DMA enabled");
ssi->playback.transfer = rz_ssi_dma_transfer;
ssi->capture.transfer = rz_ssi_dma_transfer;
}
@ -1089,21 +1126,20 @@ static int rz_ssi_probe(struct platform_device *pdev)
ssi->capture.priv = ssi;
spin_lock_init(&ssi->lock);
dev_set_drvdata(&pdev->dev, ssi);
dev_set_drvdata(dev, ssi);
/* Error Interrupt */
ssi->irq_int = platform_get_irq_byname(pdev, "int_req");
if (ssi->irq_int < 0) {
rz_ssi_release_dma_channels(ssi);
return ssi->irq_int;
ret = ssi->irq_int;
goto err_release_dma_chs;
}
ret = devm_request_irq(&pdev->dev, ssi->irq_int, &rz_ssi_interrupt,
0, dev_name(&pdev->dev), ssi);
ret = devm_request_irq(dev, ssi->irq_int, &rz_ssi_interrupt,
0, dev_name(dev), ssi);
if (ret < 0) {
rz_ssi_release_dma_channels(ssi);
return dev_err_probe(&pdev->dev, ret,
"irq request error (int_req)\n");
dev_err_probe(dev, ret, "irq request error (int_req)\n");
goto err_release_dma_chs;
}
if (!rz_ssi_is_dma_enabled(ssi)) {
@ -1115,11 +1151,11 @@ static int rz_ssi_probe(struct platform_device *pdev)
if (ssi->irq_rt < 0)
return ssi->irq_rt;
ret = devm_request_irq(&pdev->dev, ssi->irq_rt,
ret = devm_request_irq(dev, ssi->irq_rt,
&rz_ssi_interrupt, 0,
dev_name(&pdev->dev), ssi);
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
return dev_err_probe(dev, ret,
"irq request error (dma_rt)\n");
} else {
if (ssi->irq_tx < 0)
@ -1128,52 +1164,48 @@ static int rz_ssi_probe(struct platform_device *pdev)
if (ssi->irq_rx < 0)
return ssi->irq_rx;
ret = devm_request_irq(&pdev->dev, ssi->irq_tx,
ret = devm_request_irq(dev, ssi->irq_tx,
&rz_ssi_interrupt, 0,
dev_name(&pdev->dev), ssi);
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
return dev_err_probe(dev, ret,
"irq request error (dma_tx)\n");
ret = devm_request_irq(&pdev->dev, ssi->irq_rx,
ret = devm_request_irq(dev, ssi->irq_rx,
&rz_ssi_interrupt, 0,
dev_name(&pdev->dev), ssi);
dev_name(dev), ssi);
if (ret < 0)
return dev_err_probe(&pdev->dev, ret,
return dev_err_probe(dev, ret,
"irq request error (dma_rx)\n");
}
}
ssi->rstc = devm_reset_control_get_exclusive(&pdev->dev, NULL);
ssi->rstc = devm_reset_control_get_exclusive(dev, NULL);
if (IS_ERR(ssi->rstc)) {
ret = PTR_ERR(ssi->rstc);
goto err_reset;
goto err_release_dma_chs;
}
reset_control_deassert(ssi->rstc);
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_resume_and_get(&pdev->dev);
/* Default 0 for power saving. Can be overridden via sysfs. */
pm_runtime_set_autosuspend_delay(dev, 0);
pm_runtime_use_autosuspend(dev);
ret = devm_pm_runtime_enable(dev);
if (ret < 0) {
dev_err(&pdev->dev, "pm_runtime_resume_and_get failed\n");
goto err_pm;
dev_err(dev, "Failed to enable runtime PM!\n");
goto err_release_dma_chs;
}
ret = devm_snd_soc_register_component(&pdev->dev, &rz_ssi_soc_component,
ret = devm_snd_soc_register_component(dev, &rz_ssi_soc_component,
rz_ssi_soc_dai,
ARRAY_SIZE(rz_ssi_soc_dai));
if (ret < 0) {
dev_err(&pdev->dev, "failed to register snd component\n");
goto err_snd_soc;
dev_err(dev, "failed to register snd component\n");
goto err_release_dma_chs;
}
return 0;
err_snd_soc:
pm_runtime_put(ssi->dev);
err_pm:
pm_runtime_disable(ssi->dev);
reset_control_assert(ssi->rstc);
err_reset:
err_release_dma_chs:
rz_ssi_release_dma_channels(ssi);
return ret;
@ -1185,8 +1217,6 @@ static void rz_ssi_remove(struct platform_device *pdev)
rz_ssi_release_dma_channels(ssi);
pm_runtime_put(ssi->dev);
pm_runtime_disable(ssi->dev);
reset_control_assert(ssi->rstc);
}
@ -1196,10 +1226,30 @@ static const struct of_device_id rz_ssi_of_match[] = {
};
MODULE_DEVICE_TABLE(of, rz_ssi_of_match);
static int rz_ssi_runtime_suspend(struct device *dev)
{
struct rz_ssi_priv *ssi = dev_get_drvdata(dev);
return reset_control_assert(ssi->rstc);
}
static int rz_ssi_runtime_resume(struct device *dev)
{
struct rz_ssi_priv *ssi = dev_get_drvdata(dev);
return reset_control_deassert(ssi->rstc);
}
static const struct dev_pm_ops rz_ssi_pm_ops = {
RUNTIME_PM_OPS(rz_ssi_runtime_suspend, rz_ssi_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
static struct platform_driver rz_ssi_driver = {
.driver = {
.name = "rz-ssi-pcm-audio",
.of_match_table = rz_ssi_of_match,
.pm = pm_ptr(&rz_ssi_pm_ops),
},
.probe = rz_ssi_probe,
.remove = rz_ssi_remove,

View File

@ -514,33 +514,6 @@ static void rockchip_i2s_tdm_xfer_resume(struct snd_pcm_substream *substream,
I2S_XFER_RXS_START);
}
static int rockchip_i2s_ch_to_io(unsigned int ch, bool substream_capture)
{
if (substream_capture) {
switch (ch) {
case I2S_CHN_4:
return I2S_IO_6CH_OUT_4CH_IN;
case I2S_CHN_6:
return I2S_IO_4CH_OUT_6CH_IN;
case I2S_CHN_8:
return I2S_IO_2CH_OUT_8CH_IN;
default:
return I2S_IO_8CH_OUT_2CH_IN;
}
} else {
switch (ch) {
case I2S_CHN_4:
return I2S_IO_4CH_OUT_6CH_IN;
case I2S_CHN_6:
return I2S_IO_6CH_OUT_4CH_IN;
case I2S_CHN_8:
return I2S_IO_8CH_OUT_2CH_IN;
default:
return I2S_IO_2CH_OUT_8CH_IN;
}
}
}
static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@ -577,7 +550,6 @@ static int rockchip_i2s_io_multiplex(struct snd_pcm_substream *substream,
return -EINVAL;
}
rockchip_i2s_ch_to_io(val, true);
} else {
struct snd_pcm_str *capture_str =
&substream->pcm->streams[SNDRV_PCM_STREAM_CAPTURE];

View File

@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
snd-soc-sdca-objs := sdca_functions.o sdca_device.o
snd-soc-sdca-y := sdca_functions.o sdca_device.o
obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o

View File

@ -15,6 +15,7 @@
#include <sound/soc_sdw_utils.h>
#define CODEC_NAME_SIZE 8
#define CS_AMP_CHANNELS_PER_AMP 4
int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
@ -48,6 +49,51 @@ int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai
}
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
const struct snd_soc_dai_link *dai_link = rtd->dai_link;
const struct snd_soc_dai_link_ch_map *ch_map;
const struct snd_soc_dai_link_component *codec_dlc;
struct snd_soc_dai *codec_dai;
u8 ch_slot[8] = {};
unsigned int amps_per_bus, ch_per_amp, mask;
int i, ret;
WARN_ON(dai_link->num_cpus > ARRAY_SIZE(ch_slot));
/*
* CS35L56 has 4 TX channels. When the capture is aggregated the
* same bus slots will be allocated to all the amps on a bus. Only
* one amp on that bus can be transmitting in each slot so divide
* the available 4 slots between all the amps on a bus.
*/
amps_per_bus = dai_link->num_codecs / dai_link->num_cpus;
if ((amps_per_bus == 0) || (amps_per_bus > CS_AMP_CHANNELS_PER_AMP)) {
dev_err(rtd->card->dev, "Illegal num_codecs:%u / num_cpus:%u\n",
dai_link->num_codecs, dai_link->num_cpus);
return -EINVAL;
}
ch_per_amp = CS_AMP_CHANNELS_PER_AMP / amps_per_bus;
for_each_rtd_ch_maps(rtd, i, ch_map) {
codec_dlc = snd_soc_link_to_codec(rtd->dai_link, i);
codec_dai = snd_soc_find_dai(codec_dlc);
mask = GENMASK(ch_per_amp - 1, 0) << ch_slot[ch_map->cpu];
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0, mask, 4, 32);
if (ret < 0) {
dev_err(rtd->card->dev, "Failed to set TDM slot:%d\n", ret);
return ret;
}
ch_slot[ch_map->cpu] += ch_per_amp;
}
return 0;
}
EXPORT_SYMBOL_NS(asoc_sdw_cs_spk_feedback_rtd_init, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_links,
struct asoc_sdw_codec_info *info,

View File

@ -488,10 +488,10 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.part_id = 0x3556,
.dais = {
{
.direction = {true, true},
.direction = {true, false},
.dai_name = "cs35l56-sdw1",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_cs_amp_init,
.rtd_init = asoc_sdw_cs_spk_rtd_init,
.controls = generic_spk_controls,
@ -499,8 +499,15 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.widgets = generic_spk_widgets,
.num_widgets = ARRAY_SIZE(generic_spk_widgets),
},
{
.direction = {false, true},
.dai_name = "cs35l56-sdw1c",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
},
},
.dai_num = 1,
.dai_num = 2,
},
{
.part_id = 0x4242,

View File

@ -1646,6 +1646,7 @@ static int soc_probe_component(struct snd_soc_card *card,
component->driver->num_dapm_routes);
if (ret < 0) {
if (card->disable_route_checks) {
ret = 0;
dev_info(card->dev,
"%s: disable_route_checks set, ignoring errors on add_routes\n",
__func__);

View File

@ -38,7 +38,6 @@ static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd,
switch (ret) {
case -EPROBE_DEFER:
case -ENOTSUPP:
case -EINVAL:
break;
default:
dev_err(rtd->dev,
@ -986,7 +985,13 @@ static int __soc_pcm_prepare(struct snd_soc_pcm_runtime *rtd,
}
out:
return soc_pcm_ret(rtd, ret);
/*
* Don't use soc_pcm_ret() on .prepare callback to lower error log severity
*
* We don't want to log an error since we do not want to give userspace a way to do a
* denial-of-service attack on the syslog / diskspace.
*/
return ret;
}
/* PCM prepare ops for non-DPCM streams */
@ -998,6 +1003,13 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dpcm_mutex_lock(rtd);
ret = __soc_pcm_prepare(rtd, substream);
snd_soc_dpcm_mutex_unlock(rtd);
/*
* Don't use soc_pcm_ret() on .prepare callback to lower error log severity
*
* We don't want to log an error since we do not want to give userspace a way to do a
* denial-of-service attack on the syslog / diskspace.
*/
return ret;
}
@ -2539,7 +2551,13 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
be->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
}
return soc_pcm_ret(fe, ret);
/*
* Don't use soc_pcm_ret() on .prepare callback to lower error log severity
*
* We don't want to log an error since we do not want to give userspace a way to do a
* denial-of-service attack on the syslog / diskspace.
*/
return ret;
}
static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
@ -2579,7 +2597,13 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
snd_soc_dpcm_mutex_unlock(fe);
return soc_pcm_ret(fe, ret);
/*
* Don't use soc_pcm_ret() on .prepare callback to lower error log severity
*
* We don't want to log an error since we do not want to give userspace a way to do a
* denial-of-service attack on the syslog / diskspace.
*/
return ret;
}
static int dpcm_run_update_shutdown(struct snd_soc_pcm_runtime *fe, int stream)

View File

@ -1102,12 +1102,14 @@ static int soc_tplg_dapm_graph_elems_load(struct soc_tplg *tplg,
ret = snd_soc_dapm_add_routes(dapm, route, 1);
if (ret) {
if (!dapm->card->disable_route_checks) {
if (dapm->card->disable_route_checks) {
ret = 0;
dev_info(tplg->dev,
"ASoC: disable_route_checks set, ignoring dapm_add_routes errors\n");
} else {
dev_err(tplg->dev, "ASoC: dapm_add_routes failed: %d\n", ret);
break;
}
dev_info(tplg->dev,
"ASoC: disable_route_checks set, ignoring dapm_add_routes errors\n");
}
}

View File

@ -503,6 +503,12 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
int ret;
int i;
if (!w) {
dev_err(cpu_dai->dev, "%s widget not found, check amp link num in the topology\n",
cpu_dai->name);
return -EINVAL;
}
ops = hda_dai_get_ops(substream, cpu_dai);
if (!ops) {
dev_err(cpu_dai->dev, "DAI widget ops not set\n");
@ -582,6 +588,12 @@ int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
*/
for_each_rtd_cpu_dais(rtd, i, dai) {
w = snd_soc_dai_get_widget(dai, substream->stream);
if (!w) {
dev_err(cpu_dai->dev,
"%s widget not found, check amp link num in the topology\n",
dai->name);
return -EINVAL;
}
ipc4_copier = widget_to_copier(w);
memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv,
sizeof(*dma_config_tlv));

View File

@ -37,6 +37,11 @@ static bool hda_disable_rewinds;
module_param_named(disable_rewinds, hda_disable_rewinds, bool, 0444);
MODULE_PARM_DESC(disable_rewinds, "SOF HDA disable rewinds");
static int hda_force_pause_support = -1;
module_param_named(force_pause_support, hda_force_pause_support, int, 0444);
MODULE_PARM_DESC(force_pause_support,
"Pause support: -1: Use default, 0: Disable, 1: Enable (default -1)");
u32 hda_dsp_get_mult_div(struct snd_sof_dev *sdev, int rate)
{
switch (rate) {
@ -240,6 +245,16 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev,
if (hda_always_enable_dmi_l1 && direction == SNDRV_PCM_STREAM_CAPTURE)
runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE;
/*
* Do not advertise the PAUSE support if it is forced to be disabled via
* module parameter or if the pause_supported is false for the PCM
* device
*/
if (hda_force_pause_support == 0 ||
(hda_force_pause_support == -1 &&
!spcm->stream[substream->stream].pause_supported))
runtime->hw.info &= ~SNDRV_PCM_INFO_PAUSE;
if (hda_always_enable_dmi_l1 ||
direction == SNDRV_PCM_STREAM_PLAYBACK ||
spcm->stream[substream->stream].d0i3_compatible)

View File

@ -63,6 +63,11 @@ static int sdw_params_stream(struct device *dev,
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(d, params_data->substream->stream);
struct snd_sof_dai_config_data data = { 0 };
if (!w) {
dev_err(dev, "%s widget not found, check amp link num in the topology\n",
d->name);
return -EINVAL;
}
data.dai_index = (params_data->link_id << 8) | d->id;
data.dai_data = params_data->alh_stream_id;
data.dai_node_id = data.dai_data;

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