mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 00:32:00 +00:00
Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
This commit is contained in:
commit
dca381d2cc
@ -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:
|
||||
|
@ -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.
|
||||
|
156
Documentation/devicetree/bindings/sound/realtek,rt5682.yaml
Normal file
156
Documentation/devicetree/bindings/sound/realtek,rt5682.yaml
Normal file
@ -0,0 +1,156 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/sound/realtek,rt5682.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Realtek rt5682 and rt5682i codecs
|
||||
|
||||
maintainers:
|
||||
- Bard Liao <bardliao@realtek.com>
|
||||
|
||||
allOf:
|
||||
- $ref: dai-common.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- realtek,rt5682
|
||||
- realtek,rt5682i
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
description: I2C address of the device.
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
description: The CODEC's interrupt output.
|
||||
|
||||
realtek,dmic1-data-pin:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum:
|
||||
- 0 # dmic1 data is not used
|
||||
- 1 # using GPIO2 pin as dmic1 data pin
|
||||
- 2 # using GPIO5 pin as dmic1 data pin
|
||||
description:
|
||||
Specify which GPIO pin be used as DMIC1 data pin.
|
||||
|
||||
realtek,dmic1-clk-pin:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum:
|
||||
- 0 # using GPIO1 pin as dmic1 clock pin
|
||||
- 1 # using GPIO3 pin as dmic1 clock pin
|
||||
description:
|
||||
Specify which GPIO pin be used as DMIC1 clk pin.
|
||||
|
||||
realtek,jd-src:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum:
|
||||
- 0 # No JD is used
|
||||
- 1 # using JD1 as JD source
|
||||
description:
|
||||
Specify which JD source be used.
|
||||
|
||||
realtek,ldo1-en-gpios:
|
||||
description:
|
||||
The GPIO that controls the CODEC's LDO1_EN pin.
|
||||
|
||||
realtek,btndet-delay:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The debounce delay for push button.
|
||||
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
|
||||
If absent, the default is 16.
|
||||
|
||||
realtek,dmic-clk-rate-hz:
|
||||
description:
|
||||
Set the clock rate (hz) for the requirement of the particular DMIC.
|
||||
|
||||
realtek,dmic-delay-ms:
|
||||
description:
|
||||
Set the delay time (ms) for the requirement of the particular DMIC.
|
||||
|
||||
realtek,dmic-clk-driving-high:
|
||||
type: boolean
|
||||
description:
|
||||
Set the high driving of the DMIC clock out.
|
||||
|
||||
clocks:
|
||||
items:
|
||||
- description: phandle and clock specifier for codec MCLK.
|
||||
|
||||
clock-names:
|
||||
items:
|
||||
- const: mclk
|
||||
|
||||
"#clock-cells":
|
||||
const: 1
|
||||
|
||||
clock-output-names:
|
||||
minItems: 2
|
||||
maxItems: 2
|
||||
description: Name given for DAI word clock and bit clock outputs.
|
||||
|
||||
"#sound-dai-cells":
|
||||
const: 1
|
||||
|
||||
AVDD-supply:
|
||||
description: Regulator supplying analog power through the AVDD pin.
|
||||
|
||||
MICVDD-supply:
|
||||
description: Regulator supplying power for the microphone bias through
|
||||
the MICVDD pin.
|
||||
|
||||
VBAT-supply:
|
||||
description: Regulator supplying battery power through the VBAT pin.
|
||||
|
||||
DBVDD-supply:
|
||||
description: Regulator supplying I/O power through the DBVDD pin.
|
||||
|
||||
LDO1-IN-supply:
|
||||
description: Regulator supplying power to the digital core and charge
|
||||
pump through the LDO1_IN pin.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- AVDD-supply
|
||||
- VBAT-supply
|
||||
- MICVDD-supply
|
||||
- DBVDD-supply
|
||||
- LDO1-IN-supply
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
codec@1a {
|
||||
compatible = "realtek,rt5682";
|
||||
reg = <0x1a>;
|
||||
interrupts = <6 IRQ_TYPE_LEVEL_HIGH>;
|
||||
realtek,ldo1-en-gpios =
|
||||
<&gpio 2 GPIO_ACTIVE_HIGH>;
|
||||
realtek,dmic1-data-pin = <1>;
|
||||
realtek,dmic1-clk-pin = <1>;
|
||||
realtek,jd-src = <1>;
|
||||
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk";
|
||||
|
||||
clocks = <&osc>;
|
||||
clock-names = "mclk";
|
||||
|
||||
AVDD-supply = <&avdd_reg>;
|
||||
VBAT-supply = <&vbat_reg>;
|
||||
MICVDD-supply = <&micvdd_reg>;
|
||||
DBVDD-supply = <&dbvdd_reg>;
|
||||
LDO1-IN-supply = <&ldo1_in_reg>;
|
||||
};
|
||||
};
|
@ -19,6 +19,7 @@ properties:
|
||||
- renesas,r9a07g043-ssi # RZ/G2UL and RZ/Five
|
||||
- renesas,r9a07g044-ssi # RZ/G2{L,LC}
|
||||
- renesas,r9a07g054-ssi # RZ/V2L
|
||||
- renesas,r9a08g045-ssi # RZ/G3S
|
||||
- const: renesas,rz-ssi
|
||||
|
||||
reg:
|
||||
@ -57,24 +58,6 @@ properties:
|
||||
dmas:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
The first cell represents a phandle to dmac.
|
||||
The second cell specifies the encoded MID/RID values of the SSI port
|
||||
connected to the DMA client and the slave channel configuration
|
||||
parameters.
|
||||
bits[0:9] - Specifies MID/RID value of a SSI channel as below
|
||||
MID/RID value of SSI rx0 = 0x256
|
||||
MID/RID value of SSI tx0 = 0x255
|
||||
MID/RID value of SSI rx1 = 0x25a
|
||||
MID/RID value of SSI tx1 = 0x259
|
||||
MID/RID value of SSI rt2 = 0x25f
|
||||
MID/RID value of SSI rx3 = 0x262
|
||||
MID/RID value of SSI tx3 = 0x261
|
||||
bit[10] - HIEN = 1, Detects a request in response to the rising edge
|
||||
of the signal
|
||||
bit[11] - LVL = 0, Detects based on the edge
|
||||
bits[12:14] - AM = 2, Bus cycle mode
|
||||
bit[15] - TM = 0, Single transfer mode
|
||||
|
||||
dma-names:
|
||||
oneOf:
|
||||
|
@ -1,98 +0,0 @@
|
||||
RT5682 audio CODEC
|
||||
|
||||
This device supports I2C only.
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible : "realtek,rt5682" or "realtek,rt5682i"
|
||||
|
||||
- reg : The I2C address of the device.
|
||||
|
||||
- AVDD-supply: phandle to the regulator supplying analog power through the
|
||||
AVDD pin
|
||||
|
||||
- MICVDD-supply: phandle to the regulator supplying power for the microphone
|
||||
bias through the MICVDD pin. Either MICVDD or VBAT should be present.
|
||||
|
||||
- VBAT-supply: phandle to the regulator supplying battery power through the
|
||||
VBAT pin. Either MICVDD or VBAT should be present.
|
||||
|
||||
- DBVDD-supply: phandle to the regulator supplying I/O power through the DBVDD
|
||||
pin.
|
||||
|
||||
- LDO1-IN-supply: phandle to the regulator supplying power to the digital core
|
||||
and charge pump through the LDO1_IN pin.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- interrupts : The CODEC's interrupt output.
|
||||
|
||||
- realtek,dmic1-data-pin
|
||||
0: dmic1 is not used
|
||||
1: using GPIO2 pin as dmic1 data pin
|
||||
2: using GPIO5 pin as dmic1 data pin
|
||||
|
||||
- realtek,dmic1-clk-pin
|
||||
0: using GPIO1 pin as dmic1 clock pin
|
||||
1: using GPIO3 pin as dmic1 clock pin
|
||||
|
||||
- realtek,jd-src
|
||||
0: No JD is used
|
||||
1: using JD1 as JD source
|
||||
|
||||
- realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
|
||||
|
||||
- realtek,btndet-delay
|
||||
The debounce delay for push button.
|
||||
The delay time is realtek,btndet-delay value multiple of 8.192 ms.
|
||||
If absent, the default is 16.
|
||||
|
||||
- #clock-cells : Should be set to '<1>', wclk and bclk sources provided.
|
||||
- clock-output-names : Name given for DAI clocks output.
|
||||
|
||||
- clocks : phandle and clock specifier for codec MCLK.
|
||||
- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
|
||||
|
||||
- realtek,dmic-clk-rate-hz : Set the clock rate (hz) for the requirement of
|
||||
the particular DMIC.
|
||||
|
||||
- realtek,dmic-delay-ms : Set the delay time (ms) for the requirement of
|
||||
the particular DMIC.
|
||||
|
||||
- realtek,dmic-clk-driving-high : Set the high driving of the DMIC clock out.
|
||||
|
||||
- #sound-dai-cells: Should be set to '<1>'.
|
||||
|
||||
Pins on the device (for linking into audio routes) for RT5682:
|
||||
|
||||
* DMIC L1
|
||||
* DMIC R1
|
||||
* IN1P
|
||||
* HPOL
|
||||
* HPOR
|
||||
|
||||
Example:
|
||||
|
||||
rt5682 {
|
||||
compatible = "realtek,rt5682i";
|
||||
reg = <0x1a>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <TEGRA_GPIO(U, 6) IRQ_TYPE_LEVEL_HIGH>;
|
||||
realtek,ldo1-en-gpios =
|
||||
<&gpio TEGRA_GPIO(R, 2) GPIO_ACTIVE_HIGH>;
|
||||
realtek,dmic1-data-pin = <1>;
|
||||
realtek,dmic1-clk-pin = <1>;
|
||||
realtek,jd-src = <1>;
|
||||
realtek,btndet-delay = <16>;
|
||||
|
||||
#clock-cells = <1>;
|
||||
clock-output-names = "rt5682-dai-wclk", "rt5682-dai-bclk";
|
||||
|
||||
clocks = <&osc>;
|
||||
clock-names = "mclk";
|
||||
|
||||
AVDD-supply = <&avdd_reg>;
|
||||
MICVDD-supply = <&micvdd_reg>;
|
||||
DBVDD-supply = <&dbvdd_reg>;
|
||||
LDO1-IN-supply = <&ldo1_in_reg>;
|
||||
};
|
@ -5529,8 +5529,8 @@ L: patches@opensource.cirrus.com
|
||||
S: Supported
|
||||
W: https://github.com/CirrusLogic/linux-drivers/wiki
|
||||
T: git https://github.com/CirrusLogic/linux-drivers.git
|
||||
F: drivers/firmware/cirrus/*
|
||||
F: include/linux/firmware/cirrus/*
|
||||
F: drivers/firmware/cirrus/
|
||||
F: include/linux/firmware/cirrus/
|
||||
|
||||
CIRRUS LOGIC EP93XX ETHERNET DRIVER
|
||||
M: Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
|
@ -3,3 +3,23 @@
|
||||
config FW_CS_DSP
|
||||
tristate
|
||||
default n
|
||||
|
||||
config FW_CS_DSP_KUNIT_TEST_UTILS
|
||||
tristate
|
||||
depends on KUNIT
|
||||
select REGMAP
|
||||
select FW_CS_DSP
|
||||
|
||||
config FW_CS_DSP_KUNIT_TEST
|
||||
tristate "KUnit tests for Cirrus Logic cs_dsp" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
select REGMAP
|
||||
select FW_CS_DSP
|
||||
select FW_CS_DSP_KUNIT_TEST_UTILS
|
||||
help
|
||||
This builds KUnit tests for cs_dsp.
|
||||
For more information on KUnit and unit tests in general,
|
||||
please refer to the KUnit documentation in
|
||||
Documentation/dev-tools/kunit/.
|
||||
If in doubt, say "N".
|
||||
|
@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
obj-$(CONFIG_FW_CS_DSP) += cs_dsp.o
|
||||
|
||||
obj-y += test/
|
||||
|
23
drivers/firmware/cirrus/test/Makefile
Normal file
23
drivers/firmware/cirrus/test/Makefile
Normal file
@ -0,0 +1,23 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
|
||||
cs_dsp_test_utils-objs := \
|
||||
cs_dsp_mock_mem_maps.o \
|
||||
cs_dsp_mock_bin.o \
|
||||
cs_dsp_mock_regmap.o \
|
||||
cs_dsp_mock_utils.o \
|
||||
cs_dsp_mock_wmfw.o
|
||||
|
||||
cs_dsp_test-objs := \
|
||||
cs_dsp_test_bin.o \
|
||||
cs_dsp_test_bin_error.o \
|
||||
cs_dsp_test_callbacks.o \
|
||||
cs_dsp_test_control_parse.o \
|
||||
cs_dsp_test_control_cache.o \
|
||||
cs_dsp_test_control_rw.o \
|
||||
cs_dsp_test_wmfw.o \
|
||||
cs_dsp_test_wmfw_error.o \
|
||||
cs_dsp_tests.o
|
||||
|
||||
obj-$(CONFIG_FW_CS_DSP_KUNIT_TEST_UTILS) += cs_dsp_test_utils.o
|
||||
obj-$(CONFIG_FW_CS_DSP_KUNIT_TEST) += cs_dsp_test.o
|
199
drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
Normal file
199
drivers/firmware/cirrus/test/cs_dsp_mock_bin.c
Normal file
@ -0,0 +1,199 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// bin file builder for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/* Buffer large enough for bin file content */
|
||||
#define CS_DSP_MOCK_BIN_BUF_SIZE 32768
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
|
||||
|
||||
struct cs_dsp_mock_bin_builder {
|
||||
struct cs_dsp_test *test_priv;
|
||||
void *buf;
|
||||
void *write_p;
|
||||
size_t bytes_used;
|
||||
};
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_get_firmware() - Get struct firmware wrapper for data.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
*
|
||||
* Return: Pointer to a struct firmware wrapper for the data.
|
||||
*/
|
||||
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder)
|
||||
{
|
||||
struct firmware *fw;
|
||||
|
||||
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
|
||||
|
||||
fw->data = builder->buf;
|
||||
fw->size = builder->bytes_used;
|
||||
|
||||
return fw;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_raw_block() - Add a data block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @alg_id: Algorithm ID.
|
||||
* @alg_ver: Algorithm version.
|
||||
* @type: Type of the block.
|
||||
* @offset: Offset.
|
||||
* @payload_data: Pointer to buffer containing the payload data.
|
||||
* @payload_len_bytes: Length of payload data in bytes.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int type, unsigned int offset,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
struct wmfw_coeff_item *item;
|
||||
size_t bytes_needed = struct_size_t(struct wmfw_coeff_item, data, payload_len_bytes);
|
||||
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_BIN_BUF_SIZE));
|
||||
|
||||
item = builder->write_p;
|
||||
|
||||
item->offset = cpu_to_le16(offset);
|
||||
item->type = cpu_to_le16(type);
|
||||
item->id = cpu_to_le32(alg_id);
|
||||
item->ver = cpu_to_le32(alg_ver << 8);
|
||||
item->len = cpu_to_le32(payload_len_bytes);
|
||||
|
||||
if (payload_len_bytes)
|
||||
memcpy(item->data, payload_data, payload_len_bytes);
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static void cs_dsp_mock_bin_add_name_or_info(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *info, int type)
|
||||
{
|
||||
size_t info_len = strlen(info);
|
||||
char *tmp = NULL;
|
||||
|
||||
if (info_len % 4) {
|
||||
/* Create a padded string with length a multiple of 4 */
|
||||
info_len = round_up(info_len, 4);
|
||||
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
|
||||
memcpy(tmp, info, info_len);
|
||||
info = tmp;
|
||||
}
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(builder, 0, 0, WMFW_INFO_TEXT, 0, info, info_len);
|
||||
kunit_kfree(builder->test_priv->test, tmp);
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_info() - Add an info block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @info: Pointer to info string to be copied into the file.
|
||||
*
|
||||
* The string will be padded to a length that is a multiple of 4 bytes.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *info)
|
||||
{
|
||||
cs_dsp_mock_bin_add_name_or_info(builder, info, WMFW_INFO_TEXT);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_name() - Add a name block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @name: Pointer to name string to be copied into the file.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *name)
|
||||
{
|
||||
cs_dsp_mock_bin_add_name_or_info(builder, name, WMFW_NAME_TEXT);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_name, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_add_patch() - Add a patch data block to the bin file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @alg_id: Algorithm ID for the patch.
|
||||
* @alg_ver: Algorithm version for the patch.
|
||||
* @mem_region: Memory region for the patch.
|
||||
* @reg_addr_offset: Offset to start of data in register addresses.
|
||||
* @payload_data: Pointer to buffer containing the payload data.
|
||||
* @payload_len_bytes: Length of payload data in bytes.
|
||||
*/
|
||||
void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int mem_region, unsigned int reg_addr_offset,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
/* Payload length must be a multiple of 4 */
|
||||
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(builder, alg_id, alg_ver,
|
||||
mem_region, reg_addr_offset,
|
||||
payload_data, payload_len_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_add_patch, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_bin_init() - Initialize a struct cs_dsp_mock_bin_builder.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @format_version: Required bin format version.
|
||||
* @fw_version: Firmware version to put in bin file.
|
||||
*
|
||||
* Return: Pointer to created struct cs_dsp_mock_bin_builder.
|
||||
*/
|
||||
struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
|
||||
int format_version,
|
||||
unsigned int fw_version)
|
||||
{
|
||||
struct cs_dsp_mock_bin_builder *builder;
|
||||
struct wmfw_coeff_hdr *hdr;
|
||||
|
||||
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
|
||||
builder->test_priv = priv;
|
||||
|
||||
builder->buf = vmalloc(CS_DSP_MOCK_BIN_BUF_SIZE);
|
||||
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
|
||||
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
|
||||
|
||||
/* Create header */
|
||||
hdr = builder->buf;
|
||||
memcpy(hdr->magic, "WMDR", sizeof(hdr->magic));
|
||||
hdr->len = cpu_to_le32(offsetof(struct wmfw_coeff_hdr, data));
|
||||
hdr->ver = cpu_to_le32(fw_version | (format_version << 24));
|
||||
hdr->core_ver = cpu_to_le32(((u32)priv->dsp->type << 24) | priv->dsp->rev);
|
||||
|
||||
builder->write_p = hdr->data;
|
||||
builder->bytes_used = offsetof(struct wmfw_coeff_hdr, data);
|
||||
|
||||
return builder;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_bin_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
752
drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
Normal file
752
drivers/firmware/cirrus/test/cs_dsp_mock_mem_maps.c
Normal file
@ -0,0 +1,752 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Mock DSP memory maps for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/math.h>
|
||||
|
||||
const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[] = {
|
||||
{ .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
|
||||
{ .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
|
||||
{ .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
|
||||
{ .type = WMFW_ADSP2_XM, .base = 0x2800000 },
|
||||
{ .type = WMFW_ADSP2_YM, .base = 0x3400000 },
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/* List of sizes in bytes, for each entry above */
|
||||
const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[] = {
|
||||
0x5000, /* PM_PACKED */
|
||||
0x6000, /* XM_PACKED */
|
||||
0x47F4, /* YM_PACKED */
|
||||
0x8000, /* XM_UNPACKED_24 */
|
||||
0x5FF8, /* YM_UNPACKED_24 */
|
||||
|
||||
0 /* terminator */
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[] = {
|
||||
{ .type = WMFW_ADSP2_PM, .base = 0x080000 },
|
||||
{ .type = WMFW_ADSP2_XM, .base = 0x0a0000 },
|
||||
{ .type = WMFW_ADSP2_YM, .base = 0x0c0000 },
|
||||
{ .type = WMFW_ADSP2_ZM, .base = 0x0e0000 },
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/* List of sizes in bytes, for each entry above */
|
||||
const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[] = {
|
||||
0x9000, /* PM */
|
||||
0xa000, /* ZM */
|
||||
0x2000, /* XM */
|
||||
0x2000, /* YM */
|
||||
|
||||
0 /* terminator */
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[] = {
|
||||
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
|
||||
{ .type = WMFW_ADSP2_ZM, .base = 0x180000 },
|
||||
{ .type = WMFW_ADSP2_XM, .base = 0x190000 },
|
||||
{ .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/* List of sizes in bytes, for each entry above */
|
||||
const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[] = {
|
||||
0x6000, /* PM */
|
||||
0x800, /* ZM */
|
||||
0x800, /* XM */
|
||||
0x800, /* YM */
|
||||
|
||||
0 /* terminator */
|
||||
};
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
int cs_dsp_mock_count_regions(const unsigned int *region_sizes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; region_sizes[i]; ++i)
|
||||
;
|
||||
|
||||
return i;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_count_regions, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_size_of_region() - Return size of given memory region.
|
||||
*
|
||||
* @dsp: Pointer to struct cs_dsp.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Size of region in bytes.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type)
|
||||
{
|
||||
const unsigned int *sizes;
|
||||
int i;
|
||||
|
||||
if (dsp->mem == cs_dsp_mock_halo_dsp1_regions)
|
||||
sizes = cs_dsp_mock_halo_dsp1_region_sizes;
|
||||
else if (dsp->mem == cs_dsp_mock_adsp2_32bit_dsp1_regions)
|
||||
sizes = cs_dsp_mock_adsp2_32bit_dsp1_region_sizes;
|
||||
else if (dsp->mem == cs_dsp_mock_adsp2_16bit_dsp1_regions)
|
||||
sizes = cs_dsp_mock_adsp2_16bit_dsp1_region_sizes;
|
||||
else
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < dsp->num_mems; ++i) {
|
||||
if (dsp->mem[i].type == mem_type)
|
||||
return sizes[i];
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_size_of_region, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_base_addr_for_mem() - Base register address for memory region.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Base register address of region.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
int num_mems = priv->dsp->num_mems;
|
||||
const struct cs_dsp_region *region = priv->dsp->mem;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num_mems; ++i) {
|
||||
if (region[i].type == mem_type)
|
||||
return region[i].base;
|
||||
}
|
||||
|
||||
KUNIT_FAIL(priv->test, "Unexpected region %d\n", mem_type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_base_addr_for_mem, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_addr_inc_per_unpacked_word() - Unpacked register address increment per DSP word.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*
|
||||
* Return: Amount by which register address increments to move to the next
|
||||
* DSP word in unpacked XM/YM/ZM.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
return 2; /* two 16-bit register indexes per XM/YM/ZM word */
|
||||
case WMFW_HALO:
|
||||
return 4; /* one byte-addressed 32-bit register per XM/YM/ZM word */
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_addr_inc_per_unpacked_word, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_block_length_bytes() - Number of bytes in an access block.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Total number of bytes in a group of registers forming the
|
||||
* smallest bus access size (including any padding bits). For unpacked
|
||||
* memory this is the number of registers containing one DSP word.
|
||||
* For packed memory this is the number of registers in one packed
|
||||
* access block.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_PM:
|
||||
return 3 * regmap_get_val_bytes(priv->dsp->regmap);
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
case WMFW_ADSP2_ZM:
|
||||
return sizeof(u32);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
return sizeof(u32);
|
||||
case WMFW_HALO_PM_PACKED:
|
||||
return 5 * sizeof(u32);
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return 3 * sizeof(u32);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
KUNIT_FAIL(priv->test, "Unexpected mem type\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_block_length_registers() - Number of registers in an access block.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Total number of register forming the smallest bus access size.
|
||||
* For unpacked memory this is the number of registers containing one
|
||||
* DSP word. For packed memory this is the number of registers in one
|
||||
* packed access block.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
return cs_dsp_mock_reg_block_length_bytes(priv, mem_type) /
|
||||
regmap_get_val_bytes(priv->dsp->regmap);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_registers, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_reg_block_length_dsp_words() - Number of dsp_words in an access block.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Return: Total number of DSP words in a group of registers forming the
|
||||
* smallest bus access size.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_PM:
|
||||
return regmap_get_val_bytes(priv->dsp->regmap) / 2;
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
case WMFW_ADSP2_ZM:
|
||||
return 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_ADSP2_YM:
|
||||
return 1;
|
||||
case WMFW_HALO_PM_PACKED:
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return 4;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
KUNIT_FAIL(priv->test, "Unexpected mem type\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_reg_block_length_dsp_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_has_zm() - DSP has ZM
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*
|
||||
* Return: True if DSP has ZM.
|
||||
*/
|
||||
bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_has_zm, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_packed_to_unpacked_mem_type() - Unpacked region that is
|
||||
* the same memory as a packed region.
|
||||
*
|
||||
* @packed_mem_type: Type of packed memory region.
|
||||
*
|
||||
* Return: unpacked type that is the same memory as packed_mem_type.
|
||||
*/
|
||||
int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type)
|
||||
{
|
||||
switch (packed_mem_type) {
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
return WMFW_ADSP2_XM;
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return WMFW_ADSP2_YM;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_packed_to_unpacked_mem_type, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_num_dsp_words_to_num_packed_regs() - Number of DSP words
|
||||
* to number of packed registers.
|
||||
*
|
||||
* @num_dsp_words: Number of DSP words.
|
||||
*
|
||||
* Convert number of DSP words to number of packed registers rounded
|
||||
* down to the nearest register.
|
||||
*
|
||||
* Return: Number of packed registers.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words)
|
||||
{
|
||||
/* There are 3 registers for every 4 packed words */
|
||||
return (num_dsp_words * 3) / 4;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_num_dsp_words_to_num_packed_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct wmfw_halo_id_hdr cs_dsp_mock_halo_xm_hdr = {
|
||||
.fw = {
|
||||
.core_id = cpu_to_be32(WMFW_HALO << 16),
|
||||
.block_rev = cpu_to_be32(3 << 16),
|
||||
.vendor_id = cpu_to_be32(0x2),
|
||||
.id = cpu_to_be32(0xabcdef),
|
||||
.ver = cpu_to_be32(0x090101),
|
||||
},
|
||||
|
||||
/*
|
||||
* Leave enough space for this header and 40 algorithm descriptors.
|
||||
* base and size are counted in DSP words.
|
||||
*/
|
||||
.xm_base = cpu_to_be32(((sizeof(struct wmfw_halo_id_hdr) +
|
||||
(40 * sizeof(struct wmfw_halo_alg_hdr)))
|
||||
/ 4) * 3),
|
||||
.xm_size = cpu_to_be32(0x20),
|
||||
|
||||
/* Allocate a dummy word of YM */
|
||||
.ym_base = cpu_to_be32(0),
|
||||
.ym_size = cpu_to_be32(1),
|
||||
|
||||
.n_algs = 0,
|
||||
};
|
||||
|
||||
static const struct wmfw_adsp2_id_hdr cs_dsp_mock_adsp2_xm_hdr = {
|
||||
.fw = {
|
||||
.core_id = cpu_to_be32(WMFW_ADSP2 << 16),
|
||||
.core_rev = cpu_to_be32(2 << 16),
|
||||
.id = cpu_to_be32(0xabcdef),
|
||||
.ver = cpu_to_be32(0x090101),
|
||||
},
|
||||
|
||||
/*
|
||||
* Leave enough space for this header and 40 algorithm descriptors.
|
||||
* base and size are counted in DSP words.
|
||||
*/
|
||||
.xm = cpu_to_be32(((sizeof(struct wmfw_adsp2_id_hdr) +
|
||||
(40 * sizeof(struct wmfw_adsp2_alg_hdr)))
|
||||
/ 4) * 3),
|
||||
|
||||
.ym = cpu_to_be32(0),
|
||||
.zm = cpu_to_be32(0),
|
||||
|
||||
.n_algs = 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_get_alg_base_in_words() - Algorithm base offset in DSP words.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @alg_id: Algorithm ID.
|
||||
* @mem_type: Memory region type.
|
||||
*
|
||||
* Lookup an algorithm in the XM header and return the base offset in
|
||||
* DSP words of the algorithm data in the requested memory region.
|
||||
*
|
||||
* Return: Offset in DSP words.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
|
||||
unsigned int alg_id,
|
||||
int mem_type)
|
||||
{
|
||||
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
union {
|
||||
struct wmfw_adsp2_alg_hdr adsp2;
|
||||
struct wmfw_halo_alg_hdr halo;
|
||||
} alg;
|
||||
unsigned int alg_hdr_addr;
|
||||
unsigned int val, xm_base = 0, ym_base = 0, zm_base = 0;
|
||||
int ret;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
alg_hdr_addr = xm + (sizeof(struct wmfw_adsp2_id_hdr) / 2);
|
||||
for (;; alg_hdr_addr += sizeof(alg.adsp2) / 2) {
|
||||
ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
|
||||
ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
|
||||
&alg.adsp2, sizeof(alg.adsp2));
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
if (be32_to_cpu(alg.adsp2.alg.id) == alg_id) {
|
||||
xm_base = be32_to_cpu(alg.adsp2.xm);
|
||||
ym_base = be32_to_cpu(alg.adsp2.ym);
|
||||
zm_base = be32_to_cpu(alg.adsp2.zm);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
alg_hdr_addr = xm + sizeof(struct wmfw_halo_id_hdr);
|
||||
for (;; alg_hdr_addr += sizeof(alg.halo)) {
|
||||
ret = regmap_read(priv->dsp->regmap, alg_hdr_addr, &val);
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
KUNIT_ASSERT_NE(priv->test, val, 0xbedead);
|
||||
ret = regmap_raw_read(priv->dsp->regmap, alg_hdr_addr,
|
||||
&alg.halo, sizeof(alg.halo));
|
||||
KUNIT_ASSERT_GE(priv->test, ret, 0);
|
||||
if (be32_to_cpu(alg.halo.alg.id) == alg_id) {
|
||||
xm_base = be32_to_cpu(alg.halo.xm_base);
|
||||
ym_base = be32_to_cpu(alg.halo.ym_base);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Unexpected DSP type %d\n", priv->dsp->type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (mem_type) {
|
||||
case WMFW_ADSP2_XM:
|
||||
case WMFW_HALO_XM_PACKED:
|
||||
return xm_base;
|
||||
case WMFW_ADSP2_YM:
|
||||
case WMFW_HALO_YM_PACKED:
|
||||
return ym_base;
|
||||
case WMFW_ADSP2_ZM:
|
||||
return zm_base;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "Bad mem_type\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_alg_base_in_words, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_get_fw_version_from_regmap() - Firmware version.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*
|
||||
* Return: Firmware version word value.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv)
|
||||
{
|
||||
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
union {
|
||||
struct wmfw_id_hdr adsp2;
|
||||
struct wmfw_v3_id_hdr halo;
|
||||
} hdr;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
regmap_raw_read(priv->dsp->regmap, xm, &hdr.adsp2, sizeof(hdr.adsp2));
|
||||
return be32_to_cpu(hdr.adsp2.ver);
|
||||
case WMFW_HALO:
|
||||
regmap_raw_read(priv->dsp->regmap, xm, &hdr.halo, sizeof(hdr.halo));
|
||||
return be32_to_cpu(hdr.halo.ver);
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version_from_regmap,
|
||||
"FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_get_fw_version() - Firmware version.
|
||||
*
|
||||
* @header: Pointer to struct cs_dsp_mock_xm_header.
|
||||
*
|
||||
* Return: Firmware version word value.
|
||||
*/
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header)
|
||||
{
|
||||
const struct wmfw_id_hdr *adsp2_hdr;
|
||||
const struct wmfw_v3_id_hdr *halo_hdr;
|
||||
|
||||
switch (header->test_priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
adsp2_hdr = header->blob_data;
|
||||
return be32_to_cpu(adsp2_hdr->ver);
|
||||
case WMFW_HALO:
|
||||
halo_hdr = header->blob_data;
|
||||
return be32_to_cpu(halo_hdr->ver);
|
||||
default:
|
||||
KUNIT_FAIL(header->test_priv->test, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_get_fw_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_drop_from_regmap_cache() - Drop XM header from regmap cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
*/
|
||||
void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv)
|
||||
{
|
||||
unsigned int xm = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
unsigned int bytes;
|
||||
__be32 num_algs_be32;
|
||||
unsigned int num_algs;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
/*
|
||||
* Could be one 32-bit register or two 16-bit registers.
|
||||
* A raw read will read the requested number of bytes.
|
||||
*/
|
||||
regmap_raw_read(priv->dsp->regmap,
|
||||
xm + (offsetof(struct wmfw_adsp2_id_hdr, n_algs) / 2),
|
||||
&num_algs_be32, sizeof(num_algs_be32));
|
||||
num_algs = be32_to_cpu(num_algs_be32);
|
||||
bytes = sizeof(struct wmfw_adsp2_id_hdr) +
|
||||
(num_algs * sizeof(struct wmfw_adsp2_alg_hdr)) +
|
||||
4 /* terminator word */;
|
||||
|
||||
regcache_drop_region(priv->dsp->regmap, xm, xm + (bytes / 2) - 1);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
regmap_read(priv->dsp->regmap,
|
||||
xm + offsetof(struct wmfw_halo_id_hdr, n_algs),
|
||||
&num_algs);
|
||||
bytes = sizeof(struct wmfw_halo_id_hdr) +
|
||||
(num_algs * sizeof(struct wmfw_halo_alg_hdr)) +
|
||||
4 /* terminator word */;
|
||||
|
||||
regcache_drop_region(priv->dsp->regmap, xm, xm + bytes - 4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_drop_from_regmap_cache, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static void cs_dsp_mock_xm_header_add_adsp2_algs(struct cs_dsp_mock_xm_header *builder,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs)
|
||||
{
|
||||
struct wmfw_adsp2_id_hdr *hdr = builder->blob_data;
|
||||
unsigned int next_free_xm_word, next_free_ym_word, next_free_zm_word;
|
||||
|
||||
next_free_xm_word = be32_to_cpu(hdr->xm);
|
||||
next_free_ym_word = be32_to_cpu(hdr->ym);
|
||||
next_free_zm_word = be32_to_cpu(hdr->zm);
|
||||
|
||||
/* Set num_algs in XM header. */
|
||||
hdr->n_algs = cpu_to_be32(num_algs);
|
||||
|
||||
/* Create algorithm descriptor list */
|
||||
struct wmfw_adsp2_alg_hdr *alg_info =
|
||||
(struct wmfw_adsp2_alg_hdr *)(&hdr[1]);
|
||||
|
||||
for (; num_algs > 0; num_algs--, algs++, alg_info++) {
|
||||
unsigned int alg_xm_last, alg_ym_last, alg_zm_last;
|
||||
|
||||
alg_info->alg.id = cpu_to_be32(algs->id);
|
||||
alg_info->alg.ver = cpu_to_be32(algs->ver);
|
||||
alg_info->xm = cpu_to_be32(algs->xm_base_words);
|
||||
alg_info->ym = cpu_to_be32(algs->ym_base_words);
|
||||
alg_info->zm = cpu_to_be32(algs->zm_base_words);
|
||||
|
||||
/* Check if we need to auto-allocate base addresses */
|
||||
if (!alg_info->xm && algs->xm_size_words)
|
||||
alg_info->xm = cpu_to_be32(next_free_xm_word);
|
||||
|
||||
if (!alg_info->ym && algs->ym_size_words)
|
||||
alg_info->ym = cpu_to_be32(next_free_ym_word);
|
||||
|
||||
if (!alg_info->zm && algs->zm_size_words)
|
||||
alg_info->zm = cpu_to_be32(next_free_zm_word);
|
||||
|
||||
alg_xm_last = be32_to_cpu(alg_info->xm) + algs->xm_size_words - 1;
|
||||
if (alg_xm_last > next_free_xm_word)
|
||||
next_free_xm_word = alg_xm_last;
|
||||
|
||||
alg_ym_last = be32_to_cpu(alg_info->ym) + algs->ym_size_words - 1;
|
||||
if (alg_ym_last > next_free_ym_word)
|
||||
next_free_ym_word = alg_ym_last;
|
||||
|
||||
alg_zm_last = be32_to_cpu(alg_info->zm) + algs->zm_size_words - 1;
|
||||
if (alg_zm_last > next_free_zm_word)
|
||||
next_free_zm_word = alg_zm_last;
|
||||
}
|
||||
|
||||
/* Write list terminator */
|
||||
*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
|
||||
}
|
||||
|
||||
static void cs_dsp_mock_xm_header_add_halo_algs(struct cs_dsp_mock_xm_header *builder,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs)
|
||||
{
|
||||
struct wmfw_halo_id_hdr *hdr = builder->blob_data;
|
||||
unsigned int next_free_xm_word, next_free_ym_word;
|
||||
|
||||
/* Assume we're starting with bare header */
|
||||
next_free_xm_word = be32_to_cpu(hdr->xm_base) + be32_to_cpu(hdr->xm_size) - 1;
|
||||
next_free_ym_word = be32_to_cpu(hdr->ym_base) + be32_to_cpu(hdr->ym_size) - 1;
|
||||
|
||||
/* Set num_algs in XM header */
|
||||
hdr->n_algs = cpu_to_be32(num_algs);
|
||||
|
||||
/* Create algorithm descriptor list */
|
||||
struct wmfw_halo_alg_hdr *alg_info =
|
||||
(struct wmfw_halo_alg_hdr *)(&hdr[1]);
|
||||
|
||||
for (; num_algs > 0; num_algs--, algs++, alg_info++) {
|
||||
unsigned int alg_xm_last, alg_ym_last;
|
||||
|
||||
alg_info->alg.id = cpu_to_be32(algs->id);
|
||||
alg_info->alg.ver = cpu_to_be32(algs->ver);
|
||||
alg_info->xm_base = cpu_to_be32(algs->xm_base_words);
|
||||
alg_info->xm_size = cpu_to_be32(algs->xm_size_words);
|
||||
alg_info->ym_base = cpu_to_be32(algs->ym_base_words);
|
||||
alg_info->ym_size = cpu_to_be32(algs->ym_size_words);
|
||||
|
||||
/* Check if we need to auto-allocate base addresses */
|
||||
if (!alg_info->xm_base && alg_info->xm_size)
|
||||
alg_info->xm_base = cpu_to_be32(next_free_xm_word);
|
||||
|
||||
if (!alg_info->ym_base && alg_info->ym_size)
|
||||
alg_info->ym_base = cpu_to_be32(next_free_ym_word);
|
||||
|
||||
alg_xm_last = be32_to_cpu(alg_info->xm_base) + be32_to_cpu(alg_info->xm_size) - 1;
|
||||
if (alg_xm_last > next_free_xm_word)
|
||||
next_free_xm_word = alg_xm_last;
|
||||
|
||||
alg_ym_last = be32_to_cpu(alg_info->ym_base) + be32_to_cpu(alg_info->ym_size) - 1;
|
||||
if (alg_ym_last > next_free_ym_word)
|
||||
next_free_ym_word = alg_ym_last;
|
||||
}
|
||||
|
||||
/* Write list terminator */
|
||||
*(__be32 *)(alg_info) = cpu_to_be32(0xbedead);
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_xm_header_write_to_regmap() - Write XM header to regmap.
|
||||
*
|
||||
* @header: Pointer to struct cs_dsp_mock_xm_header.
|
||||
*
|
||||
* The data in header is written to the XM addresses in the regmap.
|
||||
*
|
||||
* Return: 0 on success, else negative error code.
|
||||
*/
|
||||
int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header)
|
||||
{
|
||||
struct cs_dsp_test *priv = header->test_priv;
|
||||
unsigned int reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_XM);
|
||||
|
||||
/*
|
||||
* One 32-bit word corresponds to one 32-bit unpacked XM word so the
|
||||
* blob can be written directly to the regmap.
|
||||
*/
|
||||
return regmap_raw_write(priv->dsp->regmap, reg_addr,
|
||||
header->blob_data, header->blob_size_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_xm_header_write_to_regmap, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_create_mock_xm_header() - Create a dummy XM header.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @algs: Pointer to array of struct cs_dsp_mock_alg_def listing the
|
||||
* dummy algorithm entries to include in the XM header.
|
||||
* @num_algs: Number of entries in the algs array.
|
||||
*
|
||||
* Return: Pointer to created struct cs_dsp_mock_xm_header.
|
||||
*/
|
||||
struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs)
|
||||
{
|
||||
struct cs_dsp_mock_xm_header *builder;
|
||||
size_t total_bytes_required;
|
||||
const void *header;
|
||||
size_t header_size_bytes;
|
||||
|
||||
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
|
||||
builder->test_priv = priv;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
header = &cs_dsp_mock_adsp2_xm_hdr;
|
||||
header_size_bytes = sizeof(cs_dsp_mock_adsp2_xm_hdr);
|
||||
total_bytes_required = header_size_bytes +
|
||||
(num_algs * sizeof(struct wmfw_adsp2_alg_hdr))
|
||||
+ 4; /* terminator word */
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
header = &cs_dsp_mock_halo_xm_hdr,
|
||||
header_size_bytes = sizeof(cs_dsp_mock_halo_xm_hdr);
|
||||
total_bytes_required = header_size_bytes +
|
||||
(num_algs * sizeof(struct wmfw_halo_alg_hdr))
|
||||
+ 4; /* terminator word */
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(priv->test, "%s unexpected DSP type %d\n",
|
||||
__func__, priv->dsp->type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
builder->blob_data = kunit_kzalloc(priv->test, total_bytes_required, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder->blob_data);
|
||||
builder->blob_size_bytes = total_bytes_required;
|
||||
|
||||
memcpy(builder->blob_data, header, header_size_bytes);
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
cs_dsp_mock_xm_header_add_adsp2_algs(builder, algs, num_algs);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
cs_dsp_mock_xm_header_add_halo_algs(builder, algs, num_algs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_create_mock_xm_header, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
367
drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
Normal file
367
drivers/firmware/cirrus/test/cs_dsp_mock_regmap.c
Normal file
@ -0,0 +1,367 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Mock regmap for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
static int cs_dsp_mock_regmap_read(void *context, const void *reg_buf,
|
||||
const size_t reg_size, void *val_buf,
|
||||
size_t val_size)
|
||||
{
|
||||
struct cs_dsp_test *priv = context;
|
||||
|
||||
/* Should never get here because the regmap is cache-only */
|
||||
KUNIT_FAIL(priv->test, "Unexpected bus read @%#x", *(u32 *)reg_buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int cs_dsp_mock_regmap_gather_write(void *context,
|
||||
const void *reg_buf, size_t reg_size,
|
||||
const void *val_buf, size_t val_size)
|
||||
{
|
||||
struct cs_dsp_test *priv = context;
|
||||
|
||||
priv->saw_bus_write = true;
|
||||
|
||||
/* Should never get here because the regmap is cache-only */
|
||||
KUNIT_FAIL(priv->test, "Unexpected bus gather_write @%#x", *(u32 *)reg_buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int cs_dsp_mock_regmap_write(void *context, const void *val_buf, size_t val_size)
|
||||
{
|
||||
struct cs_dsp_test *priv = context;
|
||||
|
||||
priv->saw_bus_write = true;
|
||||
|
||||
/* Should never get here because the regmap is cache-only */
|
||||
KUNIT_FAIL(priv->test, "Unexpected bus write @%#x", *(u32 *)val_buf);
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static const struct regmap_bus cs_dsp_mock_regmap_bus = {
|
||||
.read = cs_dsp_mock_regmap_read,
|
||||
.write = cs_dsp_mock_regmap_write,
|
||||
.gather_write = cs_dsp_mock_regmap_gather_write,
|
||||
.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
|
||||
};
|
||||
|
||||
static const struct reg_default adsp2_32bit_register_defaults[] = {
|
||||
{ 0xffe00, 0x0000 }, /* CONTROL */
|
||||
{ 0xffe02, 0x0000 }, /* CLOCKING */
|
||||
{ 0xffe04, 0x0001 }, /* STATUS1: RAM_RDY=1 */
|
||||
{ 0xffe30, 0x0000 }, /* WDMW_CONFIG_1 */
|
||||
{ 0xffe32, 0x0000 }, /* WDMA_CONFIG_2 */
|
||||
{ 0xffe34, 0x0000 }, /* RDMA_CONFIG_1 */
|
||||
{ 0xffe40, 0x0000 }, /* SCRATCH_0_1 */
|
||||
{ 0xffe42, 0x0000 }, /* SCRATCH_2_3 */
|
||||
};
|
||||
|
||||
static const struct regmap_range adsp2_32bit_registers[] = {
|
||||
regmap_reg_range(0x80000, 0x88ffe), /* PM */
|
||||
regmap_reg_range(0xa0000, 0xa9ffe), /* XM */
|
||||
regmap_reg_range(0xc0000, 0xc1ffe), /* YM */
|
||||
regmap_reg_range(0xe0000, 0xe1ffe), /* ZM */
|
||||
regmap_reg_range(0xffe00, 0xffe7c), /* CORE CTRL */
|
||||
};
|
||||
|
||||
const unsigned int cs_dsp_mock_adsp2_32bit_sysbase = 0xffe00;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_32bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct regmap_access_table adsp2_32bit_rw = {
|
||||
.yes_ranges = adsp2_32bit_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(adsp2_32bit_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_config cs_dsp_mock_regmap_adsp2_32bit = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 2,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.wr_table = &adsp2_32bit_rw,
|
||||
.rd_table = &adsp2_32bit_rw,
|
||||
.max_register = 0xffe7c,
|
||||
.reg_defaults = adsp2_32bit_register_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(adsp2_32bit_register_defaults),
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct reg_default adsp2_16bit_register_defaults[] = {
|
||||
{ 0x1100, 0x0000 }, /* CONTROL */
|
||||
{ 0x1101, 0x0000 }, /* CLOCKING */
|
||||
{ 0x1104, 0x0001 }, /* STATUS1: RAM_RDY=1 */
|
||||
{ 0x1130, 0x0000 }, /* WDMW_CONFIG_1 */
|
||||
{ 0x1131, 0x0000 }, /* WDMA_CONFIG_2 */
|
||||
{ 0x1134, 0x0000 }, /* RDMA_CONFIG_1 */
|
||||
{ 0x1140, 0x0000 }, /* SCRATCH_0 */
|
||||
{ 0x1141, 0x0000 }, /* SCRATCH_1 */
|
||||
{ 0x1142, 0x0000 }, /* SCRATCH_2 */
|
||||
{ 0x1143, 0x0000 }, /* SCRATCH_3 */
|
||||
};
|
||||
|
||||
static const struct regmap_range adsp2_16bit_registers[] = {
|
||||
regmap_reg_range(0x001100, 0x001143), /* CORE CTRL */
|
||||
regmap_reg_range(0x100000, 0x105fff), /* PM */
|
||||
regmap_reg_range(0x180000, 0x1807ff), /* ZM */
|
||||
regmap_reg_range(0x190000, 0x1947ff), /* XM */
|
||||
regmap_reg_range(0x1a8000, 0x1a97ff), /* YM */
|
||||
};
|
||||
|
||||
const unsigned int cs_dsp_mock_adsp2_16bit_sysbase = 0x001100;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_adsp2_16bit_sysbase, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct regmap_access_table adsp2_16bit_rw = {
|
||||
.yes_ranges = adsp2_16bit_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(adsp2_16bit_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_config cs_dsp_mock_regmap_adsp2_16bit = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 16,
|
||||
.reg_stride = 1,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.wr_table = &adsp2_16bit_rw,
|
||||
.rd_table = &adsp2_16bit_rw,
|
||||
.max_register = 0x1a97ff,
|
||||
.reg_defaults = adsp2_16bit_register_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(adsp2_16bit_register_defaults),
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
static const struct reg_default halo_register_defaults[] = {
|
||||
/* CORE */
|
||||
{ 0x2b80010, 0 }, /* HALO_CORE_SOFT_RESET */
|
||||
{ 0x2b805c0, 0 }, /* HALO_SCRATCH1 */
|
||||
{ 0x2b805c8, 0 }, /* HALO_SCRATCH2 */
|
||||
{ 0x2b805d0, 0 }, /* HALO_SCRATCH3 */
|
||||
{ 0x2b805c8, 0 }, /* HALO_SCRATCH4 */
|
||||
{ 0x2bc1000, 0 }, /* HALO_CCM_CORE_CONTROL */
|
||||
{ 0x2bc7000, 0 }, /* HALO_WDT_CONTROL */
|
||||
|
||||
/* SYSINFO */
|
||||
{ 0x25e2040, 0 }, /* HALO_AHBM_WINDOW_DEBUG_0 */
|
||||
{ 0x25e2044, 0 }, /* HALO_AHBM_WINDOW_DEBUG_1 */
|
||||
};
|
||||
|
||||
static const struct regmap_range halo_readable_registers[] = {
|
||||
regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
|
||||
regmap_reg_range(0x25e0000, 0x25e004f), /* SYSINFO */
|
||||
regmap_reg_range(0x25e2000, 0x25e2047), /* SYSINFO */
|
||||
regmap_reg_range(0x2800000, 0x2807fff), /* XM */
|
||||
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
|
||||
regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
|
||||
regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
|
||||
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
|
||||
};
|
||||
|
||||
static const struct regmap_range halo_writeable_registers[] = {
|
||||
regmap_reg_range(0x2000000, 0x2005fff), /* XM_PACKED */
|
||||
regmap_reg_range(0x2800000, 0x2807fff), /* XM */
|
||||
regmap_reg_range(0x2b80000, 0x2bc700b), /* CORE CTRL */
|
||||
regmap_reg_range(0x2c00000, 0x2c047f3), /* YM_PACKED */
|
||||
regmap_reg_range(0x3400000, 0x3405ff7), /* YM */
|
||||
regmap_reg_range(0x3800000, 0x3804fff), /* PM_PACKED */
|
||||
};
|
||||
|
||||
const unsigned int cs_dsp_mock_halo_core_base = 0x2b80000;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_core_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
const unsigned int cs_dsp_mock_halo_sysinfo_base = 0x25e0000;
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_halo_sysinfo_base, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static const struct regmap_access_table halo_readable = {
|
||||
.yes_ranges = halo_readable_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(halo_readable_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table halo_writeable = {
|
||||
.yes_ranges = halo_writeable_registers,
|
||||
.n_yes_ranges = ARRAY_SIZE(halo_writeable_registers),
|
||||
};
|
||||
|
||||
static const struct regmap_config cs_dsp_mock_regmap_halo = {
|
||||
.reg_bits = 32,
|
||||
.val_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
|
||||
.val_format_endian = REGMAP_ENDIAN_BIG,
|
||||
.wr_table = &halo_writeable,
|
||||
.rd_table = &halo_readable,
|
||||
.max_register = 0x3804ffc,
|
||||
.reg_defaults = halo_register_defaults,
|
||||
.num_reg_defaults = ARRAY_SIZE(halo_register_defaults),
|
||||
.cache_type = REGCACHE_MAPLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_range() - drop a range of registers from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @first_reg: Address of first register to drop.
|
||||
* @last_reg: Address of last register to drop.
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, unsigned int last_reg)
|
||||
{
|
||||
regcache_drop_region(priv->dsp->regmap, first_reg, last_reg);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_range, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_regs() - drop a number of registers from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @first_reg: Address of first register to drop.
|
||||
* @num_regs: Number of registers to drop.
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_regs)
|
||||
{
|
||||
int stride = regmap_get_reg_stride(priv->dsp->regmap);
|
||||
unsigned int last = first_reg + (stride * (num_regs - 1));
|
||||
|
||||
cs_dsp_mock_regmap_drop_range(priv, first_reg, last);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_bytes() - drop a number of bytes from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @first_reg: Address of first register to drop.
|
||||
* @num_bytes: Number of bytes to drop from the cache. Will be rounded
|
||||
* down to a whole number of registers. Trailing bytes that
|
||||
* are not a multiple of the register size will not be dropped.
|
||||
* (This is intended to help detect math errors in test code.)
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_bytes)
|
||||
{
|
||||
size_t num_regs = num_bytes / regmap_get_val_bytes(priv->dsp->regmap);
|
||||
|
||||
cs_dsp_mock_regmap_drop_regs(priv, first_reg, num_regs);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_bytes, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_drop_system_regs() - Drop DSP system registers from the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
*
|
||||
* Drops all DSP system registers from the regmap cache.
|
||||
*/
|
||||
void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv)
|
||||
{
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
if (priv->dsp->base) {
|
||||
regcache_drop_region(priv->dsp->regmap,
|
||||
priv->dsp->base,
|
||||
priv->dsp->base + 0x7c);
|
||||
}
|
||||
return;
|
||||
case WMFW_HALO:
|
||||
if (priv->dsp->base) {
|
||||
regcache_drop_region(priv->dsp->regmap,
|
||||
priv->dsp->base,
|
||||
priv->dsp->base + 0x47000);
|
||||
}
|
||||
|
||||
/* sysinfo registers are read-only so don't drop them */
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_drop_system_regs, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_is_dirty() - Test for dirty registers in the cache.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object.
|
||||
* @drop_system_regs: If true the DSP system regs will be dropped from
|
||||
* the cache before checking for dirty.
|
||||
*
|
||||
* All registers that are expected to be written must have been dropped
|
||||
* from the cache (DSP system registers can be dropped by passing
|
||||
* drop_system_regs == true). If any unexpected registers were written
|
||||
* there will still be dirty entries in the cache and a cache sync will
|
||||
* cause a write.
|
||||
*
|
||||
* Returns: true if there were dirty entries, false if not.
|
||||
*/
|
||||
bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs)
|
||||
{
|
||||
if (drop_system_regs)
|
||||
cs_dsp_mock_regmap_drop_system_regs(priv);
|
||||
|
||||
priv->saw_bus_write = false;
|
||||
regcache_cache_only(priv->dsp->regmap, false);
|
||||
regcache_sync(priv->dsp->regmap);
|
||||
regcache_cache_only(priv->dsp->regmap, true);
|
||||
|
||||
return priv->saw_bus_write;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_is_dirty, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_regmap_init() - Initialize a mock regmap.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test object. This must have a
|
||||
* valid pointer to a struct cs_dsp in which the type and
|
||||
* rev fields are set to the type of DSP to be simulated.
|
||||
*
|
||||
* On success the priv->dsp->regmap will point to the created
|
||||
* regmap instance.
|
||||
*
|
||||
* Return: zero on success, else negative error code.
|
||||
*/
|
||||
int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv)
|
||||
{
|
||||
const struct regmap_config *config;
|
||||
int ret;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_HALO:
|
||||
config = &cs_dsp_mock_regmap_halo;
|
||||
break;
|
||||
case WMFW_ADSP2:
|
||||
if (priv->dsp->rev == 0)
|
||||
config = &cs_dsp_mock_regmap_adsp2_16bit;
|
||||
else
|
||||
config = &cs_dsp_mock_regmap_adsp2_32bit;
|
||||
break;
|
||||
default:
|
||||
config = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
priv->dsp->regmap = devm_regmap_init(priv->dsp->dev,
|
||||
&cs_dsp_mock_regmap_bus,
|
||||
priv,
|
||||
config);
|
||||
if (IS_ERR(priv->dsp->regmap)) {
|
||||
ret = PTR_ERR(priv->dsp->regmap);
|
||||
kunit_err(priv->test, "Failed to allocate register map: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Put regmap in cache-only so it accumulates the writes done by cs_dsp */
|
||||
regcache_cache_only(priv->dsp->regmap, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_regmap_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
13
drivers/firmware/cirrus/test/cs_dsp_mock_utils.c
Normal file
13
drivers/firmware/cirrus/test/cs_dsp_mock_utils.c
Normal file
@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Utility module for cs_dsp KUnit testing.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
MODULE_DESCRIPTION("Utilities for Cirrus Logic DSP driver testing");
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("FW_CS_DSP");
|
473
drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
Normal file
473
drivers/firmware/cirrus/test/cs_dsp_mock_wmfw.c
Normal file
@ -0,0 +1,473 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// wmfw file builder for cs_dsp KUnit tests.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/math.h>
|
||||
#include <linux/overflow.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
/* Buffer large enough for bin file content */
|
||||
#define CS_DSP_MOCK_WMFW_BUF_SIZE 131072
|
||||
|
||||
struct cs_dsp_mock_wmfw_builder {
|
||||
struct cs_dsp_test *test_priv;
|
||||
int format_version;
|
||||
void *buf;
|
||||
size_t buf_size_bytes;
|
||||
void *write_p;
|
||||
size_t bytes_used;
|
||||
|
||||
void *alg_data_header;
|
||||
unsigned int num_coeffs;
|
||||
};
|
||||
|
||||
struct wmfw_adsp2_halo_header {
|
||||
struct wmfw_header header;
|
||||
struct wmfw_adsp2_sizes sizes;
|
||||
struct wmfw_footer footer;
|
||||
} __packed;
|
||||
|
||||
struct wmfw_long_string {
|
||||
__le16 len;
|
||||
u8 data[] __nonstring __counted_by(len);
|
||||
} __packed;
|
||||
|
||||
struct wmfw_short_string {
|
||||
u8 len;
|
||||
u8 data[] __nonstring __counted_by(len);
|
||||
} __packed;
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(vfree_action_wrapper, vfree, void *)
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_format_version() - Return format version.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
|
||||
*
|
||||
* Return: Format version.
|
||||
*/
|
||||
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
return builder->format_version;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_format_version, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_get_firmware() - Get struct firmware wrapper for data.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_wmfw_builder.
|
||||
*
|
||||
* Return: Pointer to a struct firmware wrapper for the data.
|
||||
*/
|
||||
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
struct firmware *fw;
|
||||
|
||||
if (!builder)
|
||||
return NULL;
|
||||
|
||||
fw = kunit_kzalloc(builder->test_priv->test, sizeof(*fw), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, fw);
|
||||
|
||||
fw->data = builder->buf;
|
||||
fw->size = builder->bytes_used;
|
||||
|
||||
return fw;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_get_firmware, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_add_raw_block() - Add a block to the wmfw file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @block_type: Block type.
|
||||
* @offset: Offset.
|
||||
* @payload_data: Pointer to buffer containing the payload data,
|
||||
* or NULL if no data.
|
||||
* @payload_len_bytes: Length of payload data in bytes, or zero.
|
||||
*/
|
||||
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int block_type, unsigned int offset,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
struct wmfw_region *header = builder->write_p;
|
||||
unsigned int bytes_needed = struct_size_t(struct wmfw_region, data, payload_len_bytes);
|
||||
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
header->offset = cpu_to_le32(offset | (block_type << 24));
|
||||
header->len = cpu_to_le32(payload_len_bytes);
|
||||
if (payload_len_bytes > 0)
|
||||
memcpy(header->data, payload_data, payload_len_bytes);
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_raw_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_add_info() - Add an info block to the wmfw file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @info: Pointer to info string to be copied into the file.
|
||||
*
|
||||
* The string will be padded to a length that is a multiple of 4 bytes.
|
||||
*/
|
||||
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const char *info)
|
||||
{
|
||||
size_t info_len = strlen(info);
|
||||
char *tmp = NULL;
|
||||
|
||||
if (info_len % 4) {
|
||||
/* Create a padded string with length a multiple of 4 */
|
||||
info_len = round_up(info_len, 4);
|
||||
tmp = kunit_kzalloc(builder->test_priv->test, info_len, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(builder->test_priv->test, tmp);
|
||||
memcpy(tmp, info, info_len);
|
||||
info = tmp;
|
||||
}
|
||||
|
||||
cs_dsp_mock_wmfw_add_raw_block(builder, WMFW_INFO_TEXT, 0, info, info_len);
|
||||
kunit_kfree(builder->test_priv->test, tmp);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_info, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_add_data_block() - Add a data block to the wmfw file.
|
||||
*
|
||||
* @builder: Pointer to struct cs_dsp_mock_bin_builder.
|
||||
* @mem_region: Memory region for the block.
|
||||
* @mem_offset_dsp_words: Offset to start of destination in DSP words.
|
||||
* @payload_data: Pointer to buffer containing the payload data.
|
||||
* @payload_len_bytes: Length of payload data in bytes.
|
||||
*/
|
||||
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int mem_region, unsigned int mem_offset_dsp_words,
|
||||
const void *payload_data, size_t payload_len_bytes)
|
||||
{
|
||||
/* Blob payload length must be a multiple of 4 */
|
||||
KUNIT_ASSERT_EQ(builder->test_priv->test, payload_len_bytes % 4, 0);
|
||||
|
||||
cs_dsp_mock_wmfw_add_raw_block(builder, mem_region, mem_offset_dsp_words,
|
||||
payload_data, payload_len_bytes);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_data_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
unsigned int alg_id,
|
||||
const char *name,
|
||||
const char *description)
|
||||
{
|
||||
struct wmfw_region *rgn = builder->write_p;
|
||||
struct wmfw_adsp_alg_data *v1;
|
||||
struct wmfw_short_string *shortstring;
|
||||
struct wmfw_long_string *longstring;
|
||||
size_t bytes_needed, name_len, description_len;
|
||||
int offset;
|
||||
|
||||
/* Bytes needed for region header */
|
||||
bytes_needed = offsetof(struct wmfw_region, data);
|
||||
|
||||
builder->alg_data_header = builder->write_p;
|
||||
builder->num_coeffs = 0;
|
||||
|
||||
switch (builder->format_version) {
|
||||
case 0:
|
||||
KUNIT_FAIL(builder->test_priv->test, "wmfwV0 does not have alg blocks\n");
|
||||
return;
|
||||
case 1:
|
||||
bytes_needed += offsetof(struct wmfw_adsp_alg_data, data);
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
memset(builder->write_p, 0, bytes_needed);
|
||||
|
||||
/* Create region header */
|
||||
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
|
||||
|
||||
/* Create algorithm entry */
|
||||
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
|
||||
v1->id = cpu_to_le32(alg_id);
|
||||
if (name)
|
||||
strscpy(v1->name, name, sizeof(v1->name));
|
||||
|
||||
if (description)
|
||||
strscpy(v1->descr, description, sizeof(v1->descr));
|
||||
break;
|
||||
default:
|
||||
name_len = 0;
|
||||
description_len = 0;
|
||||
|
||||
if (name)
|
||||
name_len = strlen(name);
|
||||
|
||||
if (description)
|
||||
description_len = strlen(description);
|
||||
|
||||
bytes_needed += sizeof(__le32); /* alg id */
|
||||
bytes_needed += round_up(name_len + sizeof(u8), sizeof(__le32));
|
||||
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
|
||||
bytes_needed += sizeof(__le32); /* coeff count */
|
||||
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
memset(builder->write_p, 0, bytes_needed);
|
||||
|
||||
/* Create region header */
|
||||
rgn->offset = cpu_to_le32(WMFW_ALGORITHM_DATA << 24);
|
||||
|
||||
/* Create algorithm entry */
|
||||
*(__force __le32 *)&rgn->data[0] = cpu_to_le32(alg_id);
|
||||
|
||||
shortstring = (struct wmfw_short_string *)&rgn->data[4];
|
||||
shortstring->len = name_len;
|
||||
|
||||
if (name_len)
|
||||
memcpy(shortstring->data, name, name_len);
|
||||
|
||||
/* Round up to next __le32 */
|
||||
offset = round_up(4 + struct_size_t(struct wmfw_short_string, data, name_len),
|
||||
sizeof(__le32));
|
||||
|
||||
longstring = (struct wmfw_long_string *)&rgn->data[offset];
|
||||
longstring->len = cpu_to_le16(description_len);
|
||||
|
||||
if (description_len)
|
||||
memcpy(longstring->data, description, description_len);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_start_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const struct cs_dsp_mock_coeff_def *def)
|
||||
{
|
||||
struct wmfw_adsp_coeff_data *v1;
|
||||
struct wmfw_short_string *shortstring;
|
||||
struct wmfw_long_string *longstring;
|
||||
size_t bytes_needed, shortname_len, fullname_len, description_len;
|
||||
__le32 *ple32;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, builder->alg_data_header);
|
||||
|
||||
switch (builder->format_version) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
bytes_needed = offsetof(struct wmfw_adsp_coeff_data, data);
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
v1 = (struct wmfw_adsp_coeff_data *)builder->write_p;
|
||||
memset(v1, 0, sizeof(*v1));
|
||||
v1->hdr.offset = cpu_to_le16(def->offset_dsp_words);
|
||||
v1->hdr.type = cpu_to_le16(def->mem_type);
|
||||
v1->hdr.size = cpu_to_le32(bytes_needed - sizeof(v1->hdr));
|
||||
v1->ctl_type = cpu_to_le16(def->type);
|
||||
v1->flags = cpu_to_le16(def->flags);
|
||||
v1->len = cpu_to_le32(def->length_bytes);
|
||||
|
||||
if (def->fullname)
|
||||
strscpy(v1->name, def->fullname, sizeof(v1->name));
|
||||
|
||||
if (def->description)
|
||||
strscpy(v1->descr, def->description, sizeof(v1->descr));
|
||||
break;
|
||||
default:
|
||||
fullname_len = 0;
|
||||
description_len = 0;
|
||||
shortname_len = strlen(def->shortname);
|
||||
|
||||
if (def->fullname)
|
||||
fullname_len = strlen(def->fullname);
|
||||
|
||||
if (def->description)
|
||||
description_len = strlen(def->description);
|
||||
|
||||
bytes_needed = sizeof(__le32) * 2; /* type, offset and size */
|
||||
bytes_needed += round_up(shortname_len + sizeof(u8), sizeof(__le32));
|
||||
bytes_needed += round_up(fullname_len + sizeof(u8), sizeof(__le32));
|
||||
bytes_needed += round_up(description_len + sizeof(__le16), sizeof(__le32));
|
||||
bytes_needed += sizeof(__le32) * 2; /* flags, type and length */
|
||||
KUNIT_ASSERT_TRUE(builder->test_priv->test,
|
||||
(builder->write_p + bytes_needed) <
|
||||
(builder->buf + CS_DSP_MOCK_WMFW_BUF_SIZE));
|
||||
|
||||
ple32 = (__force __le32 *)builder->write_p;
|
||||
*ple32++ = cpu_to_le32(def->offset_dsp_words | (def->mem_type << 16));
|
||||
*ple32++ = cpu_to_le32(bytes_needed - sizeof(__le32) - sizeof(__le32));
|
||||
|
||||
shortstring = (__force struct wmfw_short_string *)ple32;
|
||||
shortstring->len = shortname_len;
|
||||
memcpy(shortstring->data, def->shortname, shortname_len);
|
||||
|
||||
/* Round up to next __le32 multiple */
|
||||
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, shortname_len),
|
||||
sizeof(*ple32)) / sizeof(*ple32);
|
||||
|
||||
shortstring = (__force struct wmfw_short_string *)ple32;
|
||||
shortstring->len = fullname_len;
|
||||
memcpy(shortstring->data, def->fullname, fullname_len);
|
||||
|
||||
/* Round up to next __le32 multiple */
|
||||
ple32 += round_up(struct_size_t(struct wmfw_short_string, data, fullname_len),
|
||||
sizeof(*ple32)) / sizeof(*ple32);
|
||||
|
||||
longstring = (__force struct wmfw_long_string *)ple32;
|
||||
longstring->len = cpu_to_le16(description_len);
|
||||
memcpy(longstring->data, def->description, description_len);
|
||||
|
||||
/* Round up to next __le32 multiple */
|
||||
ple32 += round_up(struct_size_t(struct wmfw_long_string, data, description_len),
|
||||
sizeof(*ple32)) / sizeof(*ple32);
|
||||
|
||||
*ple32++ = cpu_to_le32(def->type | (def->flags << 16));
|
||||
*ple32 = cpu_to_le32(def->length_bytes);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->write_p += bytes_needed;
|
||||
builder->bytes_used += bytes_needed;
|
||||
builder->num_coeffs++;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_add_coeff_desc, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
struct wmfw_region *rgn = builder->alg_data_header;
|
||||
struct wmfw_adsp_alg_data *v1;
|
||||
const struct wmfw_short_string *shortstring;
|
||||
const struct wmfw_long_string *longstring;
|
||||
size_t offset;
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(builder->test_priv->test, rgn);
|
||||
|
||||
/* Fill in data size */
|
||||
rgn->len = cpu_to_le32((u8 *)builder->write_p - (u8 *)rgn->data);
|
||||
|
||||
/* Fill in coefficient count */
|
||||
switch (builder->format_version) {
|
||||
case 0:
|
||||
return;
|
||||
case 1:
|
||||
v1 = (struct wmfw_adsp_alg_data *)&rgn->data[0];
|
||||
v1->ncoeff = cpu_to_le32(builder->num_coeffs);
|
||||
break;
|
||||
default:
|
||||
offset = 4; /* skip alg id */
|
||||
|
||||
/* Get name length and round up to __le32 multiple */
|
||||
shortstring = (const struct wmfw_short_string *)&rgn->data[offset];
|
||||
offset += round_up(struct_size_t(struct wmfw_short_string, data, shortstring->len),
|
||||
sizeof(__le32));
|
||||
|
||||
/* Get description length and round up to __le32 multiple */
|
||||
longstring = (const struct wmfw_long_string *)&rgn->data[offset];
|
||||
offset += round_up(struct_size_t(struct wmfw_long_string, data,
|
||||
le16_to_cpu(longstring->len)),
|
||||
sizeof(__le32));
|
||||
|
||||
*(__force __le32 *)&rgn->data[offset] = cpu_to_le32(builder->num_coeffs);
|
||||
break;
|
||||
}
|
||||
|
||||
builder->alg_data_header = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_end_alg_info_block, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
||||
|
||||
static void cs_dsp_init_adsp2_halo_wmfw(struct cs_dsp_mock_wmfw_builder *builder)
|
||||
{
|
||||
struct wmfw_adsp2_halo_header *hdr = builder->buf;
|
||||
const struct cs_dsp *dsp = builder->test_priv->dsp;
|
||||
|
||||
memcpy(hdr->header.magic, "WMFW", sizeof(hdr->header.magic));
|
||||
hdr->header.len = cpu_to_le32(sizeof(*hdr));
|
||||
hdr->header.ver = builder->format_version;
|
||||
hdr->header.core = dsp->type;
|
||||
hdr->header.rev = cpu_to_le16(dsp->rev);
|
||||
|
||||
hdr->sizes.pm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_PM));
|
||||
hdr->sizes.xm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_XM));
|
||||
hdr->sizes.ym = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_YM));
|
||||
|
||||
switch (dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
hdr->sizes.zm = cpu_to_le32(cs_dsp_mock_size_of_region(dsp, WMFW_ADSP2_ZM));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
builder->write_p = &hdr[1];
|
||||
builder->bytes_used += sizeof(*hdr);
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_mock_wmfw_init() - Initialize a struct cs_dsp_mock_wmfw_builder.
|
||||
*
|
||||
* @priv: Pointer to struct cs_dsp_test.
|
||||
* @format_version: Required wmfw format version.
|
||||
*
|
||||
* Return: Pointer to created struct cs_dsp_mock_wmfw_builder.
|
||||
*/
|
||||
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
|
||||
int format_version)
|
||||
{
|
||||
struct cs_dsp_mock_wmfw_builder *builder;
|
||||
|
||||
/* If format version isn't given use the default for the target core */
|
||||
if (format_version < 0) {
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
format_version = 2;
|
||||
break;
|
||||
default:
|
||||
format_version = 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
builder = kunit_kzalloc(priv->test, sizeof(*builder), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(priv->test, builder);
|
||||
|
||||
builder->test_priv = priv;
|
||||
builder->format_version = format_version;
|
||||
|
||||
builder->buf = vmalloc(CS_DSP_MOCK_WMFW_BUF_SIZE);
|
||||
KUNIT_ASSERT_NOT_NULL(priv->test, builder->buf);
|
||||
kunit_add_action_or_reset(priv->test, vfree_action_wrapper, builder->buf);
|
||||
|
||||
builder->buf_size_bytes = CS_DSP_MOCK_WMFW_BUF_SIZE;
|
||||
|
||||
switch (priv->dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
case WMFW_HALO:
|
||||
cs_dsp_init_adsp2_halo_wmfw(builder);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_mock_wmfw_init, "FW_CS_DSP_KUNIT_TEST_UTILS");
|
2556
drivers/firmware/cirrus/test/cs_dsp_test_bin.c
Normal file
2556
drivers/firmware/cirrus/test/cs_dsp_test_bin.c
Normal file
File diff suppressed because it is too large
Load Diff
600
drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
Normal file
600
drivers/firmware/cirrus/test/cs_dsp_test_bin_error.c
Normal file
@ -0,0 +1,600 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// KUnit tests for cs_dsp.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
//
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *);
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *);
|
||||
|
||||
struct cs_dsp_test_local {
|
||||
struct cs_dsp_mock_bin_builder *bin_builder;
|
||||
struct cs_dsp_mock_xm_header *xm_header;
|
||||
struct cs_dsp_mock_wmfw_builder *wmfw_builder;
|
||||
struct firmware *wmfw;
|
||||
int wmfw_version;
|
||||
};
|
||||
|
||||
struct cs_dsp_bin_test_param {
|
||||
int block_type;
|
||||
};
|
||||
|
||||
static const struct cs_dsp_mock_alg_def cs_dsp_bin_err_test_mock_algs[] = {
|
||||
{
|
||||
.id = 0xfafa,
|
||||
.ver = 0x100000,
|
||||
.xm_size_words = 164,
|
||||
.ym_size_words = 164,
|
||||
.zm_size_words = 164,
|
||||
},
|
||||
};
|
||||
|
||||
/* Load a bin containing unknown blocks. They should be skipped. */
|
||||
static void bin_load_with_unknown_blocks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
unsigned int reg_addr;
|
||||
u8 *payload_data, *readback;
|
||||
u8 random_data[8];
|
||||
const unsigned int payload_size_bytes = 64;
|
||||
|
||||
payload_data = kunit_kmalloc(test, payload_size_bytes, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, payload_data);
|
||||
get_random_bytes(payload_data, payload_size_bytes);
|
||||
|
||||
readback = kunit_kzalloc(test, payload_size_bytes, GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, readback);
|
||||
|
||||
/* Add some unknown blocks at the start of the bin */
|
||||
get_random_bytes(random_data, sizeof(random_data));
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
0xf5, 0,
|
||||
random_data, sizeof(random_data));
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
0xf500, 0,
|
||||
random_data, sizeof(random_data));
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
0xc300, 0,
|
||||
random_data, sizeof(random_data));
|
||||
|
||||
/* Add a single payload to be written to DSP memory */
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
WMFW_ADSP2_YM, 0,
|
||||
payload_data, payload_size_bytes);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
/* Check that the payload was written to memory */
|
||||
reg_addr = cs_dsp_mock_base_addr_for_mem(priv, WMFW_ADSP2_YM);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
regmap_raw_read(priv->dsp->regmap, reg_addr, readback, payload_size_bytes),
|
||||
0);
|
||||
KUNIT_EXPECT_MEMEQ(test, readback, payload_data, payload_size_bytes);
|
||||
}
|
||||
|
||||
/* Load a bin that doesn't have a valid magic marker. */
|
||||
static void bin_err_wrong_magic(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
|
||||
memcpy((void *)bin->data, "WMFW", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "xMDR", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "WxDR", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "WMxR", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memcpy((void *)bin->data, "WMDx", 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
memset((void *)bin->data, 0, 4);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
/* Load a bin that is too short for a valid header. */
|
||||
static void bin_err_too_short_for_header(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
do {
|
||||
bin->size--;
|
||||
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
} while (bin->size > 0);
|
||||
}
|
||||
|
||||
/* Header length field isn't a valid header length. */
|
||||
static void bin_err_bad_header_length(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
struct wmfw_coeff_hdr *header;
|
||||
unsigned int real_len, len;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header = (struct wmfw_coeff_hdr *)bin->data;
|
||||
real_len = le32_to_cpu(header->len);
|
||||
|
||||
for (len = 0; len < real_len; len++) {
|
||||
header->len = cpu_to_le32(len);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
for (len = real_len + 1; len < real_len + 7; len++) {
|
||||
header->len = cpu_to_le32(len);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
header->len = cpu_to_le32(0xffffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->len = cpu_to_le32(0x80000000);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->len = cpu_to_le32(0x7fffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
/* Wrong core type in header. */
|
||||
static void bin_err_bad_core_type(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
struct wmfw_coeff_hdr *header;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header = (struct wmfw_coeff_hdr *)bin->data;
|
||||
|
||||
header->core_ver = cpu_to_le32(0);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->core_ver = cpu_to_le32(1);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->core_ver = cpu_to_le32(priv->dsp->type + 1);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
header->core_ver = cpu_to_le32(0xff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
/* File too short to contain a full block header */
|
||||
static void bin_too_short_for_block_header(struct kunit *test)
|
||||
{
|
||||
const struct cs_dsp_bin_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
unsigned int header_length;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header_length = bin->size;
|
||||
kunit_kfree(test, bin);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
param->block_type, 0,
|
||||
NULL, 0);
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
KUNIT_ASSERT_GT(test, bin->size, header_length);
|
||||
|
||||
for (bin->size--; bin->size > header_length; bin->size--) {
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/* File too short to contain the block payload */
|
||||
static void bin_too_short_for_block_payload(struct kunit *test)
|
||||
{
|
||||
const struct cs_dsp_bin_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
static const u8 payload[256] = { };
|
||||
int i;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
param->block_type, 0,
|
||||
payload, sizeof(payload));
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
for (i = 0; i < sizeof(payload); i++) {
|
||||
bin->size--;
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Block payload length is a garbage value */
|
||||
static void bin_block_payload_len_garbage(struct kunit *test)
|
||||
{
|
||||
const struct cs_dsp_bin_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *bin;
|
||||
struct wmfw_coeff_hdr *header;
|
||||
struct wmfw_coeff_item *block;
|
||||
u32 payload = 0;
|
||||
|
||||
/* Sanity-check that the wmfw loads ok without the bin */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
cs_dsp_power_down(priv->dsp);
|
||||
|
||||
cs_dsp_mock_bin_add_raw_block(local->bin_builder,
|
||||
cs_dsp_bin_err_test_mock_algs[0].id,
|
||||
cs_dsp_bin_err_test_mock_algs[0].ver,
|
||||
param->block_type, 0,
|
||||
&payload, sizeof(payload));
|
||||
|
||||
bin = cs_dsp_mock_bin_get_firmware(local->bin_builder);
|
||||
header = (struct wmfw_coeff_hdr *)bin->data;
|
||||
block = (struct wmfw_coeff_item *)&bin->data[le32_to_cpu(header->len)];
|
||||
|
||||
/* Sanity check that we're looking at the correct part of the bin */
|
||||
KUNIT_ASSERT_EQ(test, le16_to_cpu(block->type), param->block_type);
|
||||
KUNIT_ASSERT_EQ(test, le32_to_cpu(block->len), sizeof(payload));
|
||||
|
||||
block->len = cpu_to_le32(0x8000);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0xffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0x7fffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0x80000000);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
|
||||
block->len = cpu_to_le32(0xffffffff);
|
||||
KUNIT_EXPECT_LT(test,
|
||||
cs_dsp_power_up(priv->dsp, local->wmfw, "wmfw", bin, "bin", "misc"),
|
||||
0);
|
||||
}
|
||||
|
||||
static void cs_dsp_bin_err_test_exit(struct kunit *test)
|
||||
{
|
||||
/*
|
||||
* Testing error conditions can produce a lot of log output
|
||||
* from cs_dsp error messages, so rate limit the test cases.
|
||||
*/
|
||||
usleep_range(200, 500);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_test_common_init(struct kunit *test, struct cs_dsp *dsp,
|
||||
int wmfw_version)
|
||||
{
|
||||
struct cs_dsp_test *priv;
|
||||
struct cs_dsp_test_local *local;
|
||||
struct device *test_dev;
|
||||
int ret;
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
|
||||
if (!local)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->test = test;
|
||||
priv->dsp = dsp;
|
||||
test->priv = priv;
|
||||
priv->local = local;
|
||||
priv->local->wmfw_version = wmfw_version;
|
||||
|
||||
/* Create dummy struct device */
|
||||
test_dev = kunit_device_register(test, "cs_dsp_test_drv");
|
||||
if (IS_ERR(test_dev))
|
||||
return PTR_ERR(test_dev);
|
||||
|
||||
dsp->dev = get_device(test_dev);
|
||||
if (!dsp->dev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dsp->dev, priv);
|
||||
|
||||
/* Allocate regmap */
|
||||
ret = cs_dsp_mock_regmap_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* There must always be a XM header with at least 1 algorithm, so create
|
||||
* a dummy one that tests can use and extract it to a data payload.
|
||||
*/
|
||||
local->xm_header = cs_dsp_create_mock_xm_header(priv,
|
||||
cs_dsp_bin_err_test_mock_algs,
|
||||
ARRAY_SIZE(cs_dsp_bin_err_test_mock_algs));
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->xm_header);
|
||||
|
||||
local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, priv->local->wmfw_version);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);
|
||||
|
||||
/* Add dummy XM header payload to wmfw */
|
||||
cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
|
||||
WMFW_ADSP2_XM, 0,
|
||||
local->xm_header->blob_data,
|
||||
local->xm_header->blob_size_bytes);
|
||||
|
||||
local->wmfw = cs_dsp_mock_wmfw_get_firmware(priv->local->wmfw_builder);
|
||||
|
||||
local->bin_builder =
|
||||
cs_dsp_mock_bin_init(priv, 1,
|
||||
cs_dsp_mock_xm_header_get_fw_version_from_regmap(priv));
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->bin_builder);
|
||||
|
||||
/* Init cs_dsp */
|
||||
dsp->client_ops = kunit_kzalloc(test, sizeof(*dsp->client_ops), GFP_KERNEL);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp->client_ops);
|
||||
|
||||
switch (dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
ret = cs_dsp_adsp2_init(dsp);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
ret = cs_dsp_halo_init(dsp);
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Automatically call cs_dsp_remove() when test case ends */
|
||||
return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_test_halo_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_HALO;
|
||||
dsp->mem = cs_dsp_mock_halo_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_halo_core_base;
|
||||
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
|
||||
|
||||
return cs_dsp_bin_err_test_common_init(test, dsp, 3);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_test_adsp2_32bit_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = 1;
|
||||
dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
|
||||
|
||||
return cs_dsp_bin_err_test_common_init(test, dsp, 2);
|
||||
}
|
||||
|
||||
static int cs_dsp_bin_err_test_adsp2_16bit_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = 0;
|
||||
dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
|
||||
|
||||
return cs_dsp_bin_err_test_common_init(test, dsp, 1);
|
||||
}
|
||||
|
||||
static struct kunit_case cs_dsp_bin_err_test_cases_halo[] = {
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static void cs_dsp_bin_err_block_types_desc(const struct cs_dsp_bin_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "block_type:%#x", param->block_type);
|
||||
}
|
||||
|
||||
/* Some block types to test against, including illegal types */
|
||||
static const struct cs_dsp_bin_test_param bin_test_block_types_cases[] = {
|
||||
{ .block_type = WMFW_INFO_TEXT << 8 },
|
||||
{ .block_type = WMFW_METADATA << 8 },
|
||||
{ .block_type = WMFW_ADSP2_PM },
|
||||
{ .block_type = WMFW_ADSP2_XM },
|
||||
{ .block_type = 0x33 },
|
||||
{ .block_type = 0xf500 },
|
||||
{ .block_type = 0xc000 },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(bin_test_block_types,
|
||||
bin_test_block_types_cases,
|
||||
cs_dsp_bin_err_block_types_desc);
|
||||
|
||||
static struct kunit_case cs_dsp_bin_err_test_cases_adsp2[] = {
|
||||
KUNIT_CASE(bin_load_with_unknown_blocks),
|
||||
KUNIT_CASE(bin_err_wrong_magic),
|
||||
KUNIT_CASE(bin_err_too_short_for_header),
|
||||
KUNIT_CASE(bin_err_bad_header_length),
|
||||
KUNIT_CASE(bin_err_bad_core_type),
|
||||
|
||||
KUNIT_CASE_PARAM(bin_too_short_for_block_header, bin_test_block_types_gen_params),
|
||||
KUNIT_CASE_PARAM(bin_too_short_for_block_payload, bin_test_block_types_gen_params),
|
||||
KUNIT_CASE_PARAM(bin_block_payload_len_garbage, bin_test_block_types_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_bin_err_test_halo = {
|
||||
.name = "cs_dsp_bin_err_halo",
|
||||
.init = cs_dsp_bin_err_test_halo_init,
|
||||
.exit = cs_dsp_bin_err_test_exit,
|
||||
.test_cases = cs_dsp_bin_err_test_cases_halo,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_bin_err_test_adsp2_32bit = {
|
||||
.name = "cs_dsp_bin_err_adsp2_32bit",
|
||||
.init = cs_dsp_bin_err_test_adsp2_32bit_init,
|
||||
.exit = cs_dsp_bin_err_test_exit,
|
||||
.test_cases = cs_dsp_bin_err_test_cases_adsp2,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_bin_err_test_adsp2_16bit = {
|
||||
.name = "cs_dsp_bin_err_adsp2_16bit",
|
||||
.init = cs_dsp_bin_err_test_adsp2_16bit_init,
|
||||
.exit = cs_dsp_bin_err_test_exit,
|
||||
.test_cases = cs_dsp_bin_err_test_cases_adsp2,
|
||||
};
|
||||
|
||||
kunit_test_suites(&cs_dsp_bin_err_test_halo,
|
||||
&cs_dsp_bin_err_test_adsp2_32bit,
|
||||
&cs_dsp_bin_err_test_adsp2_16bit);
|
688
drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
Normal file
688
drivers/firmware/cirrus/test/cs_dsp_test_callbacks.c
Normal file
@ -0,0 +1,688 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// KUnit tests for cs_dsp.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
//
|
||||
|
||||
#include <kunit/device.h>
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp.h>
|
||||
#include <linux/firmware/cirrus/cs_dsp_test_utils.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#define ADSP2_LOCK_REGION_CTRL 0x7A
|
||||
#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
|
||||
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_put_device_wrapper, put_device, struct device *)
|
||||
KUNIT_DEFINE_ACTION_WRAPPER(_cs_dsp_remove_wrapper, cs_dsp_remove, struct cs_dsp *)
|
||||
|
||||
struct cs_dsp_test_local {
|
||||
struct cs_dsp_mock_wmfw_builder *wmfw_builder;
|
||||
|
||||
int num_control_add;
|
||||
int num_control_remove;
|
||||
int num_pre_run;
|
||||
int num_post_run;
|
||||
int num_pre_stop;
|
||||
int num_post_stop;
|
||||
int num_watchdog_expired;
|
||||
|
||||
struct cs_dsp_coeff_ctl *passed_ctl[16];
|
||||
struct cs_dsp *passed_dsp;
|
||||
};
|
||||
|
||||
struct cs_dsp_callbacks_test_param {
|
||||
const struct cs_dsp_client_ops *ops;
|
||||
const char *case_name;
|
||||
};
|
||||
|
||||
static const struct cs_dsp_mock_alg_def cs_dsp_callbacks_test_mock_algs[] = {
|
||||
{
|
||||
.id = 0xfafa,
|
||||
.ver = 0x100000,
|
||||
.xm_size_words = 164,
|
||||
.ym_size_words = 164,
|
||||
.zm_size_words = 164,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct cs_dsp_mock_coeff_def mock_coeff_template = {
|
||||
.shortname = "Dummy Coeff",
|
||||
.type = WMFW_CTL_TYPE_BYTES,
|
||||
.mem_type = WMFW_ADSP2_YM,
|
||||
.flags = WMFW_CTL_FLAG_VOLATILE,
|
||||
.length_bytes = 4,
|
||||
};
|
||||
|
||||
static int cs_dsp_test_control_add_callback(struct cs_dsp_coeff_ctl *ctl)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_ctl[local->num_control_add] = ctl;
|
||||
local->num_control_add++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_control_remove_callback(struct cs_dsp_coeff_ctl *ctl)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_ctl[local->num_control_remove] = ctl;
|
||||
local->num_control_remove++;
|
||||
}
|
||||
|
||||
static int cs_dsp_test_pre_run_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_pre_run++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cs_dsp_test_post_run_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_post_run++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_pre_stop_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_pre_stop++;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_post_stop_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_post_stop++;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_watchdog_expired_callback(struct cs_dsp *dsp)
|
||||
{
|
||||
struct kunit *test = kunit_get_current_test();
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
|
||||
local->passed_dsp = dsp;
|
||||
local->num_watchdog_expired++;
|
||||
}
|
||||
|
||||
static const struct cs_dsp_client_ops cs_dsp_callback_test_client_ops = {
|
||||
.control_add = cs_dsp_test_control_add_callback,
|
||||
.control_remove = cs_dsp_test_control_remove_callback,
|
||||
.pre_run = cs_dsp_test_pre_run_callback,
|
||||
.post_run = cs_dsp_test_post_run_callback,
|
||||
.pre_stop = cs_dsp_test_pre_stop_callback,
|
||||
.post_stop = cs_dsp_test_post_stop_callback,
|
||||
.watchdog_expired = cs_dsp_test_watchdog_expired_callback,
|
||||
};
|
||||
|
||||
static const struct cs_dsp_client_ops cs_dsp_callback_test_empty_client_ops = {
|
||||
/* No entries */
|
||||
};
|
||||
|
||||
static void cs_dsp_test_run_stop_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
|
||||
cs_dsp_stop(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 1);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
|
||||
cs_dsp_stop(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 2);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 2);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
local->passed_dsp = NULL;
|
||||
}
|
||||
|
||||
static void cs_dsp_test_ctl_v1_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
|
||||
struct cs_dsp_coeff_ctl *ctl;
|
||||
struct firmware *wmfw;
|
||||
int i;
|
||||
|
||||
/* Add a control for each memory */
|
||||
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
|
||||
cs_dsp_callbacks_test_mock_algs[0].id,
|
||||
"dummyalg", NULL);
|
||||
def.shortname = "zm";
|
||||
def.mem_type = WMFW_ADSP2_ZM;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
|
||||
def.shortname = "ym";
|
||||
def.mem_type = WMFW_ADSP2_YM;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
|
||||
def.shortname = "xm";
|
||||
def.mem_type = WMFW_ADSP2_XM;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
|
||||
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
/* There should have been an add callback for each control */
|
||||
KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list), 3);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
|
||||
/*
|
||||
* Call cs_dsp_remove() and there should be a remove callback
|
||||
* for each control
|
||||
*/
|
||||
memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
|
||||
cs_dsp_remove(priv->dsp);
|
||||
|
||||
/* Prevent double cleanup */
|
||||
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, 3);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 3);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_ctl_v2_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
|
||||
struct cs_dsp_coeff_ctl *ctl;
|
||||
struct firmware *wmfw;
|
||||
char name[2] = { };
|
||||
int i;
|
||||
|
||||
/* Add some controls */
|
||||
def.shortname = name;
|
||||
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
|
||||
cs_dsp_callbacks_test_mock_algs[0].id,
|
||||
"dummyalg", NULL);
|
||||
for (i = 0; i < ARRAY_SIZE(local->passed_ctl); ++i) {
|
||||
name[0] = 'A' + i;
|
||||
def.offset_dsp_words = i;
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
}
|
||||
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
/* There should have been an add callback for each control */
|
||||
KUNIT_EXPECT_EQ(test, list_count_nodes(&priv->dsp->ctl_list),
|
||||
ARRAY_SIZE(local->passed_ctl));
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
|
||||
/*
|
||||
* Call cs_dsp_remove() and there should be a remove callback
|
||||
* for each control
|
||||
*/
|
||||
memset(local->passed_ctl, 0, sizeof(local->passed_ctl));
|
||||
cs_dsp_remove(priv->dsp);
|
||||
|
||||
/* Prevent double cleanup */
|
||||
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, ARRAY_SIZE(local->passed_ctl));
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, ARRAY_SIZE(local->passed_ctl));
|
||||
|
||||
i = 0;
|
||||
list_for_each_entry_reverse(ctl, &priv->dsp->ctl_list, list)
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_ctl[i++], ctl);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_no_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct cs_dsp_mock_coeff_def def = mock_coeff_template;
|
||||
struct firmware *wmfw;
|
||||
|
||||
/* Add a controls */
|
||||
def.shortname = "A";
|
||||
cs_dsp_mock_wmfw_start_alg_info_block(local->wmfw_builder,
|
||||
cs_dsp_callbacks_test_mock_algs[0].id,
|
||||
"dummyalg", NULL);
|
||||
cs_dsp_mock_wmfw_add_coeff_desc(local->wmfw_builder, &def);
|
||||
cs_dsp_mock_wmfw_end_alg_info_block(local->wmfw_builder);
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
/* Run a sequence of ops that would invoke callbacks */
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
cs_dsp_stop(priv->dsp);
|
||||
cs_dsp_remove(priv->dsp);
|
||||
|
||||
/* Prevent double cleanup */
|
||||
kunit_remove_action(priv->test, _cs_dsp_remove_wrapper, priv->dsp);
|
||||
|
||||
/* Something went very wrong if any of our callbacks were called */
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_add, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_control_remove, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_run, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_run, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_pre_stop, 0);
|
||||
KUNIT_EXPECT_EQ(test, local->num_post_stop, 0);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_adsp2v2_watchdog_callback(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Set the watchdog timeout bit */
|
||||
regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
|
||||
ADSP2_WDT_TIMEOUT_STS_MASK);
|
||||
|
||||
/* Notify an interrupt and the watchdog callback should be called */
|
||||
cs_dsp_adsp2_bus_error(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_adsp2v2_watchdog_no_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Set the watchdog timeout bit */
|
||||
regmap_write(priv->dsp->regmap, priv->dsp->base + ADSP2_LOCK_REGION_CTRL,
|
||||
ADSP2_WDT_TIMEOUT_STS_MASK);
|
||||
|
||||
/* Notify an interrupt, which will look for a watchdog callback */
|
||||
cs_dsp_adsp2_bus_error(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_halo_watchdog_callback(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Notify an interrupt and the watchdog callback should be called */
|
||||
cs_dsp_halo_wdt_expire(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 1);
|
||||
KUNIT_EXPECT_PTR_EQ(test, local->passed_dsp, priv->dsp);
|
||||
}
|
||||
|
||||
static void cs_dsp_test_halo_watchdog_no_callbacks(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp_test *priv = test->priv;
|
||||
struct cs_dsp_test_local *local = priv->local;
|
||||
struct firmware *wmfw;
|
||||
|
||||
wmfw = cs_dsp_mock_wmfw_get_firmware(local->wmfw_builder);
|
||||
KUNIT_EXPECT_EQ(test,
|
||||
cs_dsp_power_up(priv->dsp, wmfw, "wmfw", NULL, NULL, "misc"),
|
||||
0);
|
||||
KUNIT_EXPECT_EQ(test, cs_dsp_run(priv->dsp), 0);
|
||||
|
||||
/* Notify an interrupt, which will look for a watchdog callback */
|
||||
cs_dsp_halo_wdt_expire(priv->dsp);
|
||||
KUNIT_EXPECT_EQ(test, local->num_watchdog_expired, 0);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_common_init(struct kunit *test, struct cs_dsp *dsp,
|
||||
int wmfw_version)
|
||||
{
|
||||
const struct cs_dsp_callbacks_test_param *param = test->param_value;
|
||||
struct cs_dsp_test *priv;
|
||||
struct cs_dsp_test_local *local;
|
||||
struct device *test_dev;
|
||||
struct cs_dsp_mock_xm_header *xm_header;
|
||||
int ret;
|
||||
|
||||
priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
local = kunit_kzalloc(test, sizeof(struct cs_dsp_test_local), GFP_KERNEL);
|
||||
if (!local)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->test = test;
|
||||
priv->dsp = dsp;
|
||||
test->priv = priv;
|
||||
priv->local = local;
|
||||
|
||||
/* Create dummy struct device */
|
||||
test_dev = kunit_device_register(test, "cs_dsp_test_drv");
|
||||
if (IS_ERR(test_dev))
|
||||
return PTR_ERR(test_dev);
|
||||
|
||||
dsp->dev = get_device(test_dev);
|
||||
if (!dsp->dev)
|
||||
return -ENODEV;
|
||||
|
||||
ret = kunit_add_action_or_reset(test, _put_device_wrapper, dsp->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_set_drvdata(dsp->dev, priv);
|
||||
|
||||
/* Allocate regmap */
|
||||
ret = cs_dsp_mock_regmap_init(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* There must always be a XM header with at least 1 algorithm,
|
||||
* so create a dummy one and pre-populate XM so the wmfw doesn't
|
||||
* have to contain an XM blob.
|
||||
*/
|
||||
xm_header = cs_dsp_create_mock_xm_header(priv,
|
||||
cs_dsp_callbacks_test_mock_algs,
|
||||
ARRAY_SIZE(cs_dsp_callbacks_test_mock_algs));
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xm_header);
|
||||
cs_dsp_mock_xm_header_write_to_regmap(xm_header);
|
||||
|
||||
local->wmfw_builder = cs_dsp_mock_wmfw_init(priv, wmfw_version);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, local->wmfw_builder);
|
||||
|
||||
/* Add dummy XM header payload to wmfw */
|
||||
cs_dsp_mock_wmfw_add_data_block(local->wmfw_builder,
|
||||
WMFW_ADSP2_XM, 0,
|
||||
xm_header->blob_data,
|
||||
xm_header->blob_size_bytes);
|
||||
|
||||
/* Init cs_dsp */
|
||||
dsp->client_ops = param->ops;
|
||||
|
||||
switch (dsp->type) {
|
||||
case WMFW_ADSP2:
|
||||
ret = cs_dsp_adsp2_init(dsp);
|
||||
break;
|
||||
case WMFW_HALO:
|
||||
ret = cs_dsp_halo_init(dsp);
|
||||
break;
|
||||
default:
|
||||
KUNIT_FAIL(test, "Untested DSP type %d\n", dsp->type);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Automatically call cs_dsp_remove() when test case ends */
|
||||
return kunit_add_action_or_reset(priv->test, _cs_dsp_remove_wrapper, dsp);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_halo_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_HALO;
|
||||
dsp->mem = cs_dsp_mock_halo_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_halo_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_halo_core_base;
|
||||
dsp->base_sysinfo = cs_dsp_mock_halo_sysinfo_base;
|
||||
|
||||
return cs_dsp_callbacks_test_common_init(test, dsp, 3);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2_32bit_init(struct kunit *test, int rev)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = rev;
|
||||
dsp->mem = cs_dsp_mock_adsp2_32bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_32bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_32bit_sysbase;
|
||||
|
||||
return cs_dsp_callbacks_test_common_init(test, dsp, 2);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2v2_32bit_init(struct kunit *test)
|
||||
{
|
||||
return cs_dsp_callbacks_test_adsp2_32bit_init(test, 2);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2v1_32bit_init(struct kunit *test)
|
||||
{
|
||||
return cs_dsp_callbacks_test_adsp2_32bit_init(test, 1);
|
||||
}
|
||||
|
||||
static int cs_dsp_callbacks_test_adsp2_16bit_init(struct kunit *test)
|
||||
{
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
/* Fill in cs_dsp and initialize */
|
||||
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
|
||||
if (!dsp)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp->num = 1;
|
||||
dsp->type = WMFW_ADSP2;
|
||||
dsp->rev = 0;
|
||||
dsp->mem = cs_dsp_mock_adsp2_16bit_dsp1_regions;
|
||||
dsp->num_mems = cs_dsp_mock_count_regions(cs_dsp_mock_adsp2_16bit_dsp1_region_sizes);
|
||||
dsp->base = cs_dsp_mock_adsp2_16bit_sysbase;
|
||||
|
||||
return cs_dsp_callbacks_test_common_init(test, dsp, 1);
|
||||
}
|
||||
|
||||
static void cs_dsp_callbacks_param_desc(const struct cs_dsp_callbacks_test_param *param,
|
||||
char *desc)
|
||||
{
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s", param->case_name);
|
||||
}
|
||||
|
||||
/* Parameterize on different client callback ops tables */
|
||||
static const struct cs_dsp_callbacks_test_param cs_dsp_callbacks_ops_cases[] = {
|
||||
{ .ops = &cs_dsp_callback_test_client_ops, .case_name = "all ops" },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(cs_dsp_callbacks_ops,
|
||||
cs_dsp_callbacks_ops_cases,
|
||||
cs_dsp_callbacks_param_desc);
|
||||
|
||||
static const struct cs_dsp_callbacks_test_param cs_dsp_no_callbacks_cases[] = {
|
||||
{ .ops = &cs_dsp_callback_test_empty_client_ops, .case_name = "empty ops" },
|
||||
};
|
||||
|
||||
KUNIT_ARRAY_PARAM(cs_dsp_no_callbacks,
|
||||
cs_dsp_no_callbacks_cases,
|
||||
cs_dsp_callbacks_param_desc);
|
||||
|
||||
static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv1_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v1_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_callbacks_adsp2_wmfwv2_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_callbacks_halo_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_run_stop_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_ctl_v2_callbacks, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_watchdog_adsp2v2_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_adsp2v2_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_case cs_dsp_watchdog_halo_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_callback, cs_dsp_callbacks_ops_gen_params),
|
||||
KUNIT_CASE_PARAM(cs_dsp_test_halo_watchdog_no_callbacks, cs_dsp_no_callbacks_gen_params),
|
||||
|
||||
{ } /* terminator */
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_halo = {
|
||||
.name = "cs_dsp_callbacks_halo",
|
||||
.init = cs_dsp_callbacks_test_halo_init,
|
||||
.test_cases = cs_dsp_callbacks_halo_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_adsp2v2_32bit = {
|
||||
.name = "cs_dsp_callbacks_adsp2v2_32bit_wmfwv2",
|
||||
.init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
|
||||
.test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_adsp2v1_32bit = {
|
||||
.name = "cs_dsp_callbacks_adsp2v1_32bit_wmfwv2",
|
||||
.init = cs_dsp_callbacks_test_adsp2v1_32bit_init,
|
||||
.test_cases = cs_dsp_callbacks_adsp2_wmfwv2_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_callbacks_test_adsp2_16bit = {
|
||||
.name = "cs_dsp_callbacks_adsp2_16bit_wmfwv1",
|
||||
.init = cs_dsp_callbacks_test_adsp2_16bit_init,
|
||||
.test_cases = cs_dsp_callbacks_adsp2_wmfwv1_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_watchdog_test_adsp2v2_32bit = {
|
||||
.name = "cs_dsp_watchdog_adsp2v2_32bit",
|
||||
.init = cs_dsp_callbacks_test_adsp2v2_32bit_init,
|
||||
.test_cases = cs_dsp_watchdog_adsp2v2_test_cases,
|
||||
};
|
||||
|
||||
static struct kunit_suite cs_dsp_watchdog_test_halo_32bit = {
|
||||
.name = "cs_dsp_watchdog_halo",
|
||||
.init = cs_dsp_callbacks_test_halo_init,
|
||||
.test_cases = cs_dsp_watchdog_halo_test_cases,
|
||||
};
|
||||
|
||||
kunit_test_suites(&cs_dsp_callbacks_test_halo,
|
||||
&cs_dsp_callbacks_test_adsp2v2_32bit,
|
||||
&cs_dsp_callbacks_test_adsp2v1_32bit,
|
||||
&cs_dsp_callbacks_test_adsp2_16bit,
|
||||
&cs_dsp_watchdog_test_adsp2v2_32bit,
|
||||
&cs_dsp_watchdog_test_halo_32bit);
|
3282
drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c
Normal file
3282
drivers/firmware/cirrus/test/cs_dsp_test_control_cache.c
Normal file
File diff suppressed because it is too large
Load Diff
1851
drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c
Normal file
1851
drivers/firmware/cirrus/test/cs_dsp_test_control_parse.c
Normal file
File diff suppressed because it is too large
Load Diff
2669
drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c
Normal file
2669
drivers/firmware/cirrus/test/cs_dsp_test_control_rw.c
Normal file
File diff suppressed because it is too large
Load Diff
2211
drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c
Normal file
2211
drivers/firmware/cirrus/test/cs_dsp_test_wmfw.c
Normal file
File diff suppressed because it is too large
Load Diff
1347
drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c
Normal file
1347
drivers/firmware/cirrus/test/cs_dsp_test_wmfw_error.c
Normal file
File diff suppressed because it is too large
Load Diff
14
drivers/firmware/cirrus/test/cs_dsp_tests.c
Normal file
14
drivers/firmware/cirrus/test/cs_dsp_tests.c
Normal file
@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
// Utility module for cs_dsp KUnit testing.
|
||||
//
|
||||
// Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
// Cirrus Logic International Semiconductor Ltd.
|
||||
|
||||
#include <linux/module.h>
|
||||
|
||||
MODULE_DESCRIPTION("KUnit tests for Cirrus Logic DSP driver");
|
||||
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS("FW_CS_DSP");
|
||||
MODULE_IMPORT_NS("FW_CS_DSP_KUNIT_TEST_UTILS");
|
@ -10,6 +10,5 @@
|
||||
#define AIF3_PB 4
|
||||
#define AIF3_CAP 5
|
||||
#define AIF4_PB 6
|
||||
#define NUM_CODEC_DAIS 7
|
||||
|
||||
#endif
|
||||
|
160
include/linux/firmware/cirrus/cs_dsp_test_utils.h
Normal file
160
include/linux/firmware/cirrus/cs_dsp_test_utils.h
Normal file
@ -0,0 +1,160 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Support utilities for cs_dsp testing.
|
||||
*
|
||||
* Copyright (C) 2024 Cirrus Logic, Inc. and
|
||||
* Cirrus Logic International Semiconductor Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/firmware/cirrus/wmfw.h>
|
||||
|
||||
struct kunit;
|
||||
struct cs_dsp_test;
|
||||
struct cs_dsp_test_local;
|
||||
|
||||
/**
|
||||
* struct cs_dsp_test - base class for test utilities
|
||||
*
|
||||
* @test: Pointer to struct kunit instance.
|
||||
* @dsp: Pointer to struct cs_dsp instance.
|
||||
* @local: Private data for each test suite.
|
||||
*/
|
||||
struct cs_dsp_test {
|
||||
struct kunit *test;
|
||||
struct cs_dsp *dsp;
|
||||
|
||||
struct cs_dsp_test_local *local;
|
||||
|
||||
/* Following members are private */
|
||||
bool saw_bus_write;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cs_dsp_mock_alg_def - Info for creating a mock algorithm entry.
|
||||
*
|
||||
* @id Algorithm ID.
|
||||
* @ver; Algorithm version.
|
||||
* @xm_base_words XM base address in DSP words.
|
||||
* @xm_size_words XM size in DSP words.
|
||||
* @ym_base_words YM base address in DSP words.
|
||||
* @ym_size_words YM size in DSP words.
|
||||
* @zm_base_words ZM base address in DSP words.
|
||||
* @zm_size_words ZM size in DSP words.
|
||||
*/
|
||||
struct cs_dsp_mock_alg_def {
|
||||
unsigned int id;
|
||||
unsigned int ver;
|
||||
unsigned int xm_base_words;
|
||||
unsigned int xm_size_words;
|
||||
unsigned int ym_base_words;
|
||||
unsigned int ym_size_words;
|
||||
unsigned int zm_base_words;
|
||||
unsigned int zm_size_words;
|
||||
};
|
||||
|
||||
struct cs_dsp_mock_coeff_def {
|
||||
const char *shortname;
|
||||
const char *fullname;
|
||||
const char *description;
|
||||
u16 type;
|
||||
u16 flags;
|
||||
u16 mem_type;
|
||||
unsigned int offset_dsp_words;
|
||||
unsigned int length_bytes;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cs_dsp_mock_xm_header - XM header builder
|
||||
*
|
||||
* @test_priv: Pointer to the struct cs_dsp_test.
|
||||
* @blob_data: Pointer to the created blob data.
|
||||
* @blob_size_bytes: Size of the data at blob_data.
|
||||
*/
|
||||
struct cs_dsp_mock_xm_header {
|
||||
struct cs_dsp_test *test_priv;
|
||||
void *blob_data;
|
||||
size_t blob_size_bytes;
|
||||
};
|
||||
|
||||
struct cs_dsp_mock_wmfw_builder;
|
||||
struct cs_dsp_mock_bin_builder;
|
||||
|
||||
extern const unsigned int cs_dsp_mock_adsp2_32bit_sysbase;
|
||||
extern const unsigned int cs_dsp_mock_adsp2_16bit_sysbase;
|
||||
extern const unsigned int cs_dsp_mock_halo_core_base;
|
||||
extern const unsigned int cs_dsp_mock_halo_sysinfo_base;
|
||||
|
||||
extern const struct cs_dsp_region cs_dsp_mock_halo_dsp1_regions[];
|
||||
extern const unsigned int cs_dsp_mock_halo_dsp1_region_sizes[];
|
||||
extern const struct cs_dsp_region cs_dsp_mock_adsp2_32bit_dsp1_regions[];
|
||||
extern const unsigned int cs_dsp_mock_adsp2_32bit_dsp1_region_sizes[];
|
||||
extern const struct cs_dsp_region cs_dsp_mock_adsp2_16bit_dsp1_regions[];
|
||||
extern const unsigned int cs_dsp_mock_adsp2_16bit_dsp1_region_sizes[];
|
||||
int cs_dsp_mock_count_regions(const unsigned int *region_sizes);
|
||||
unsigned int cs_dsp_mock_size_of_region(const struct cs_dsp *dsp, int mem_type);
|
||||
unsigned int cs_dsp_mock_base_addr_for_mem(struct cs_dsp_test *priv, int mem_type);
|
||||
unsigned int cs_dsp_mock_reg_addr_inc_per_unpacked_word(struct cs_dsp_test *priv);
|
||||
unsigned int cs_dsp_mock_reg_block_length_bytes(struct cs_dsp_test *priv, int mem_type);
|
||||
unsigned int cs_dsp_mock_reg_block_length_registers(struct cs_dsp_test *priv, int mem_type);
|
||||
unsigned int cs_dsp_mock_reg_block_length_dsp_words(struct cs_dsp_test *priv, int mem_type);
|
||||
bool cs_dsp_mock_has_zm(struct cs_dsp_test *priv);
|
||||
int cs_dsp_mock_packed_to_unpacked_mem_type(int packed_mem_type);
|
||||
unsigned int cs_dsp_mock_num_dsp_words_to_num_packed_regs(unsigned int num_dsp_words);
|
||||
unsigned int cs_dsp_mock_xm_header_get_alg_base_in_words(struct cs_dsp_test *priv,
|
||||
unsigned int alg_id,
|
||||
int mem_type);
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version_from_regmap(struct cs_dsp_test *priv);
|
||||
unsigned int cs_dsp_mock_xm_header_get_fw_version(struct cs_dsp_mock_xm_header *header);
|
||||
void cs_dsp_mock_xm_header_drop_from_regmap_cache(struct cs_dsp_test *priv);
|
||||
int cs_dsp_mock_xm_header_write_to_regmap(struct cs_dsp_mock_xm_header *header);
|
||||
struct cs_dsp_mock_xm_header *cs_dsp_create_mock_xm_header(struct cs_dsp_test *priv,
|
||||
const struct cs_dsp_mock_alg_def *algs,
|
||||
size_t num_algs);
|
||||
|
||||
int cs_dsp_mock_regmap_init(struct cs_dsp_test *priv);
|
||||
void cs_dsp_mock_regmap_drop_range(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, unsigned int last_reg);
|
||||
void cs_dsp_mock_regmap_drop_regs(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_regs);
|
||||
void cs_dsp_mock_regmap_drop_bytes(struct cs_dsp_test *priv,
|
||||
unsigned int first_reg, size_t num_bytes);
|
||||
void cs_dsp_mock_regmap_drop_system_regs(struct cs_dsp_test *priv);
|
||||
bool cs_dsp_mock_regmap_is_dirty(struct cs_dsp_test *priv, bool drop_system_regs);
|
||||
|
||||
struct cs_dsp_mock_bin_builder *cs_dsp_mock_bin_init(struct cs_dsp_test *priv,
|
||||
int format_version,
|
||||
unsigned int fw_version);
|
||||
void cs_dsp_mock_bin_add_raw_block(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int type, unsigned int offset,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
void cs_dsp_mock_bin_add_info(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *info);
|
||||
void cs_dsp_mock_bin_add_name(struct cs_dsp_mock_bin_builder *builder,
|
||||
const char *name);
|
||||
void cs_dsp_mock_bin_add_patch(struct cs_dsp_mock_bin_builder *builder,
|
||||
unsigned int alg_id, unsigned int alg_ver,
|
||||
int mem_region, unsigned int reg_addr_offset,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
struct firmware *cs_dsp_mock_bin_get_firmware(struct cs_dsp_mock_bin_builder *builder);
|
||||
|
||||
struct cs_dsp_mock_wmfw_builder *cs_dsp_mock_wmfw_init(struct cs_dsp_test *priv,
|
||||
int format_version);
|
||||
void cs_dsp_mock_wmfw_add_raw_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int mem_region, unsigned int mem_offset_dsp_words,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
void cs_dsp_mock_wmfw_add_info(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const char *info);
|
||||
void cs_dsp_mock_wmfw_add_data_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
int mem_region, unsigned int mem_offset_dsp_words,
|
||||
const void *payload_data, size_t payload_len_bytes);
|
||||
void cs_dsp_mock_wmfw_start_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
unsigned int alg_id,
|
||||
const char *name,
|
||||
const char *description);
|
||||
void cs_dsp_mock_wmfw_add_coeff_desc(struct cs_dsp_mock_wmfw_builder *builder,
|
||||
const struct cs_dsp_mock_coeff_def *def);
|
||||
void cs_dsp_mock_wmfw_end_alg_info_block(struct cs_dsp_mock_wmfw_builder *builder);
|
||||
struct firmware *cs_dsp_mock_wmfw_get_firmware(struct cs_dsp_mock_wmfw_builder *builder);
|
||||
int cs_dsp_mock_wmfw_format_version(struct cs_dsp_mock_wmfw_builder *builder);
|
@ -224,6 +224,8 @@ int asoc_sdw_cs_amp_init(struct snd_soc_card *card,
|
||||
struct snd_soc_dai_link *dai_links,
|
||||
struct asoc_sdw_codec_info *info,
|
||||
bool playback);
|
||||
int asoc_sdw_cs_spk_feedback_rtd_init(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_soc_dai *dai);
|
||||
|
||||
/* MAXIM codec support */
|
||||
int asoc_sdw_maxim_init(struct snd_soc_card *card,
|
||||
|
@ -334,6 +334,14 @@ union snd_codec_options {
|
||||
struct snd_dec_wma wma_d;
|
||||
struct snd_dec_alac alac_d;
|
||||
struct snd_dec_ape ape_d;
|
||||
struct {
|
||||
__u32 out_sample_rate;
|
||||
} src_d;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
struct snd_codec_desc_src {
|
||||
__u32 out_sample_rate_min;
|
||||
__u32 out_sample_rate_max;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/** struct snd_codec_desc - description of codec capabilities
|
||||
@ -347,6 +355,9 @@ union snd_codec_options {
|
||||
* @modes: Supported modes. See SND_AUDIOMODE defines
|
||||
* @formats: Supported formats. See SND_AUDIOSTREAMFORMAT defines
|
||||
* @min_buffer: Minimum buffer size handled by codec implementation
|
||||
* @pcm_formats: Output (for decoders) or input (for encoders)
|
||||
* PCM formats (required to accel mode, 0 for other modes)
|
||||
* @u_space: union space (for codec dependent data)
|
||||
* @reserved: reserved for future use
|
||||
*
|
||||
* This structure provides a scalar value for profiles, modes and stream
|
||||
@ -370,7 +381,12 @@ struct snd_codec_desc {
|
||||
__u32 modes;
|
||||
__u32 formats;
|
||||
__u32 min_buffer;
|
||||
__u32 reserved[15];
|
||||
__u32 pcm_formats;
|
||||
union {
|
||||
__u32 u_space[6];
|
||||
struct snd_codec_desc_src src;
|
||||
} __attribute__((packed, aligned(4)));
|
||||
__u32 reserved[8];
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
/** struct snd_codec
|
||||
@ -395,6 +411,8 @@ struct snd_codec_desc {
|
||||
* @align: Block alignment in bytes of an audio sample.
|
||||
* Only required for PCM or IEC formats.
|
||||
* @options: encoder-specific settings
|
||||
* @pcm_format: Output (for decoders) or input (for encoders)
|
||||
* PCM formats (required to accel mode, 0 for other modes)
|
||||
* @reserved: reserved for future use
|
||||
*/
|
||||
|
||||
@ -411,7 +429,8 @@ struct snd_codec {
|
||||
__u32 format;
|
||||
__u32 align;
|
||||
union snd_codec_options options;
|
||||
__u32 reserved[3];
|
||||
__u32 pcm_format;
|
||||
__u32 reserved[2];
|
||||
} __attribute__((packed, aligned(4)));
|
||||
|
||||
#endif
|
||||
|
@ -153,6 +153,8 @@
|
||||
/* Stream */
|
||||
#define SOF_TKN_STREAM_PLAYBACK_COMPATIBLE_D0I3 1200
|
||||
#define SOF_TKN_STREAM_CAPTURE_COMPATIBLE_D0I3 1201
|
||||
#define SOF_TKN_STREAM_PLAYBACK_PAUSE_SUPPORTED 1202
|
||||
#define SOF_TKN_STREAM_CAPTURE_PAUSE_SUPPORTED 1203
|
||||
|
||||
/* Led control for mute switches */
|
||||
#define SOF_TKN_MUTE_LED_USE 1300
|
||||
|
@ -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)
|
||||
|
@ -80,7 +80,7 @@ snd-soc-cs35l56-shared-y := cs35l56-shared.o
|
||||
snd-soc-cs35l56-i2c-y := cs35l56-i2c.o
|
||||
snd-soc-cs35l56-spi-y := cs35l56-spi.o
|
||||
snd-soc-cs35l56-sdw-y := cs35l56-sdw.o
|
||||
snd-soc-cs40l50-objs := cs40l50-codec.o
|
||||
snd-soc-cs40l50-y := cs40l50-codec.o
|
||||
snd-soc-cs42l42-y := cs42l42.o
|
||||
snd-soc-cs42l42-i2c-y := cs42l42-i2c.o
|
||||
snd-soc-cs42l42-sdw-y := cs42l42-sdw.o
|
||||
@ -92,7 +92,7 @@ snd-soc-cs42l52-y := cs42l52.o
|
||||
snd-soc-cs42l56-y := cs42l56.o
|
||||
snd-soc-cs42l73-y := cs42l73.o
|
||||
snd-soc-cs42l83-i2c-y := cs42l83-i2c.o
|
||||
snd-soc-cs42l84-objs := cs42l84.o
|
||||
snd-soc-cs42l84-y := cs42l84.o
|
||||
snd-soc-cs4234-y := cs4234.o
|
||||
snd-soc-cs4265-y := cs4265.o
|
||||
snd-soc-cs4270-y := cs4270.o
|
||||
@ -334,8 +334,8 @@ snd-soc-wcd-classh-y := wcd-clsh-v2.o
|
||||
snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
|
||||
snd-soc-wcd9335-y := wcd9335.o
|
||||
snd-soc-wcd934x-y := wcd934x.o
|
||||
snd-soc-wcd937x-objs := wcd937x.o
|
||||
snd-soc-wcd937x-sdw-objs := wcd937x-sdw.o
|
||||
snd-soc-wcd937x-y := wcd937x.o
|
||||
snd-soc-wcd937x-sdw-y := wcd937x-sdw.o
|
||||
snd-soc-wcd938x-y := wcd938x.o
|
||||
snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
|
||||
snd-soc-wcd939x-y := wcd939x.o
|
||||
|
@ -23,7 +23,6 @@ MODULE_DEVICE_TABLE(i2c, ad193x_id);
|
||||
static int ad193x_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap_config config;
|
||||
const struct i2c_device_id *id = i2c_match_id(ad193x_id, client);
|
||||
|
||||
config = ad193x_regmap_config;
|
||||
config.val_bits = 8;
|
||||
@ -31,7 +30,7 @@ static int ad193x_i2c_probe(struct i2c_client *client)
|
||||
|
||||
return ad193x_probe(&client->dev,
|
||||
devm_regmap_init_i2c(client, &config),
|
||||
(enum ad193x_type)id->driver_data);
|
||||
(uintptr_t)i2c_get_match_data(client));
|
||||
}
|
||||
|
||||
static struct i2c_driver ad193x_i2c_driver = {
|
||||
|
@ -14,12 +14,9 @@
|
||||
|
||||
#include "adau1761.h"
|
||||
|
||||
static const struct i2c_device_id adau1761_i2c_ids[];
|
||||
|
||||
static int adau1761_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap_config config;
|
||||
const struct i2c_device_id *id = i2c_match_id(adau1761_i2c_ids, client);
|
||||
|
||||
config = adau1761_regmap_config;
|
||||
config.val_bits = 8;
|
||||
@ -27,7 +24,7 @@ static int adau1761_i2c_probe(struct i2c_client *client)
|
||||
|
||||
return adau1761_probe(&client->dev,
|
||||
devm_regmap_init_i2c(client, &config),
|
||||
id->driver_data, NULL);
|
||||
(uintptr_t)i2c_get_match_data(client), NULL);
|
||||
}
|
||||
|
||||
static void adau1761_i2c_remove(struct i2c_client *client)
|
||||
|
@ -14,12 +14,9 @@
|
||||
|
||||
#include "adau1781.h"
|
||||
|
||||
static const struct i2c_device_id adau1781_i2c_ids[];
|
||||
|
||||
static int adau1781_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap_config config;
|
||||
const struct i2c_device_id *id = i2c_match_id(adau1781_i2c_ids, client);
|
||||
|
||||
config = adau1781_regmap_config;
|
||||
config.val_bits = 8;
|
||||
@ -27,7 +24,7 @@ static int adau1781_i2c_probe(struct i2c_client *client)
|
||||
|
||||
return adau1781_probe(&client->dev,
|
||||
devm_regmap_init_i2c(client, &config),
|
||||
id->driver_data, NULL);
|
||||
(uintptr_t)i2c_get_match_data(client), NULL);
|
||||
}
|
||||
|
||||
static void adau1781_i2c_remove(struct i2c_client *client)
|
||||
|
@ -14,12 +14,9 @@
|
||||
|
||||
#include "adau1977.h"
|
||||
|
||||
static const struct i2c_device_id adau1977_i2c_ids[];
|
||||
|
||||
static int adau1977_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
struct regmap_config config;
|
||||
const struct i2c_device_id *id = i2c_match_id(adau1977_i2c_ids, client);
|
||||
|
||||
config = adau1977_regmap_config;
|
||||
config.val_bits = 8;
|
||||
@ -27,7 +24,7 @@ static int adau1977_i2c_probe(struct i2c_client *client)
|
||||
|
||||
return adau1977_probe(&client->dev,
|
||||
devm_regmap_init_i2c(client, &config),
|
||||
id->driver_data, NULL);
|
||||
(uintptr_t)i2c_get_match_data(client), NULL);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adau1977_i2c_ids[] = {
|
||||
|
@ -987,9 +987,9 @@ static int alc5623_i2c_probe(struct i2c_client *client)
|
||||
struct alc5623_priv *alc5623;
|
||||
struct device_node *np;
|
||||
unsigned int vid1, vid2;
|
||||
unsigned int matched_id;
|
||||
int ret;
|
||||
u32 val32;
|
||||
const struct i2c_device_id *id;
|
||||
|
||||
alc5623 = devm_kzalloc(&client->dev, sizeof(struct alc5623_priv),
|
||||
GFP_KERNEL);
|
||||
@ -1016,12 +1016,12 @@ static int alc5623_i2c_probe(struct i2c_client *client)
|
||||
}
|
||||
vid2 >>= 8;
|
||||
|
||||
id = i2c_match_id(alc5623_i2c_table, client);
|
||||
matched_id = (uintptr_t)i2c_get_match_data(client);
|
||||
|
||||
if ((vid1 != 0x10ec) || (vid2 != id->driver_data)) {
|
||||
if ((vid1 != 0x10ec) || (vid2 != matched_id)) {
|
||||
dev_err(&client->dev, "unknown or wrong codec\n");
|
||||
dev_err(&client->dev, "Expected %x:%lx, got %x:%x\n",
|
||||
0x10ec, id->driver_data,
|
||||
dev_err(&client->dev, "Expected %x:%x, got %x:%x\n",
|
||||
0x10ec, matched_id,
|
||||
vid1, vid2);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1108,7 +1108,7 @@ static int alc5632_i2c_probe(struct i2c_client *client)
|
||||
struct alc5632_priv *alc5632;
|
||||
int ret, ret1, ret2;
|
||||
unsigned int vid1, vid2;
|
||||
const struct i2c_device_id *id;
|
||||
unsigned int matched_id;
|
||||
|
||||
alc5632 = devm_kzalloc(&client->dev,
|
||||
sizeof(struct alc5632_priv), GFP_KERNEL);
|
||||
@ -1134,9 +1134,9 @@ static int alc5632_i2c_probe(struct i2c_client *client)
|
||||
|
||||
vid2 >>= 8;
|
||||
|
||||
id = i2c_match_id(alc5632_i2c_table, client);
|
||||
matched_id = (uintptr_t)i2c_get_match_data(client);
|
||||
|
||||
if ((vid1 != 0x10EC) || (vid2 != id->driver_data)) {
|
||||
if ((vid1 != 0x10EC) || (vid2 != matched_id)) {
|
||||
dev_err(&client->dev,
|
||||
"Device is not a ALC5632: VID1=0x%x, VID2=0x%x\n", vid1, vid2);
|
||||
return -EINVAL;
|
||||
|
@ -646,6 +646,12 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
|
||||
.rates = CS35L56_RATES,
|
||||
.formats = CS35L56_RX_FORMATS,
|
||||
},
|
||||
.symmetric_rate = 1,
|
||||
.ops = &cs35l56_sdw_dai_ops,
|
||||
},
|
||||
{
|
||||
.name = "cs35l56-sdw1c",
|
||||
.id = 2,
|
||||
.capture = {
|
||||
.stream_name = "SDW1 Capture",
|
||||
.channels_min = 1,
|
||||
@ -655,7 +661,7 @@ static struct snd_soc_dai_driver cs35l56_dai[] = {
|
||||
},
|
||||
.symmetric_rate = 1,
|
||||
.ops = &cs35l56_sdw_dai_ops,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static int cs35l56_write_cal(struct cs35l56_private *cs35l56)
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/find.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
@ -13,9 +13,9 @@
|
||||
|
||||
#include "cs42l51.h"
|
||||
|
||||
static struct i2c_device_id cs42l51_i2c_id[] = {
|
||||
{"cs42l51"},
|
||||
{}
|
||||
static const struct i2c_device_id cs42l51_i2c_id[] = {
|
||||
{ "cs42l51" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
|
||||
|
||||
|
@ -1087,7 +1087,7 @@ static const struct of_device_id cs42l84_of_match[] = {
|
||||
MODULE_DEVICE_TABLE(of, cs42l84_of_match);
|
||||
|
||||
static const struct i2c_device_id cs42l84_id[] = {
|
||||
{"cs42l84", 0},
|
||||
{ "cs42l84" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cs42l84_id);
|
||||
|
@ -758,7 +758,7 @@ static int es8323_i2c_probe(struct i2c_client *i2c_client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id es8323_i2c_id[] = {
|
||||
{ "es8323", 0 },
|
||||
{ "es8323" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);
|
||||
|
@ -1731,7 +1731,6 @@ MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
|
||||
static int max98088_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct max98088_priv *max98088;
|
||||
const struct i2c_device_id *id;
|
||||
|
||||
max98088 = devm_kzalloc(&i2c->dev, sizeof(struct max98088_priv),
|
||||
GFP_KERNEL);
|
||||
@ -1747,8 +1746,7 @@ static int max98088_i2c_probe(struct i2c_client *i2c)
|
||||
if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER)
|
||||
return PTR_ERR(max98088->mclk);
|
||||
|
||||
id = i2c_match_id(max98088_i2c_id, i2c);
|
||||
max98088->devtype = id->driver_data;
|
||||
max98088->devtype = (uintptr_t)i2c_get_match_data(i2c);
|
||||
|
||||
i2c_set_clientdata(i2c, max98088);
|
||||
max98088->pdata = i2c->dev.platform_data;
|
||||
|
@ -2543,8 +2543,6 @@ MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
|
||||
static int max98090_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct max98090_priv *max98090;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
kernel_ulong_t driver_data = 0;
|
||||
int ret;
|
||||
|
||||
pr_debug("max98090_i2c_probe\n");
|
||||
@ -2554,21 +2552,7 @@ static int max98090_i2c_probe(struct i2c_client *i2c)
|
||||
if (max98090 == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (ACPI_HANDLE(&i2c->dev)) {
|
||||
acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
|
||||
&i2c->dev);
|
||||
if (!acpi_id) {
|
||||
dev_err(&i2c->dev, "No driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
driver_data = acpi_id->driver_data;
|
||||
} else {
|
||||
const struct i2c_device_id *i2c_id =
|
||||
i2c_match_id(max98090_i2c_id, i2c);
|
||||
driver_data = i2c_id->driver_data;
|
||||
}
|
||||
|
||||
max98090->devtype = driver_data;
|
||||
max98090->devtype = (uintptr_t)i2c_get_match_data(i2c);
|
||||
i2c_set_clientdata(i2c, max98090);
|
||||
max98090->pdata = i2c->dev.platform_data;
|
||||
|
||||
|
@ -2115,7 +2115,6 @@ static int max98095_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct max98095_priv *max98095;
|
||||
int ret;
|
||||
const struct i2c_device_id *id;
|
||||
|
||||
max98095 = devm_kzalloc(&i2c->dev, sizeof(struct max98095_priv),
|
||||
GFP_KERNEL);
|
||||
@ -2131,8 +2130,7 @@ static int max98095_i2c_probe(struct i2c_client *i2c)
|
||||
return ret;
|
||||
}
|
||||
|
||||
id = i2c_match_id(max98095_i2c_id, i2c);
|
||||
max98095->devtype = id->driver_data;
|
||||
max98095->devtype = (uintptr_t)i2c_get_match_data(i2c);
|
||||
i2c_set_clientdata(i2c, max98095);
|
||||
max98095->pdata = i2c->dev.platform_data;
|
||||
|
||||
|
@ -454,7 +454,7 @@ static int ntp8835_i2c_probe(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ntp8835_i2c_id[] = {
|
||||
{ "ntp8835", 0 },
|
||||
{ "ntp8835" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id);
|
||||
|
@ -371,7 +371,7 @@ static int ntp8918_i2c_probe(struct i2c_client *i2c)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ntp8918_i2c_id[] = {
|
||||
{ "ntp8918", 0 },
|
||||
{ "ntp8918" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);
|
||||
|
@ -33,8 +33,7 @@ MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
|
||||
|
||||
static int pcm186x_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_match_id(pcm186x_i2c_id, i2c);
|
||||
const enum pcm186x_type type = (enum pcm186x_type)id->driver_data;
|
||||
const enum pcm186x_type type = (uintptr_t)i2c_get_match_data(i2c);
|
||||
int irq = i2c->irq;
|
||||
struct regmap *regmap;
|
||||
|
||||
|
@ -2059,7 +2059,6 @@ static char *str_to_upper(char *str)
|
||||
|
||||
static int pcmdevice_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_match_id(pcmdevice_i2c_id, i2c);
|
||||
struct pcmdevice_priv *pcm_dev;
|
||||
struct device_node *np;
|
||||
unsigned int dev_addrs[PCMDEVICE_MAX_I2C_DEVICES];
|
||||
@ -2069,7 +2068,7 @@ static int pcmdevice_i2c_probe(struct i2c_client *i2c)
|
||||
if (!pcm_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
pcm_dev->chip_id = (id != NULL) ? id->driver_data : 0;
|
||||
pcm_dev->chip_id = (uintptr_t)i2c_get_match_data(i2c);
|
||||
|
||||
pcm_dev->dev = &i2c->dev;
|
||||
pcm_dev->client = i2c;
|
||||
|
@ -372,47 +372,6 @@ static const struct regmap_config rt715_sdw_regmap = {
|
||||
.use_single_write = true,
|
||||
};
|
||||
|
||||
int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload,
|
||||
unsigned int *sdw_addr_h, unsigned int *sdw_data_h,
|
||||
unsigned int *sdw_addr_l, unsigned int *sdw_data_l)
|
||||
{
|
||||
unsigned int offset_h, offset_l, e_verb;
|
||||
|
||||
if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */
|
||||
if (verb == 0x7ff) /* special case */
|
||||
offset_h = 0;
|
||||
else
|
||||
offset_h = 0x3000;
|
||||
|
||||
if (verb & 0x800) /* get command */
|
||||
e_verb = (verb - 0xf00) | 0x80;
|
||||
else /* set command */
|
||||
e_verb = (verb - 0x700);
|
||||
|
||||
*sdw_data_h = payload; /* 7 bits payload */
|
||||
*sdw_addr_l = *sdw_data_l = 0;
|
||||
} else { /* 4 bits command */
|
||||
if ((verb & 0x800) == 0x800) { /* read */
|
||||
offset_h = 0x9000;
|
||||
offset_l = 0xa000;
|
||||
} else { /* write */
|
||||
offset_h = 0x7000;
|
||||
offset_l = 0x8000;
|
||||
}
|
||||
e_verb = verb >> 8;
|
||||
*sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */
|
||||
*sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */
|
||||
*sdw_addr_l += offset_l;
|
||||
*sdw_data_l = payload & 0xff;
|
||||
}
|
||||
|
||||
*sdw_addr_h = (e_verb << 8) | nid;
|
||||
*sdw_addr_h += offset_h;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hda_to_sdw);
|
||||
|
||||
static int rt715_update_status(struct sdw_slave *slave,
|
||||
enum sdw_slave_status status)
|
||||
{
|
||||
|
@ -220,8 +220,5 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave);
|
||||
int rt715_init(struct device *dev, struct regmap *sdw_regmap,
|
||||
struct regmap *regmap, struct sdw_slave *slave);
|
||||
|
||||
int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload,
|
||||
unsigned int *sdw_addr_h, unsigned int *sdw_data_h,
|
||||
unsigned int *sdw_addr_l, unsigned int *sdw_data_l);
|
||||
int rt715_clock_config(struct device *dev);
|
||||
#endif /* __RT715_H__ */
|
||||
|
@ -2011,8 +2011,8 @@ static void sma1307_i2c_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
static const struct i2c_device_id sma1307_i2c_id[] = {
|
||||
{ "sma1307a", 0 },
|
||||
{ "sma1307aq", 0 },
|
||||
{ "sma1307a" },
|
||||
{ "sma1307aq" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -13,8 +13,6 @@
|
||||
|
||||
#include "ssm2602.h"
|
||||
|
||||
static const struct i2c_device_id ssm2602_i2c_id[];
|
||||
|
||||
/*
|
||||
* ssm2602 2 wire address is determined by GPIO5
|
||||
* state during powerup.
|
||||
@ -23,8 +21,7 @@ static const struct i2c_device_id ssm2602_i2c_id[];
|
||||
*/
|
||||
static int ssm2602_i2c_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_match_id(ssm2602_i2c_id, client);
|
||||
return ssm2602_probe(&client->dev, id->driver_data,
|
||||
return ssm2602_probe(&client->dev, (uintptr_t)i2c_get_match_data(client),
|
||||
devm_regmap_init_i2c(client, &ssm2602_regmap_config));
|
||||
}
|
||||
|
||||
|
@ -731,16 +731,14 @@ static int tas2562_probe(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
struct tas2562_data *data;
|
||||
int ret;
|
||||
const struct i2c_device_id *id;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
id = i2c_match_id(tas2562_id, client);
|
||||
data->client = client;
|
||||
data->dev = &client->dev;
|
||||
data->model_id = id->driver_data;
|
||||
data->model_id = (uintptr_t)i2c_get_match_data(client);
|
||||
|
||||
tas2562_parse_dt(data);
|
||||
|
||||
|
@ -489,14 +489,11 @@ static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
|
||||
struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
|
||||
const int sum = ARRAY_SIZE(tas2563_cali_start_reg);
|
||||
int rc = 1;
|
||||
int i, j;
|
||||
|
||||
guard(mutex)(&tas_priv->codec_lock);
|
||||
if (tas_priv->chip_id != TAS2563) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
if (tas_priv->chip_id != TAS2563)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < tas_priv->ndev; i++) {
|
||||
struct tasdevice *tasdev = tas_priv->tasdevice;
|
||||
@ -523,8 +520,8 @@ static int tas2563_calib_start_put(struct snd_kcontrol *kcontrol,
|
||||
q[j].val, 4);
|
||||
}
|
||||
}
|
||||
out:
|
||||
return rc;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
|
||||
@ -576,7 +573,7 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
|
||||
struct cali_reg *p = &cali_data->cali_reg_array;
|
||||
unsigned char *src = ucontrol->value.bytes.data;
|
||||
unsigned char *dst = cali_data->data;
|
||||
int rc = 1, i = 0;
|
||||
int i = 0;
|
||||
int j;
|
||||
|
||||
guard(mutex)(&priv->codec_lock);
|
||||
@ -605,7 +602,7 @@ static int tasdev_cali_data_put(struct snd_kcontrol *kcontrol,
|
||||
i += 3;
|
||||
|
||||
memcpy(dst, &src[i], cali_data->total_sz);
|
||||
return rc;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int tas2781_latch_reg_get(struct snd_kcontrol *kcontrol,
|
||||
@ -1115,25 +1112,21 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
|
||||
char *conf_name, *prog_name;
|
||||
int nr_controls = 4;
|
||||
int mix_index = 0;
|
||||
int ret;
|
||||
|
||||
/* Alloc kcontrol via devm_kzalloc, which don't manually
|
||||
* free the kcontrol
|
||||
*/
|
||||
dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls,
|
||||
sizeof(dsp_ctrls[0]), GFP_KERNEL);
|
||||
if (!dsp_ctrls) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!dsp_ctrls)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create mixer items for selecting the active Program and Config */
|
||||
prog_name = devm_kstrdup(tas_priv->dev, "Speaker Program Id",
|
||||
GFP_KERNEL);
|
||||
if (!prog_name) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!prog_name)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp_ctrls[mix_index].name = prog_name;
|
||||
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
dsp_ctrls[mix_index].info = tasdevice_info_programs;
|
||||
@ -1143,10 +1136,9 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
|
||||
|
||||
conf_name = devm_kstrdup(tas_priv->dev, "Speaker Config Id",
|
||||
GFP_KERNEL);
|
||||
if (!conf_name) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!conf_name)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp_ctrls[mix_index].name = conf_name;
|
||||
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
dsp_ctrls[mix_index].info = tasdevice_info_configurations;
|
||||
@ -1156,10 +1148,9 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
|
||||
|
||||
active_dev_num = devm_kstrdup(tas_priv->dev, "Activate Tasdevice Num",
|
||||
GFP_KERNEL);
|
||||
if (!active_dev_num) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!active_dev_num)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp_ctrls[mix_index].name = active_dev_num;
|
||||
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
dsp_ctrls[mix_index].info = tasdevice_info_active_num;
|
||||
@ -1168,21 +1159,17 @@ static int tasdevice_dsp_create_ctrls(struct tasdevice_priv *tas_priv)
|
||||
mix_index++;
|
||||
|
||||
chip_id = devm_kstrdup(tas_priv->dev, "Tasdevice Chip Id", GFP_KERNEL);
|
||||
if (!chip_id) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!chip_id)
|
||||
return -ENOMEM;
|
||||
|
||||
dsp_ctrls[mix_index].name = chip_id;
|
||||
dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
dsp_ctrls[mix_index].info = tasdevice_info_chip_id;
|
||||
dsp_ctrls[mix_index].get = tasdevice_get_chip_id;
|
||||
mix_index++;
|
||||
|
||||
ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
|
||||
return snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls,
|
||||
nr_controls < mix_index ? nr_controls : mix_index);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
|
||||
@ -1469,7 +1456,6 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream,
|
||||
unsigned int slot_width;
|
||||
unsigned int fsrate;
|
||||
int bclk_rate;
|
||||
int rc = 0;
|
||||
|
||||
fsrate = params_rate(params);
|
||||
switch (fsrate) {
|
||||
@ -1479,8 +1465,7 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream,
|
||||
default:
|
||||
dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n",
|
||||
__func__, fsrate);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
slot_width = params_width(params);
|
||||
@ -1493,20 +1478,17 @@ static int tasdevice_hw_params(struct snd_pcm_substream *substream,
|
||||
default:
|
||||
dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n",
|
||||
__func__, slot_width);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bclk_rate = snd_soc_params_to_bclk(params);
|
||||
if (bclk_rate < 0) {
|
||||
dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n",
|
||||
__func__, bclk_rate);
|
||||
rc = bclk_rate;
|
||||
goto out;
|
||||
return bclk_rate;
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai,
|
||||
@ -1663,7 +1645,6 @@ static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv)
|
||||
|
||||
static int tasdevice_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c);
|
||||
const struct acpi_device_id *acpi_id;
|
||||
struct tasdevice_priv *tas_priv;
|
||||
int ret;
|
||||
@ -1685,7 +1666,7 @@ static int tasdevice_i2c_probe(struct i2c_client *i2c)
|
||||
tas_priv->chip_id = acpi_id->driver_data;
|
||||
tas_priv->isacpi = true;
|
||||
} else {
|
||||
tas_priv->chip_id = id ? id->driver_data : 0;
|
||||
tas_priv->chip_id = (uintptr_t)i2c_get_match_data(i2c);
|
||||
tas_priv->isacpi = false;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,6 @@ static const char * const tas5720_supply_names[] = {
|
||||
struct tas5720_data {
|
||||
struct snd_soc_component *component;
|
||||
struct regmap *regmap;
|
||||
struct i2c_client *tas5720_client;
|
||||
enum tas572x_type devtype;
|
||||
struct regulator_bulk_data supplies[TAS5720_NUM_SUPPLIES];
|
||||
struct delayed_work fault_check_work;
|
||||
@ -729,7 +728,6 @@ static int tas5720_probe(struct i2c_client *client)
|
||||
struct device *dev = &client->dev;
|
||||
struct tas5720_data *data;
|
||||
const struct regmap_config *regmap_config;
|
||||
const struct i2c_device_id *id;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
@ -737,11 +735,9 @@ static int tas5720_probe(struct i2c_client *client)
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
id = i2c_match_id(tas5720_id, client);
|
||||
data->tas5720_client = client;
|
||||
data->devtype = id->driver_data;
|
||||
data->devtype = (uintptr_t)i2c_get_match_data(client);
|
||||
|
||||
switch (id->driver_data) {
|
||||
switch (data->devtype) {
|
||||
case TAS5720:
|
||||
regmap_config = &tas5720_regmap_config;
|
||||
break;
|
||||
@ -774,7 +770,7 @@ static int tas5720_probe(struct i2c_client *client)
|
||||
|
||||
dev_set_drvdata(dev, data);
|
||||
|
||||
switch (id->driver_data) {
|
||||
switch (data->devtype) {
|
||||
case TAS5720:
|
||||
ret = devm_snd_soc_register_component(&client->dev,
|
||||
&soc_component_dev_tas5720,
|
||||
|
@ -1401,7 +1401,6 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct device *dev = &i2c->dev;
|
||||
struct adc3xxx *adc3xxx = NULL;
|
||||
const struct i2c_device_id *id;
|
||||
int ret;
|
||||
|
||||
adc3xxx = devm_kzalloc(dev, sizeof(struct adc3xxx), GFP_KERNEL);
|
||||
@ -1466,8 +1465,7 @@ static int adc3xxx_i2c_probe(struct i2c_client *i2c)
|
||||
|
||||
i2c_set_clientdata(i2c, adc3xxx);
|
||||
|
||||
id = i2c_match_id(adc3xxx_i2c_id, i2c);
|
||||
adc3xxx->type = id->driver_data;
|
||||
adc3xxx->type = (uintptr_t)i2c_get_match_data(i2c);
|
||||
|
||||
/* Reset codec chip */
|
||||
gpiod_set_value_cansleep(adc3xxx->rst_pin, 1);
|
||||
|
@ -1736,12 +1736,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct aic31xx_priv *aic31xx;
|
||||
unsigned int micbias_value = MICBIAS_2_0V;
|
||||
const struct i2c_device_id *id = i2c_match_id(aic31xx_i2c_id, i2c);
|
||||
int i, ret;
|
||||
|
||||
dev_dbg(&i2c->dev, "## %s: %s codec_type = %d\n", __func__,
|
||||
id->name, (int)id->driver_data);
|
||||
|
||||
aic31xx = devm_kzalloc(&i2c->dev, sizeof(*aic31xx), GFP_KERNEL);
|
||||
if (!aic31xx)
|
||||
return -ENOMEM;
|
||||
@ -1758,7 +1754,7 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c)
|
||||
aic31xx->dev = &i2c->dev;
|
||||
aic31xx->irq = i2c->irq;
|
||||
|
||||
aic31xx->codec_type = id->driver_data;
|
||||
aic31xx->codec_type = (uintptr_t)i2c_get_match_data(i2c);
|
||||
|
||||
dev_set_drvdata(aic31xx->dev, aic31xx);
|
||||
|
||||
|
@ -31,14 +31,13 @@ static int aic3x_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
struct regmap_config config;
|
||||
const struct i2c_device_id *id = i2c_match_id(aic3x_i2c_id, i2c);
|
||||
|
||||
config = aic3x_regmap;
|
||||
config.reg_bits = 8;
|
||||
config.val_bits = 8;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &config);
|
||||
return aic3x_probe(&i2c->dev, regmap, id->driver_data);
|
||||
return aic3x_probe(&i2c->dev, regmap, (uintptr_t)i2c_get_match_data(i2c));
|
||||
}
|
||||
|
||||
static void aic3x_i2c_remove(struct i2c_client *i2c)
|
||||
|
@ -222,7 +222,6 @@ static int tpa6130a2_probe(struct i2c_client *client)
|
||||
struct tpa6130a2_data *data;
|
||||
struct tpa6130a2_platform_data *pdata = client->dev.platform_data;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
const struct i2c_device_id *id;
|
||||
const char *regulator;
|
||||
unsigned int version;
|
||||
int ret;
|
||||
@ -251,8 +250,7 @@ static int tpa6130a2_probe(struct i2c_client *client)
|
||||
|
||||
i2c_set_clientdata(client, data);
|
||||
|
||||
id = i2c_match_id(tpa6130a2_id, client);
|
||||
data->id = id->driver_data;
|
||||
data->id = (uintptr_t)i2c_get_match_data(client);
|
||||
|
||||
if (data->power_gpio >= 0) {
|
||||
ret = devm_gpio_request(dev, data->power_gpio,
|
||||
|
@ -319,7 +319,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops,
|
||||
uda1342_suspend, uda1342_resume, NULL);
|
||||
|
||||
static const struct i2c_device_id uda1342_i2c_id[] = {
|
||||
{ "uda1342", 0 },
|
||||
{ "uda1342" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id);
|
||||
|
@ -159,6 +159,8 @@
|
||||
{"AMIC MUX" #id, "ADC5", "ADC5"}, \
|
||||
{"AMIC MUX" #id, "ADC6", "ADC6"}
|
||||
|
||||
#define NUM_CODEC_DAIS 7
|
||||
|
||||
enum {
|
||||
WCD9335_RX0 = 0,
|
||||
WCD9335_RX1,
|
||||
|
@ -2196,18 +2196,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (i2c->dev.of_node) {
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(wm8904_of_match, i2c->dev.of_node);
|
||||
if (match == NULL)
|
||||
return -EINVAL;
|
||||
wm8904->devtype = (uintptr_t)match->data;
|
||||
} else {
|
||||
const struct i2c_device_id *id =
|
||||
i2c_match_id(wm8904_i2c_id, i2c);
|
||||
wm8904->devtype = id->driver_data;
|
||||
}
|
||||
wm8904->devtype = (uintptr_t)i2c_get_match_data(i2c);
|
||||
|
||||
i2c_set_clientdata(i2c, wm8904);
|
||||
wm8904->pdata = i2c->dev.platform_data;
|
||||
|
@ -1166,12 +1166,10 @@ static struct spi_driver wm8985_spi_driver = {
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
static const struct i2c_device_id wm8985_i2c_id[];
|
||||
|
||||
static int wm8985_i2c_probe(struct i2c_client *i2c)
|
||||
{
|
||||
struct wm8985_priv *wm8985;
|
||||
const struct i2c_device_id *id = i2c_match_id(wm8985_i2c_id, i2c);
|
||||
int ret;
|
||||
|
||||
wm8985 = devm_kzalloc(&i2c->dev, sizeof *wm8985, GFP_KERNEL);
|
||||
@ -1180,7 +1178,7 @@ static int wm8985_i2c_probe(struct i2c_client *i2c)
|
||||
|
||||
i2c_set_clientdata(i2c, wm8985);
|
||||
|
||||
wm8985->dev_type = id->driver_data;
|
||||
wm8985->dev_type = (uintptr_t)i2c_get_match_data(i2c);
|
||||
|
||||
wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
|
||||
if (IS_ERR(wm8985->regmap)) {
|
||||
|
@ -8,6 +8,8 @@ config SND_SOC_FSL_ASRC
|
||||
depends on HAS_DMA
|
||||
select REGMAP_MMIO
|
||||
select SND_SOC_GENERIC_DMAENGINE_PCM
|
||||
select SND_COMPRESS_ACCEL
|
||||
select SND_COMPRESS_OFFLOAD
|
||||
help
|
||||
Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
|
||||
support for the Freescale CPUs.
|
||||
|
@ -10,7 +10,7 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
|
||||
# Freescale SSI/DMA/SAI/SPDIF Support
|
||||
snd-soc-fsl-audmix-y := fsl_audmix.o
|
||||
snd-soc-fsl-asoc-card-y := fsl-asoc-card.o
|
||||
snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o
|
||||
snd-soc-fsl-asrc-y := fsl_asrc.o fsl_asrc_dma.o fsl_asrc_m2m.o
|
||||
snd-soc-fsl-lpc3xxx-y := lpc3xxx-pcm.o lpc3xxx-i2s.o
|
||||
snd-soc-fsl-sai-y := fsl_sai.o
|
||||
snd-soc-fsl-ssi-y := fsl_ssi.o
|
||||
|
@ -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);
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
#include "fsl_asrc_common.h"
|
||||
|
||||
#define ASRC_M2M_INPUTFIFO_WML 0x4
|
||||
#define ASRC_M2M_OUTPUTFIFO_WML 0x2
|
||||
#define ASRC_DMA_BUFFER_NUM 2
|
||||
#define ASRC_INPUTFIFO_THRESHOLD 32
|
||||
#define ASRC_OUTPUTFIFO_THRESHOLD 32
|
||||
|
@ -21,6 +21,26 @@ enum asrc_pair_index {
|
||||
|
||||
#define PAIR_CTX_NUM 0x4
|
||||
|
||||
/**
|
||||
* struct fsl_asrc_m2m_cap - capability data
|
||||
* @fmt_in: input sample format
|
||||
* @fmt_out: output sample format
|
||||
* @chan_min: minimum channel number
|
||||
* @chan_max: maximum channel number
|
||||
* @rate_in: minimum rate
|
||||
* @rate_out: maximum rete
|
||||
*/
|
||||
struct fsl_asrc_m2m_cap {
|
||||
u64 fmt_in;
|
||||
u64 fmt_out;
|
||||
int chan_min;
|
||||
int chan_max;
|
||||
const unsigned int *rate_in;
|
||||
int rate_in_count;
|
||||
const unsigned int *rate_out;
|
||||
int rate_out_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* fsl_asrc_pair: ASRC Pair common data
|
||||
*
|
||||
@ -34,6 +54,14 @@ enum asrc_pair_index {
|
||||
* @pos: hardware pointer position
|
||||
* @req_dma_chan: flag to release dev_to_dev chan
|
||||
* @private: pair private area
|
||||
* @complete: dma task complete
|
||||
* @sample_format: format of m2m
|
||||
* @rate: rate of m2m
|
||||
* @buf_len: buffer length of m2m
|
||||
* @dma_buffer: buffer pointers
|
||||
* @first_convert: start of conversion
|
||||
* @ratio_mod_flag: flag for new ratio modifier
|
||||
* @ratio_mod: ratio modification
|
||||
*/
|
||||
struct fsl_asrc_pair {
|
||||
struct fsl_asrc *asrc;
|
||||
@ -49,6 +77,16 @@ struct fsl_asrc_pair {
|
||||
bool req_dma_chan;
|
||||
|
||||
void *private;
|
||||
|
||||
/* used for m2m */
|
||||
struct completion complete[2];
|
||||
snd_pcm_format_t sample_format[2];
|
||||
unsigned int rate[2];
|
||||
unsigned int buf_len[2];
|
||||
struct snd_dma_buffer dma_buffer[2];
|
||||
unsigned int first_convert;
|
||||
bool ratio_mod_flag;
|
||||
unsigned int ratio_mod;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -62,6 +100,7 @@ struct fsl_asrc_pair {
|
||||
* @mem_clk: clock source to access register
|
||||
* @ipg_clk: clock source to drive peripheral
|
||||
* @spba_clk: SPBA clock (optional, depending on SoC design)
|
||||
* @card: compress sound card
|
||||
* @lock: spin lock for resource protection
|
||||
* @pair: pair pointers
|
||||
* @channel_avail: non-occupied channel numbers
|
||||
@ -72,6 +111,17 @@ struct fsl_asrc_pair {
|
||||
* @request_pair: function pointer
|
||||
* @release_pair: function pointer
|
||||
* @get_fifo_addr: function pointer
|
||||
* @m2m_get_cap: function pointer
|
||||
* @m2m_prepare: function pointer
|
||||
* @m2m_start: function pointer
|
||||
* @m2m_unprepare: function pointer
|
||||
* @m2m_stop: function pointer
|
||||
* @m2m_calc_out_len: function pointer
|
||||
* @m2m_get_maxburst: function pointer
|
||||
* @m2m_pair_suspend: function pointer
|
||||
* @m2m_pair_resume: function pointer
|
||||
* @m2m_set_ratio_mod: function pointer
|
||||
* @get_output_fifo_size: function pointer
|
||||
* @pair_priv_size: size of pair private struct.
|
||||
* @private: private data structure
|
||||
*/
|
||||
@ -84,6 +134,7 @@ struct fsl_asrc {
|
||||
struct clk *mem_clk;
|
||||
struct clk *ipg_clk;
|
||||
struct clk *spba_clk;
|
||||
struct snd_card *card;
|
||||
spinlock_t lock; /* spin lock for resource protection */
|
||||
|
||||
struct fsl_asrc_pair *pair[PAIR_CTX_NUM];
|
||||
@ -97,6 +148,20 @@ struct fsl_asrc {
|
||||
int (*request_pair)(int channels, struct fsl_asrc_pair *pair);
|
||||
void (*release_pair)(struct fsl_asrc_pair *pair);
|
||||
int (*get_fifo_addr)(u8 dir, enum asrc_pair_index index);
|
||||
int (*m2m_get_cap)(struct fsl_asrc_m2m_cap *cap);
|
||||
|
||||
int (*m2m_prepare)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_start)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_unprepare)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_stop)(struct fsl_asrc_pair *pair);
|
||||
|
||||
int (*m2m_calc_out_len)(struct fsl_asrc_pair *pair, int input_buffer_length);
|
||||
int (*m2m_get_maxburst)(u8 dir, struct fsl_asrc_pair *pair);
|
||||
int (*m2m_pair_suspend)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_pair_resume)(struct fsl_asrc_pair *pair);
|
||||
int (*m2m_set_ratio_mod)(struct fsl_asrc_pair *pair, int val);
|
||||
|
||||
unsigned int (*get_output_fifo_size)(struct fsl_asrc_pair *pair);
|
||||
size_t pair_priv_size;
|
||||
|
||||
void *private;
|
||||
@ -105,4 +170,9 @@ struct fsl_asrc {
|
||||
#define DRV_NAME "fsl-asrc-dai"
|
||||
extern struct snd_soc_component_driver fsl_asrc_component;
|
||||
|
||||
int fsl_asrc_m2m_init(struct fsl_asrc *asrc);
|
||||
void fsl_asrc_m2m_exit(struct fsl_asrc *asrc);
|
||||
int fsl_asrc_m2m_resume(struct fsl_asrc *asrc);
|
||||
int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc);
|
||||
|
||||
#endif /* _FSL_ASRC_COMMON_H */
|
||||
|
727
sound/soc/fsl/fsl_asrc_m2m.c
Normal file
727
sound/soc/fsl/fsl_asrc_m2m.c
Normal file
@ -0,0 +1,727 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
|
||||
// Copyright (C) 2019-2024 NXP
|
||||
//
|
||||
// Freescale ASRC Memory to Memory (M2M) driver
|
||||
|
||||
#include <linux/dma/imx-dma.h>
|
||||
#include <linux/dma-buf.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <sound/asound.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
#include <sound/initval.h>
|
||||
|
||||
#include "fsl_asrc_common.h"
|
||||
|
||||
#define DIR_STR(dir) (dir) == IN ? "in" : "out"
|
||||
|
||||
#define ASRC_xPUT_DMA_CALLBACK(dir) \
|
||||
(((dir) == IN) ? asrc_input_dma_callback \
|
||||
: asrc_output_dma_callback)
|
||||
|
||||
/* Maximum output and capture buffer size */
|
||||
#define ASRC_M2M_BUFFER_SIZE (512 * 1024)
|
||||
|
||||
/* Maximum output and capture period size */
|
||||
#define ASRC_M2M_PERIOD_SIZE (48 * 1024)
|
||||
|
||||
/* dma complete callback */
|
||||
static void asrc_input_dma_callback(void *data)
|
||||
{
|
||||
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
|
||||
|
||||
complete(&pair->complete[IN]);
|
||||
}
|
||||
|
||||
/* dma complete callback */
|
||||
static void asrc_output_dma_callback(void *data)
|
||||
{
|
||||
struct fsl_asrc_pair *pair = (struct fsl_asrc_pair *)data;
|
||||
|
||||
complete(&pair->complete[OUT]);
|
||||
}
|
||||
|
||||
/**
|
||||
*asrc_read_last_fifo: read all the remaining data from FIFO
|
||||
*@pair: Structure pointer of fsl_asrc_pair
|
||||
*@dma_vaddr: virtual address of capture buffer
|
||||
*@length: payload length of capture buffer
|
||||
*/
|
||||
static void asrc_read_last_fifo(struct fsl_asrc_pair *pair, void *dma_vaddr, u32 *length)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
u32 i, reg, size, t_size = 0, width;
|
||||
u32 *reg32 = NULL;
|
||||
u16 *reg16 = NULL;
|
||||
u8 *reg24 = NULL;
|
||||
|
||||
width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
|
||||
if (width == 32)
|
||||
reg32 = dma_vaddr + *length;
|
||||
else if (width == 16)
|
||||
reg16 = dma_vaddr + *length;
|
||||
else
|
||||
reg24 = dma_vaddr + *length;
|
||||
retry:
|
||||
size = asrc->get_output_fifo_size(pair);
|
||||
if (size + *length > ASRC_M2M_BUFFER_SIZE)
|
||||
goto end;
|
||||
|
||||
for (i = 0; i < size * pair->channels; i++) {
|
||||
regmap_read(asrc->regmap, asrc->get_fifo_addr(OUT, index), ®);
|
||||
if (reg32) {
|
||||
*reg32++ = reg;
|
||||
} else if (reg16) {
|
||||
*reg16++ = (u16)reg;
|
||||
} else {
|
||||
*reg24++ = (u8)reg;
|
||||
*reg24++ = (u8)(reg >> 8);
|
||||
*reg24++ = (u8)(reg >> 16);
|
||||
}
|
||||
}
|
||||
t_size += size;
|
||||
|
||||
/* In case there is data left in FIFO */
|
||||
if (size)
|
||||
goto retry;
|
||||
end:
|
||||
/* Update payload length */
|
||||
if (reg32)
|
||||
*length += t_size * pair->channels * 4;
|
||||
else if (reg16)
|
||||
*length += t_size * pair->channels * 2;
|
||||
else
|
||||
*length += t_size * pair->channels * 3;
|
||||
}
|
||||
|
||||
/* config dma channel */
|
||||
static int asrc_dmaconfig(struct fsl_asrc_pair *pair,
|
||||
struct dma_chan *chan,
|
||||
u32 dma_addr, dma_addr_t buf_addr, u32 buf_len,
|
||||
int dir, int width)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct dma_slave_config slave_config;
|
||||
enum dma_slave_buswidth buswidth;
|
||||
unsigned int sg_len, max_period_size;
|
||||
struct scatterlist *sg;
|
||||
int ret, i;
|
||||
|
||||
switch (width) {
|
||||
case 8:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||
break;
|
||||
case 16:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
|
||||
break;
|
||||
case 24:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_3_BYTES;
|
||||
break;
|
||||
case 32:
|
||||
buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "invalid word width\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
memset(&slave_config, 0, sizeof(slave_config));
|
||||
if (dir == IN) {
|
||||
slave_config.direction = DMA_MEM_TO_DEV;
|
||||
slave_config.dst_addr = dma_addr;
|
||||
slave_config.dst_addr_width = buswidth;
|
||||
slave_config.dst_maxburst = asrc->m2m_get_maxburst(IN, pair);
|
||||
} else {
|
||||
slave_config.direction = DMA_DEV_TO_MEM;
|
||||
slave_config.src_addr = dma_addr;
|
||||
slave_config.src_addr_width = buswidth;
|
||||
slave_config.src_maxburst = asrc->m2m_get_maxburst(OUT, pair);
|
||||
}
|
||||
|
||||
ret = dmaengine_slave_config(chan, &slave_config);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to config dmaengine for %s task: %d\n",
|
||||
DIR_STR(dir), ret);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
max_period_size = rounddown(ASRC_M2M_PERIOD_SIZE, width * pair->channels / 8);
|
||||
/* scatter gather mode */
|
||||
sg_len = buf_len / max_period_size;
|
||||
if (buf_len % max_period_size)
|
||||
sg_len += 1;
|
||||
|
||||
sg = kmalloc_array(sg_len, sizeof(*sg), GFP_KERNEL);
|
||||
if (!sg)
|
||||
return -ENOMEM;
|
||||
|
||||
sg_init_table(sg, sg_len);
|
||||
for (i = 0; i < (sg_len - 1); i++) {
|
||||
sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
|
||||
sg_dma_len(&sg[i]) = max_period_size;
|
||||
}
|
||||
sg_dma_address(&sg[i]) = buf_addr + i * max_period_size;
|
||||
sg_dma_len(&sg[i]) = buf_len - i * max_period_size;
|
||||
|
||||
pair->desc[dir] = dmaengine_prep_slave_sg(chan, sg, sg_len,
|
||||
slave_config.direction,
|
||||
DMA_PREP_INTERRUPT);
|
||||
kfree(sg);
|
||||
if (!pair->desc[dir]) {
|
||||
dev_err(dev, "failed to prepare dmaengine for %s task\n", DIR_STR(dir));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pair->desc[dir]->callback = ASRC_xPUT_DMA_CALLBACK(dir);
|
||||
pair->desc[dir]->callback_param = pair;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* main function of converter */
|
||||
static void asrc_m2m_device_run(struct fsl_asrc_pair *pair, struct snd_compr_task_runtime *task)
|
||||
{
|
||||
struct fsl_asrc *asrc = pair->asrc;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
enum asrc_pair_index index = pair->index;
|
||||
struct snd_dma_buffer *src_buf, *dst_buf;
|
||||
unsigned int in_buf_len;
|
||||
unsigned int out_dma_len;
|
||||
unsigned int width;
|
||||
u32 fifo_addr;
|
||||
int ret;
|
||||
|
||||
/* set ratio mod */
|
||||
if (asrc->m2m_set_ratio_mod) {
|
||||
if (pair->ratio_mod_flag) {
|
||||
asrc->m2m_set_ratio_mod(pair, pair->ratio_mod);
|
||||
pair->ratio_mod_flag = false;
|
||||
}
|
||||
}
|
||||
|
||||
src_buf = &pair->dma_buffer[IN];
|
||||
dst_buf = &pair->dma_buffer[OUT];
|
||||
|
||||
width = snd_pcm_format_physical_width(pair->sample_format[IN]);
|
||||
fifo_addr = asrc->paddr + asrc->get_fifo_addr(IN, index);
|
||||
|
||||
in_buf_len = task->input_size;
|
||||
|
||||
if (in_buf_len < width * pair->channels / 8 ||
|
||||
in_buf_len > ASRC_M2M_BUFFER_SIZE ||
|
||||
in_buf_len % (width * pair->channels / 8)) {
|
||||
dev_err(dev, "out buffer size is error: [%d]\n", in_buf_len);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* dma config for output dma channel */
|
||||
ret = asrc_dmaconfig(pair,
|
||||
pair->dma_chan[IN],
|
||||
fifo_addr,
|
||||
src_buf->addr,
|
||||
in_buf_len, IN, width);
|
||||
if (ret) {
|
||||
dev_err(dev, "out dma config error\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
width = snd_pcm_format_physical_width(pair->sample_format[OUT]);
|
||||
fifo_addr = asrc->paddr + asrc->get_fifo_addr(OUT, index);
|
||||
out_dma_len = asrc->m2m_calc_out_len(pair, in_buf_len);
|
||||
if (out_dma_len > 0 && out_dma_len <= ASRC_M2M_BUFFER_SIZE) {
|
||||
/* dma config for capture dma channel */
|
||||
ret = asrc_dmaconfig(pair,
|
||||
pair->dma_chan[OUT],
|
||||
fifo_addr,
|
||||
dst_buf->addr,
|
||||
out_dma_len, OUT, width);
|
||||
if (ret) {
|
||||
dev_err(dev, "cap dma config error\n");
|
||||
goto end;
|
||||
}
|
||||
} else if (out_dma_len > ASRC_M2M_BUFFER_SIZE) {
|
||||
dev_err(dev, "cap buffer size error\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
reinit_completion(&pair->complete[IN]);
|
||||
reinit_completion(&pair->complete[OUT]);
|
||||
|
||||
/* Submit DMA request */
|
||||
dmaengine_submit(pair->desc[IN]);
|
||||
dma_async_issue_pending(pair->desc[IN]->chan);
|
||||
if (out_dma_len > 0) {
|
||||
dmaengine_submit(pair->desc[OUT]);
|
||||
dma_async_issue_pending(pair->desc[OUT]->chan);
|
||||
}
|
||||
|
||||
asrc->m2m_start(pair);
|
||||
|
||||
if (!wait_for_completion_interruptible_timeout(&pair->complete[IN], 10 * HZ)) {
|
||||
dev_err(dev, "out DMA task timeout\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (out_dma_len > 0) {
|
||||
if (!wait_for_completion_interruptible_timeout(&pair->complete[OUT], 10 * HZ)) {
|
||||
dev_err(dev, "cap DMA task timeout\n");
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
/* read the last words from FIFO */
|
||||
asrc_read_last_fifo(pair, dst_buf->area, &out_dma_len);
|
||||
/* update payload length for capture */
|
||||
task->output_size = out_dma_len;
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct fsl_asrc_pair *pair;
|
||||
int size, ret;
|
||||
|
||||
pair = kzalloc(sizeof(*pair) + asrc->pair_priv_size, GFP_KERNEL);
|
||||
if (!pair)
|
||||
return -ENOMEM;
|
||||
|
||||
pair->private = (void *)pair + sizeof(struct fsl_asrc_pair);
|
||||
pair->asrc = asrc;
|
||||
|
||||
init_completion(&pair->complete[IN]);
|
||||
init_completion(&pair->complete[OUT]);
|
||||
|
||||
runtime->private_data = pair;
|
||||
|
||||
size = ASRC_M2M_BUFFER_SIZE;
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[IN]);
|
||||
if (ret)
|
||||
goto error_alloc_in_buf;
|
||||
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, &pair->dma_buffer[OUT]);
|
||||
if (ret)
|
||||
goto error_alloc_out_buf;
|
||||
|
||||
ret = pm_runtime_get_sync(dev);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Failed to power up asrc\n");
|
||||
goto err_pm_runtime;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_pm_runtime:
|
||||
snd_dma_free_pages(&pair->dma_buffer[OUT]);
|
||||
error_alloc_out_buf:
|
||||
snd_dma_free_pages(&pair->dma_buffer[IN]);
|
||||
error_alloc_in_buf:
|
||||
kfree(pair);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_release(struct snd_compr_stream *stream)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
snd_dma_free_pages(&pair->dma_buffer[IN]);
|
||||
snd_dma_free_pages(&pair->dma_buffer[OUT]);
|
||||
|
||||
kfree(runtime->private_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_set_params(struct snd_compr_stream *stream,
|
||||
struct snd_compr_params *params)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct fsl_asrc_m2m_cap cap;
|
||||
int ret, i;
|
||||
|
||||
ret = asrc->m2m_get_cap(&cap);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.format) & cap.fmt_in)
|
||||
pair->sample_format[IN] = (__force snd_pcm_format_t)params->codec.format;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (pcm_format_to_bits((__force snd_pcm_format_t)params->codec.pcm_format) & cap.fmt_out)
|
||||
pair->sample_format[OUT] = (__force snd_pcm_format_t)params->codec.pcm_format;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* check input rate is in scope */
|
||||
for (i = 0; i < cap.rate_in_count; i++)
|
||||
if (params->codec.sample_rate == cap.rate_in[i]) {
|
||||
pair->rate[IN] = params->codec.sample_rate;
|
||||
break;
|
||||
}
|
||||
if (i == cap.rate_in_count)
|
||||
return -EINVAL;
|
||||
|
||||
/* check output rate is in scope */
|
||||
for (i = 0; i < cap.rate_out_count; i++)
|
||||
if (params->codec.options.src_d.out_sample_rate == cap.rate_out[i]) {
|
||||
pair->rate[OUT] = params->codec.options.src_d.out_sample_rate;
|
||||
break;
|
||||
}
|
||||
if (i == cap.rate_out_count)
|
||||
return -EINVAL;
|
||||
|
||||
if (params->codec.ch_in != params->codec.ch_out ||
|
||||
params->codec.ch_in < cap.chan_min ||
|
||||
params->codec.ch_in > cap.chan_max)
|
||||
return -EINVAL;
|
||||
|
||||
pair->channels = params->codec.ch_in;
|
||||
pair->buf_len[IN] = params->buffer.fragment_size;
|
||||
pair->buf_len[OUT] = params->buffer.fragment_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
|
||||
{
|
||||
struct snd_dma_buffer *dmab = dmabuf->priv;
|
||||
|
||||
return snd_dma_buffer_mmap(dmab, vma);
|
||||
}
|
||||
|
||||
static struct sg_table *fsl_asrc_m2m_map_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct snd_dma_buffer *dmab = attachment->dmabuf->priv;
|
||||
struct sg_table *sgt;
|
||||
|
||||
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
|
||||
if (!sgt)
|
||||
return NULL;
|
||||
|
||||
if (dma_get_sgtable(attachment->dev, sgt, dmab->area, dmab->addr, dmab->bytes) < 0)
|
||||
goto free;
|
||||
|
||||
if (dma_map_sgtable(attachment->dev, sgt, direction, 0))
|
||||
goto free;
|
||||
|
||||
return sgt;
|
||||
|
||||
free:
|
||||
sg_free_table(sgt);
|
||||
kfree(sgt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fsl_asrc_m2m_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *table,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
dma_unmap_sgtable(attachment->dev, table, direction, 0);
|
||||
}
|
||||
|
||||
static void fsl_asrc_m2m_release(struct dma_buf *dmabuf)
|
||||
{
|
||||
/* buffer is released by fsl_asrc_m2m_comp_release() */
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops fsl_asrc_m2m_dma_buf_ops = {
|
||||
.mmap = fsl_asrc_m2m_mmap,
|
||||
.map_dma_buf = fsl_asrc_m2m_map_dma_buf,
|
||||
.unmap_dma_buf = fsl_asrc_m2m_unmap_dma_buf,
|
||||
.release = fsl_asrc_m2m_release,
|
||||
};
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info_in);
|
||||
DEFINE_DMA_BUF_EXPORT_INFO(exp_info_out);
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
int ret;
|
||||
|
||||
exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
|
||||
exp_info_in.size = ASRC_M2M_BUFFER_SIZE;
|
||||
exp_info_in.flags = O_RDWR;
|
||||
exp_info_in.priv = &pair->dma_buffer[IN];
|
||||
task->input = dma_buf_export(&exp_info_in);
|
||||
if (IS_ERR(task->input)) {
|
||||
ret = PTR_ERR(task->input);
|
||||
return ret;
|
||||
}
|
||||
|
||||
exp_info_out.ops = &fsl_asrc_m2m_dma_buf_ops;
|
||||
exp_info_out.size = ASRC_M2M_BUFFER_SIZE;
|
||||
exp_info_out.flags = O_RDWR;
|
||||
exp_info_out.priv = &pair->dma_buffer[OUT];
|
||||
task->output = dma_buf_export(&exp_info_out);
|
||||
if (IS_ERR(task->output)) {
|
||||
ret = PTR_ERR(task->output);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Request asrc pair/context */
|
||||
ret = asrc->request_pair(pair->channels, pair);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request pair: %d\n", ret);
|
||||
goto err_request_pair;
|
||||
}
|
||||
|
||||
ret = asrc->m2m_prepare(pair);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to start pair part one: %d\n", ret);
|
||||
goto err_start_part_one;
|
||||
}
|
||||
|
||||
/* Request dma channels */
|
||||
pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
|
||||
if (!pair->dma_chan[IN]) {
|
||||
dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
|
||||
ret = -EBUSY;
|
||||
goto err_dma_channel_in;
|
||||
}
|
||||
|
||||
pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
|
||||
if (!pair->dma_chan[OUT]) {
|
||||
dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
|
||||
ret = -EBUSY;
|
||||
goto err_dma_channel_out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_dma_channel_out:
|
||||
dma_release_channel(pair->dma_chan[IN]);
|
||||
err_dma_channel_in:
|
||||
if (asrc->m2m_unprepare)
|
||||
asrc->m2m_unprepare(pair);
|
||||
err_start_part_one:
|
||||
asrc->release_pair(pair);
|
||||
err_request_pair:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_start(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
|
||||
asrc_m2m_device_run(pair, task);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_stop(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_comp_task_free(struct snd_compr_stream *stream,
|
||||
struct snd_compr_task_runtime *task)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
struct snd_compr_runtime *runtime = stream->runtime;
|
||||
struct fsl_asrc_pair *pair = runtime->private_data;
|
||||
|
||||
/* Stop & release pair/context */
|
||||
if (asrc->m2m_stop)
|
||||
asrc->m2m_stop(pair);
|
||||
|
||||
if (asrc->m2m_unprepare)
|
||||
asrc->m2m_unprepare(pair);
|
||||
asrc->release_pair(pair);
|
||||
|
||||
/* Release dma channel */
|
||||
if (pair->dma_chan[IN])
|
||||
dma_release_channel(pair->dma_chan[IN]);
|
||||
if (pair->dma_chan[OUT])
|
||||
dma_release_channel(pair->dma_chan[OUT]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_get_caps(struct snd_compr_stream *cstream,
|
||||
struct snd_compr_caps *caps)
|
||||
{
|
||||
caps->num_codecs = 1;
|
||||
caps->min_fragment_size = 4096;
|
||||
caps->max_fragment_size = 4096;
|
||||
caps->min_fragments = 1;
|
||||
caps->max_fragments = 1;
|
||||
caps->codecs[0] = SND_AUDIOCODEC_PCM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_fill_codec_caps(struct fsl_asrc *asrc,
|
||||
struct snd_compr_codec_caps *codec)
|
||||
{
|
||||
struct fsl_asrc_m2m_cap cap;
|
||||
snd_pcm_format_t k;
|
||||
int j = 0;
|
||||
int ret;
|
||||
|
||||
ret = asrc->m2m_get_cap(&cap);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
pcm_for_each_format(k) {
|
||||
if (pcm_format_to_bits(k) & cap.fmt_in) {
|
||||
codec->descriptor[j].max_ch = cap.chan_max;
|
||||
memcpy(codec->descriptor[j].sample_rates,
|
||||
cap.rate_in,
|
||||
cap.rate_in_count * sizeof(__u32));
|
||||
codec->descriptor[j].num_sample_rates = cap.rate_in_count;
|
||||
codec->descriptor[j].formats = (__force __u32)k;
|
||||
codec->descriptor[j].pcm_formats = cap.fmt_out;
|
||||
codec->descriptor[j].src.out_sample_rate_min = cap.rate_out[0];
|
||||
codec->descriptor[j].src.out_sample_rate_max =
|
||||
cap.rate_out[cap.rate_out_count - 1];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
codec->codec = SND_AUDIOCODEC_PCM;
|
||||
codec->num_descriptors = j;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_asrc_m2m_get_codec_caps(struct snd_compr_stream *stream,
|
||||
struct snd_compr_codec_caps *codec)
|
||||
{
|
||||
struct fsl_asrc *asrc = stream->private_data;
|
||||
|
||||
return fsl_asrc_m2m_fill_codec_caps(asrc, codec);
|
||||
}
|
||||
|
||||
static struct snd_compr_ops fsl_asrc_m2m_compr_ops = {
|
||||
.open = fsl_asrc_m2m_comp_open,
|
||||
.free = fsl_asrc_m2m_comp_release,
|
||||
.set_params = fsl_asrc_m2m_comp_set_params,
|
||||
.get_caps = fsl_asrc_m2m_get_caps,
|
||||
.get_codec_caps = fsl_asrc_m2m_get_codec_caps,
|
||||
.task_create = fsl_asrc_m2m_comp_task_create,
|
||||
.task_start = fsl_asrc_m2m_comp_task_start,
|
||||
.task_stop = fsl_asrc_m2m_comp_task_stop,
|
||||
.task_free = fsl_asrc_m2m_comp_task_free,
|
||||
};
|
||||
|
||||
int fsl_asrc_m2m_suspend(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct fsl_asrc_pair *pair;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PAIR_CTX_NUM; i++) {
|
||||
pair = asrc->pair[i];
|
||||
if (!pair)
|
||||
continue;
|
||||
if (!completion_done(&pair->complete[IN])) {
|
||||
if (pair->dma_chan[IN])
|
||||
dmaengine_terminate_all(pair->dma_chan[IN]);
|
||||
asrc_input_dma_callback((void *)pair);
|
||||
}
|
||||
if (!completion_done(&pair->complete[OUT])) {
|
||||
if (pair->dma_chan[OUT])
|
||||
dmaengine_terminate_all(pair->dma_chan[OUT]);
|
||||
asrc_output_dma_callback((void *)pair);
|
||||
}
|
||||
|
||||
if (asrc->m2m_pair_suspend)
|
||||
asrc->m2m_pair_suspend(pair);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_suspend);
|
||||
|
||||
int fsl_asrc_m2m_resume(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct fsl_asrc_pair *pair;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PAIR_CTX_NUM; i++) {
|
||||
pair = asrc->pair[i];
|
||||
if (!pair)
|
||||
continue;
|
||||
if (asrc->m2m_pair_resume)
|
||||
asrc->m2m_pair_resume(pair);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_resume);
|
||||
|
||||
int fsl_asrc_m2m_init(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct device *dev = &asrc->pdev->dev;
|
||||
struct snd_card *card;
|
||||
struct snd_compr *compr;
|
||||
int ret;
|
||||
|
||||
ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
|
||||
THIS_MODULE, 0, &card);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
strscpy(card->driver, "fsl-asrc-m2m", sizeof(card->driver));
|
||||
strscpy(card->shortname, "ASRC-M2M", sizeof(card->shortname));
|
||||
strscpy(card->longname, "ASRC-M2M", sizeof(card->shortname));
|
||||
|
||||
asrc->card = card;
|
||||
|
||||
compr = devm_kzalloc(dev, sizeof(*compr), GFP_KERNEL);
|
||||
if (!compr) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
compr->ops = &fsl_asrc_m2m_compr_ops;
|
||||
compr->private_data = asrc;
|
||||
|
||||
ret = snd_compress_new(card, 0, SND_COMPRESS_ACCEL, "ASRC M2M", compr);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
ret = snd_card_register(card);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
snd_card_free(card);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_init);
|
||||
|
||||
void fsl_asrc_m2m_exit(struct fsl_asrc *asrc)
|
||||
{
|
||||
struct snd_card *card = asrc->card;
|
||||
|
||||
snd_card_free(card);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fsl_asrc_m2m_exit);
|
||||
|
||||
MODULE_IMPORT_NS("DMA_BUF");
|
||||
MODULE_AUTHOR("Shengjiu Wang <Shengjiu.Wang@nxp.com>");
|
||||
MODULE_DESCRIPTION("Freescale ASRC M2M driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -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 = {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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];
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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__);
|
||||
|
@ -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)
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user