summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/mfd/motorola-cpcap.txt6
-rw-r--r--Documentation/devicetree/bindings/misc/atmel-ssc.txt50
-rw-r--r--Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml104
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml36
-rw-r--r--Documentation/devicetree/bindings/sound/rockchip-spdif.yaml4
-rw-r--r--MAINTAINERS3
-rw-r--r--drivers/soundwire/qcom.c26
-rw-r--r--include/dt-bindings/sound/qcom,wcd934x.h16
-rw-r--r--include/sound/sdca.h22
-rw-r--r--include/sound/sdca_function.h1089
-rw-r--r--include/sound/simple_card_utils.h8
-rw-r--r--include/sound/soc-dapm.h2
-rw-r--r--include/sound/soc.h1
-rw-r--r--include/sound/tas2781.h1
-rw-r--r--include/uapi/sound/intel/avs/tokens.h7
-rw-r--r--sound/hda/intel-dsp-config.c4
-rw-r--r--sound/pci/hda/tas2781-spi.h1
-rw-r--r--sound/pci/hda/tas2781_hda_i2c.c8
-rw-r--r--sound/pci/hda/tas2781_hda_spi.c3
-rw-r--r--sound/soc/amd/Kconfig10
-rw-r--r--sound/soc/amd/acp/Kconfig1
-rw-r--r--sound/soc/amd/acp/Makefile2
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c45
-rw-r--r--sound/soc/amd/acp/acp-sdw-mach-common.c34
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c5
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c160
-rw-r--r--sound/soc/amd/acp/soc_amd_sdw_common.h12
-rw-r--r--sound/soc/amd/mach-config.h1
-rw-r--r--sound/soc/amd/ps/Makefile2
-rw-r--r--sound/soc/amd/ps/acp63.h246
-rw-r--r--sound/soc/amd/ps/pci-ps.c432
-rw-r--r--sound/soc/amd/ps/ps-common.c475
-rw-r--r--sound/soc/amd/ps/ps-pdm-dma.c6
-rw-r--r--sound/soc/amd/ps/ps-sdw-dma.c375
-rw-r--r--sound/soc/codecs/cpcap.c200
-rw-r--r--sound/soc/codecs/dmic.c4
-rw-r--r--sound/soc/codecs/mt6358.c104
-rw-r--r--sound/soc/codecs/mt6358.h4
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c9
-rw-r--r--sound/soc/codecs/pcm3168a.c11
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c130
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h99
-rw-r--r--sound/soc/codecs/rt722-sdca.c135
-rw-r--r--sound/soc/codecs/rt722-sdca.h4
-rw-r--r--sound/soc/codecs/tscs454.c5
-rw-r--r--sound/soc/codecs/wcd934x.c16
-rw-r--r--sound/soc/codecs/wcd937x-sdw.c41
-rw-r--r--sound/soc/codecs/wcd937x.c53
-rw-r--r--sound/soc/codecs/wcd937x.h7
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c2
-rw-r--r--sound/soc/codecs/wcd939x-sdw.c2
-rw-r--r--sound/soc/fsl/fsl_micfil.c51
-rw-r--r--sound/soc/fsl/fsl_micfil.h1
-rw-r--r--sound/soc/generic/audio-graph-card.c97
-rw-r--r--sound/soc/generic/audio-graph-card2.c95
-rw-r--r--sound/soc/generic/simple-card-utils.c171
-rw-r--r--sound/soc/generic/simple-card.c132
-rw-r--r--sound/soc/intel/avs/avs.h1
-rw-r--r--sound/soc/intel/avs/board_selection.c17
-rw-r--r--sound/soc/intel/avs/boards/Kconfig10
-rw-r--r--sound/soc/intel/avs/boards/Makefile2
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c143
-rw-r--r--sound/soc/intel/avs/dsp.c3
-rw-r--r--sound/soc/intel/avs/loader.c64
-rw-r--r--sound/soc/intel/avs/messages.c38
-rw-r--r--sound/soc/intel/avs/messages.h22
-rw-r--r--sound/soc/intel/avs/path.c267
-rw-r--r--sound/soc/intel/avs/tgl.c33
-rw-r--r--sound/soc/intel/avs/topology.c42
-rw-r--r--sound/soc/intel/avs/topology.h11
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c12
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-tgl-match.c16
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c20
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h2
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.c55
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.h2
-rw-r--r--sound/soc/qcom/sdw.c34
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c4
-rw-r--r--sound/soc/sdca/sdca_device.c3
-rw-r--r--sound/soc/sdca/sdca_functions.c1407
-rw-r--r--sound/soc/soc-card.c14
-rw-r--r--sound/soc/soc-component.c38
-rw-r--r--sound/soc/soc-core.c32
-rw-r--r--sound/soc/soc-dai.c18
-rw-r--r--sound/soc/soc-dapm.c3
-rw-r--r--sound/soc/soc-link.c18
-rw-r--r--sound/soc/soc-ops.c1
-rw-r--r--sound/soc/soc-pcm.c114
-rw-r--r--sound/soc/soc-utils.c27
-rw-r--r--sound/soc/sof/imx/Kconfig17
-rw-r--r--sound/soc/sof/imx/Makefile6
-rw-r--r--sound/soc/sof/imx/imx-common.c431
-rw-r--r--sound/soc/sof/imx/imx-common.h151
-rw-r--r--sound/soc/sof/imx/imx8.c753
-rw-r--r--sound/soc/sof/imx/imx8m.c567
-rw-r--r--sound/soc/sof/imx/imx8ulp.c520
-rw-r--r--sound/soc/sof/imx/imx9.c137
-rw-r--r--sound/soc/sof/ipc3-pcm.c13
-rw-r--r--sound/soc/sof/ipc3-topology.c20
-rw-r--r--sound/soc/sof/ipc4-loader.c143
-rw-r--r--sound/soc/sof/ipc4-pcm.c21
-rw-r--r--sound/soc/sof/ipc4-priv.h1
-rw-r--r--sound/soc/sof/ipc4-topology.c40
-rw-r--r--sound/soc/sof/ipc4.c8
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195-clk.c3
-rw-r--r--sound/soc/sof/pcm.c169
-rw-r--r--sound/soc/sof/sof-audio.c49
-rw-r--r--sound/soc/sof/sof-audio.h17
-rw-r--r--sound/soc/sof/topology.c4
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra210_admaif.c24
-rw-r--r--sound/soc/tegra/tegra210_admaif.h9
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.c129
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.h31
115 files changed, 7086 insertions, 3224 deletions
diff --git a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
index 190230216de8..f00827c9b67f 100644
--- a/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
+++ b/Documentation/devicetree/bindings/mfd/motorola-cpcap.txt
@@ -31,6 +31,10 @@ node must be named "audio-codec".
Required properties for the audio-codec subnode:
- #sound-dai-cells = <1>;
+- interrupts : should contain jack detection interrupts, with headset
+ detect interrupt matching "hs" and microphone bias 2
+ detect interrupt matching "mb2" in interrupt-names.
+- interrupt-names : Contains "hs", "mb2"
The audio-codec provides two DAIs. The first one is connected to the
Stereo HiFi DAC and the second one is connected to the Voice DAC.
@@ -52,6 +56,8 @@ Example:
audio-codec {
#sound-dai-cells = <1>;
+ interrupts-extended = <&cpcap 9 0>, <&cpcap 10 0>;
+ interrupt-names = "hs", "mb2";
/* HiFi */
port@0 {
diff --git a/Documentation/devicetree/bindings/misc/atmel-ssc.txt b/Documentation/devicetree/bindings/misc/atmel-ssc.txt
deleted file mode 100644
index f9fb412642fe..000000000000
--- a/Documentation/devicetree/bindings/misc/atmel-ssc.txt
+++ /dev/null
@@ -1,50 +0,0 @@
-* Atmel SSC driver.
-
-Required properties:
-- compatible: "atmel,at91rm9200-ssc" or "atmel,at91sam9g45-ssc"
- - atmel,at91rm9200-ssc: support pdc transfer
- - atmel,at91sam9g45-ssc: support dma transfer
-- reg: Should contain SSC registers location and length
-- interrupts: Should contain SSC interrupt
-- clock-names: tuple listing input clock names.
- Required elements: "pclk"
-- clocks: phandles to input clocks.
-
-
-Required properties for devices compatible with "atmel,at91sam9g45-ssc":
-- dmas: DMA specifier, consisting of a phandle to DMA controller node,
- the memory interface and SSC DMA channel ID (for tx and rx).
- See Documentation/devicetree/bindings/dma/atmel-dma.txt for details.
-- dma-names: Must be "tx", "rx".
-
-Optional properties:
- - atmel,clk-from-rk-pin: bool property.
- - When SSC works in slave mode, according to the hardware design, the
- clock can get from TK pin, and also can get from RK pin. So, add
- this parameter to choose where the clock from.
- - By default the clock is from TK pin, if the clock from RK pin, this
- property is needed.
- - #sound-dai-cells: Should contain <0>.
- - This property makes the SSC into an automatically registered DAI.
-
-Examples:
-- PDC transfer:
-ssc0: ssc@fffbc000 {
- compatible = "atmel,at91rm9200-ssc";
- reg = <0xfffbc000 0x4000>;
- interrupts = <14 4 5>;
- clocks = <&ssc0_clk>;
- clock-names = "pclk";
-};
-
-- DMA transfer:
-ssc0: ssc@f0010000 {
- compatible = "atmel,at91sam9g45-ssc";
- reg = <0xf0010000 0x4000>;
- interrupts = <28 4 5>;
- dmas = <&dma0 1 13>,
- <&dma0 1 14>;
- dma-names = "tx", "rx";
- pinctrl-names = "default";
- pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
-};
diff --git a/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml b/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml
new file mode 100644
index 000000000000..a05e61431824
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/atmel,at91-ssc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Atmel Serial Synchronous Serial (SSC)
+
+maintainers:
+ - Andrei Simion <andrei.simion@microchip.com>
+
+description:
+ The Atmel Synchronous Serial Controller (SSC) provides a versatile
+ synchronous communication link for audio and telecom applications,
+ supporting protocols like I2S, Short Frame Sync, and Long Frame Sync.
+
+properties:
+ compatible:
+ enum:
+ - atmel,at91rm9200-ssc
+ - atmel,at91sam9g45-ssc
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ items:
+ - const: pclk
+
+ dmas:
+ items:
+ - description: TX DMA Channel
+ - description: RX DMA Channel
+
+ dma-names:
+ items:
+ - const: tx
+ - const: rx
+
+ atmel,clk-from-rk-pin:
+ description:
+ Specify the clock source for the SSC (Synchronous Serial Controller)
+ when operating in slave mode. By default, the clock is sourced from
+ the TK pin.
+ type: boolean
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - clocks
+ - clock-names
+
+allOf:
+ - $ref: dai-common.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - atmel,at91sam9g45-ssc
+ then:
+ required:
+ - dmas
+ - dma-names
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/at91.h>
+ #include <dt-bindings/dma/at91.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ ssc@100000 {
+ compatible = "atmel,at91sam9g45-ssc";
+ reg = <0x100000 0x4000>;
+ interrupts = <28 IRQ_TYPE_LEVEL_HIGH 5>;
+ dmas = <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(38))>,
+ <&dma0 (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+ AT91_XDMAC_DT_PERID(39))>;
+ dma-names = "tx", "rx";
+ clocks = <&pmc PMC_TYPE_PERIPHERAL 28>;
+ clock-names = "pclk";
+ #sound-dai-cells = <0>;
+ };
+
+ ssc@c00000 {
+ compatible = "atmel,at91rm9200-ssc";
+ reg = <0xc00000 0x4000>;
+ interrupts = <14 IRQ_TYPE_LEVEL_HIGH 5>;
+ clocks = <&pmc PMC_TYPE_PERIPHERAL 14>;
+ clock-names = "pclk";
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml
index d3cf8f59cb23..c8543f969ebb 100644
--- a/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,wcd937x-sdw.yaml
@@ -58,6 +58,40 @@ properties:
items:
enum: [1, 2, 3, 4, 5]
+ qcom,tx-channel-mapping:
+ description: |
+ Specifies static channel mapping between slave and master tx port
+ channels.
+ In the order of slave port channels which is adc1, adc2, adc3,
+ dmic0, dmic1, mbhc, dmic2, dmic3, dmci4, dmic5, dmic6, dmic7.
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 12
+ maxItems: 12
+ additionalItems: false
+ items:
+ enum:
+ - 1 # WCD9370_SWRM_CH1
+ - 2 # WCD9370_SWRM_CH2
+ - 3 # WCD9370_SWRM_CH3
+ - 4 # WCD9370_SWRM_CH4
+
+ qcom,rx-channel-mapping:
+ description: |
+ Specifies static channels mapping between slave and master rx port
+ channels.
+ In the order of slave port channels, which is
+ hph_l, hph_r, clsh, comp_l, comp_r, lo, dsd_r, dsd_l.
+ $ref: /schemas/types.yaml#/definitions/uint8-array
+ minItems: 8
+ maxItems: 8
+ additionalItems: false
+ items:
+ enum:
+ - 1 # WCD9370_SWRM_CH1
+ - 2 # WCD9370_SWRM_CH2
+ - 3 # WCD9370_SWRM_CH3
+ - 4 # WCD9370_SWRM_CH4
+
required:
- compatible
- reg
@@ -74,6 +108,7 @@ examples:
compatible = "sdw20217010a00";
reg = <0 4>;
qcom,rx-port-mapping = <1 2 3 4 5>;
+ qcom,rx-channel-mapping = /bits/ 8 <1 2 1 1 2 1 1 2>;
};
};
@@ -85,6 +120,7 @@ examples:
compatible = "sdw20217010a00";
reg = <0 3>;
qcom,tx-port-mapping = <2 2 3 4>;
+ qcom,tx-channel-mapping = /bits/ 8 <1 2 1 1 2 3 3 4 1 2 3 4>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
index c3c989ef2a2c..32dea7392e8d 100644
--- a/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
+++ b/Documentation/devicetree/bindings/sound/rockchip-spdif.yaml
@@ -31,6 +31,10 @@ properties:
- rockchip,rk3288-spdif
- rockchip,rk3308-spdif
- const: rockchip,rk3066-spdif
+ - items:
+ - enum:
+ - rockchip,rk3588-spdif
+ - const: rockchip,rk3568-spdif
reg:
maxItems: 1
diff --git a/MAINTAINERS b/MAINTAINERS
index 896a307fa065..c8b5ef22cf05 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15632,7 +15632,7 @@ M: Claudiu Beznea <claudiu.beznea@tuxon.dev>
M: Andrei Simion <andrei.simion@microchip.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Supported
-F: Documentation/devicetree/bindings/misc/atmel-ssc.txt
+F: Documentation/devicetree/bindings/sound/atmel,at91-ssc.yaml
F: drivers/misc/atmel-ssc.c
F: include/linux/atmel-ssc.h
@@ -19149,6 +19149,7 @@ F: Documentation/devicetree/bindings/soc/qcom/qcom,apr*
F: Documentation/devicetree/bindings/sound/qcom,*
F: drivers/soc/qcom/apr.c
F: include/dt-bindings/sound/qcom,wcd9335.h
+F: include/dt-bindings/sound/qcom,wcd934x.h
F: sound/soc/codecs/lpass-rx-macro.*
F: sound/soc/codecs/lpass-tx-macro.*
F: sound/soc/codecs/lpass-va-macro.c
diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c
index 0f45e3404756..295a46dc2be7 100644
--- a/drivers/soundwire/qcom.c
+++ b/drivers/soundwire/qcom.c
@@ -156,6 +156,7 @@ struct qcom_swrm_port_config {
u8 word_length;
u8 blk_group_count;
u8 lane_control;
+ u8 ch_mask;
};
/*
@@ -1048,9 +1049,13 @@ static int qcom_swrm_port_enable(struct sdw_bus *bus,
{
u32 reg = SWRM_DP_PORT_CTRL_BANK(enable_ch->port_num, bank);
struct qcom_swrm_ctrl *ctrl = to_qcom_sdw(bus);
+ struct qcom_swrm_port_config *pcfg;
u32 val;
+ pcfg = &ctrl->pconfig[enable_ch->port_num];
ctrl->reg_read(ctrl, reg, &val);
+ if (pcfg->ch_mask != SWR_INVALID_PARAM && pcfg->ch_mask != 0)
+ enable_ch->ch_mask = pcfg->ch_mask;
if (enable_ch->enable)
val |= (enable_ch->ch_mask << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
@@ -1270,6 +1275,26 @@ static void *qcom_swrm_get_sdw_stream(struct snd_soc_dai *dai, int direction)
return ctrl->sruntime[dai->id];
}
+static int qcom_swrm_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, const unsigned int *tx_slot,
+ unsigned int rx_num, const unsigned int *rx_slot)
+{
+ struct qcom_swrm_ctrl *ctrl = dev_get_drvdata(dai->dev);
+ int i;
+
+ if (tx_slot) {
+ for (i = 0; i < tx_num; i++)
+ ctrl->pconfig[i].ch_mask = tx_slot[i];
+ }
+
+ if (rx_slot) {
+ for (i = 0; i < rx_num; i++)
+ ctrl->pconfig[i].ch_mask = rx_slot[i];
+ }
+
+ return 0;
+}
+
static int qcom_swrm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -1306,6 +1331,7 @@ static const struct snd_soc_dai_ops qcom_swrm_pdm_dai_ops = {
.shutdown = qcom_swrm_shutdown,
.set_stream = qcom_swrm_set_sdw_stream,
.get_stream = qcom_swrm_get_sdw_stream,
+ .set_channel_map = qcom_swrm_set_channel_map,
};
static const struct snd_soc_component_driver qcom_swrm_dai_component = {
diff --git a/include/dt-bindings/sound/qcom,wcd934x.h b/include/dt-bindings/sound/qcom,wcd934x.h
new file mode 100644
index 000000000000..8b30d34fcc87
--- /dev/null
+++ b/include/dt-bindings/sound/qcom,wcd934x.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef __DT_SOUND_QCOM_WCD934x_H
+#define __DT_SOUND_QCOM_WCD934x_H
+
+#define AIF1_PB 0
+#define AIF1_CAP 1
+#define AIF2_PB 2
+#define AIF2_CAP 3
+#define AIF3_PB 4
+#define AIF3_CAP 5
+#define AIF4_PB 6
+#define AIF4_VIFEED 7
+#define AIF4_MAD_TX 8
+
+#endif
diff --git a/include/sound/sdca.h b/include/sound/sdca.h
index 973252d0adac..5a5d6de78d72 100644
--- a/include/sound/sdca.h
+++ b/include/sound/sdca.h
@@ -17,29 +17,31 @@ struct sdw_slave;
#define SDCA_MAX_FUNCTION_COUNT 8
/**
- * sdca_device_desc - short descriptor for an SDCA Function
- * @adr: ACPI address (used for SDCA register access)
- * @type: Function topology type
- * @name: human-readable string
+ * struct sdca_function_desc - short descriptor for an SDCA Function
+ * @node: firmware node for the Function.
+ * @name: Human-readable string.
+ * @type: Function topology type.
+ * @adr: ACPI address (used for SDCA register access).
*/
struct sdca_function_desc {
+ struct fwnode_handle *node;
const char *name;
u32 type;
u8 adr;
};
/**
- * sdca_device_data - structure containing all SDCA related information
- * @sdca_interface_revision: value read from _DSD property, mainly to check
- * for changes between silicon versions
- * @num_functions: total number of supported SDCA functions. Invalid/unsupported
+ * struct sdca_device_data - structure containing all SDCA related information
+ * @interface_revision: Value read from _DSD property, mainly to check
+ * for changes between silicon versions.
+ * @num_functions: Total number of supported SDCA functions. Invalid/unsupported
* functions will be skipped.
- * @sdca_func: array of function descriptors
+ * @function: Array of function descriptors.
*/
struct sdca_device_data {
u32 interface_revision;
int num_functions;
- struct sdca_function_desc sdca_func[SDCA_MAX_FUNCTION_COUNT];
+ struct sdca_function_desc function[SDCA_MAX_FUNCTION_COUNT];
};
enum sdca_quirk {
diff --git a/include/sound/sdca_function.h b/include/sound/sdca_function.h
index c051c17903e8..f001ab643fed 100644
--- a/include/sound/sdca_function.h
+++ b/include/sound/sdca_function.h
@@ -10,40 +10,437 @@
#define __SDCA_FUNCTION_H__
#include <linux/bits.h>
+#include <linux/types.h>
+
+struct device;
+struct sdca_entity;
+struct sdca_function_desc;
+
+/*
+ * The addressing space for SDCA relies on 7 bits for Entities, so a
+ * maximum of 128 Entities per function can be represented.
+ */
+#define SDCA_MAX_ENTITY_COUNT 128
+
+/*
+ * Sanity check on number of initialization writes, can be expanded if needed.
+ */
+#define SDCA_MAX_INIT_COUNT 2048
+
+/*
+ * The Cluster IDs are 16-bit, so a maximum of 65535 Clusters per
+ * function can be represented, however limit this to a slightly
+ * more reasonable value. Can be expanded if needed.
+ */
+#define SDCA_MAX_CLUSTER_COUNT 256
/*
+ * Sanity check on number of channels per Cluster, can be expanded if needed.
+ */
+#define SDCA_MAX_CHANNEL_COUNT 32
+
+/*
+ * Sanity check on number of PDE delays, can be expanded if needed.
+ */
+#define SDCA_MAX_DELAY_COUNT 256
+
+/**
+ * enum sdca_function_type - SDCA Function Type codes
+ * @SDCA_FUNCTION_TYPE_SMART_AMP: Amplifier with protection features.
+ * @SDCA_FUNCTION_TYPE_SIMPLE_AMP: Subset of SmartAmp.
+ * @SDCA_FUNCTION_TYPE_SMART_MIC: Smart microphone with acoustic triggers.
+ * @SDCA_FUNCTION_TYPE_SIMPLE_MIC: Subset of SmartMic.
+ * @SDCA_FUNCTION_TYPE_SPEAKER_MIC: Combination of SmartMic and SmartAmp.
+ * @SDCA_FUNCTION_TYPE_UAJ: 3.5mm Universal Audio jack.
+ * @SDCA_FUNCTION_TYPE_RJ: Retaskable jack.
+ * @SDCA_FUNCTION_TYPE_SIMPLE_JACK: Subset of UAJ.
+ * @SDCA_FUNCTION_TYPE_HID: Human Interface Device, for e.g. buttons.
+ * @SDCA_FUNCTION_TYPE_IMP_DEF: Implementation-defined function.
+ *
* SDCA Function Types from SDCA specification v1.0a Section 5.1.2
- * all Function types not described are reserved
+ * all Function types not described are reserved.
+ *
* Note that SIMPLE_AMP, SIMPLE_MIC and SIMPLE_JACK Function Types
* are NOT defined in SDCA 1.0a, but they were defined in earlier
* drafts and are planned for 1.1.
*/
-
enum sdca_function_type {
- SDCA_FUNCTION_TYPE_SMART_AMP = 0x01, /* Amplifier with protection features */
- SDCA_FUNCTION_TYPE_SIMPLE_AMP = 0x02, /* subset of SmartAmp */
- SDCA_FUNCTION_TYPE_SMART_MIC = 0x03, /* Smart microphone with acoustic triggers */
- SDCA_FUNCTION_TYPE_SIMPLE_MIC = 0x04, /* subset of SmartMic */
- SDCA_FUNCTION_TYPE_SPEAKER_MIC = 0x05, /* Combination of SmartMic and SmartAmp */
- SDCA_FUNCTION_TYPE_UAJ = 0x06, /* 3.5mm Universal Audio jack */
- SDCA_FUNCTION_TYPE_RJ = 0x07, /* Retaskable jack */
- SDCA_FUNCTION_TYPE_SIMPLE_JACK = 0x08, /* Subset of UAJ */
- SDCA_FUNCTION_TYPE_HID = 0x0A, /* Human Interface Device, for e.g. buttons */
- SDCA_FUNCTION_TYPE_IMP_DEF = 0x1F, /* Implementation-defined function */
+ SDCA_FUNCTION_TYPE_SMART_AMP = 0x01,
+ SDCA_FUNCTION_TYPE_SIMPLE_AMP = 0x02,
+ SDCA_FUNCTION_TYPE_SMART_MIC = 0x03,
+ SDCA_FUNCTION_TYPE_SIMPLE_MIC = 0x04,
+ SDCA_FUNCTION_TYPE_SPEAKER_MIC = 0x05,
+ SDCA_FUNCTION_TYPE_UAJ = 0x06,
+ SDCA_FUNCTION_TYPE_RJ = 0x07,
+ SDCA_FUNCTION_TYPE_SIMPLE_JACK = 0x08,
+ SDCA_FUNCTION_TYPE_HID = 0x0A,
+ SDCA_FUNCTION_TYPE_IMP_DEF = 0x1F,
};
/* Human-readable names used for kernel logs and Function device registration/bind */
-#define SDCA_FUNCTION_TYPE_SMART_AMP_NAME "SmartAmp"
-#define SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME "SimpleAmp"
-#define SDCA_FUNCTION_TYPE_SMART_MIC_NAME "SmartMic"
-#define SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME "SimpleMic"
-#define SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME "SpeakerMic"
-#define SDCA_FUNCTION_TYPE_UAJ_NAME "UAJ"
-#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ"
-#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack"
-#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
-#define SDCA_FUNCTION_TYPE_IMP_DEF_NAME "ImplementationDefined"
+#define SDCA_FUNCTION_TYPE_SMART_AMP_NAME "SmartAmp"
+#define SDCA_FUNCTION_TYPE_SIMPLE_AMP_NAME "SimpleAmp"
+#define SDCA_FUNCTION_TYPE_SMART_MIC_NAME "SmartMic"
+#define SDCA_FUNCTION_TYPE_SIMPLE_MIC_NAME "SimpleMic"
+#define SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME "SpeakerMic"
+#define SDCA_FUNCTION_TYPE_UAJ_NAME "UAJ"
+#define SDCA_FUNCTION_TYPE_RJ_NAME "RJ"
+#define SDCA_FUNCTION_TYPE_SIMPLE_NAME "SimpleJack"
+#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
+#define SDCA_FUNCTION_TYPE_IMP_DEF_NAME "ImplementationDefined"
+
+/**
+ * struct sdca_init_write - a single initialization write
+ * @addr: Register address to be written
+ * @val: Single byte value to be written
+ */
+struct sdca_init_write {
+ u32 addr;
+ u8 val;
+};
+
+/**
+ * define SDCA_CTL_TYPE - create a unique identifier for an SDCA Control
+ * @ent: Entity Type code.
+ * @sel: Control Selector code.
+ *
+ * Sometimes there is a need to identify a type of Control, for example to
+ * determine what name the control should have. SDCA Selectors are reused
+ * across Entity types, as such it is necessary to combine both the Entity
+ * Type and the Control Selector to obtain a unique identifier.
+ */
+#define SDCA_CTL_TYPE(ent, sel) ((ent) << 8 | (sel))
+
+/**
+ * define SDCA_CTL_TYPE_S - static version of SDCA_CTL_TYPE
+ * @ent: Entity name, for example IT, MFPU, etc. this string can be read
+ * from the last characters of the SDCA_ENTITY_TYPE_* macros.
+ * @sel: Control Selector name, for example MIC_BIAS, MUTE, etc. this
+ * string can be read from the last characters of the SDCA_CTL_*_*
+ * macros.
+ *
+ * Short hand to specific a Control type statically for example:
+ * SDAC_CTL_TYPE_S(IT, MIC_BIAS).
+ */
+#define SDCA_CTL_TYPE_S(ent, sel) SDCA_CTL_TYPE(SDCA_ENTITY_TYPE_##ent, \
+ SDCA_CTL_##ent##_##sel)
+
+/**
+ * enum sdca_it_controls - SDCA Controls for Input Terminal
+ *
+ * Control Selectors for Input Terminal from SDCA specification v1.0
+ * section 6.2.1.3.
+ */
+enum sdca_it_controls {
+ SDCA_CTL_IT_MIC_BIAS = 0x03,
+ SDCA_CTL_IT_USAGE = 0x04,
+ SDCA_CTL_IT_LATENCY = 0x08,
+ SDCA_CTL_IT_CLUSTERINDEX = 0x10,
+ SDCA_CTL_IT_DATAPORT_SELECTOR = 0x11,
+ SDCA_CTL_IT_MATCHING_GUID = 0x12,
+ SDCA_CTL_IT_KEEP_ALIVE = 0x13,
+ SDCA_CTL_IT_NDAI_STREAM = 0x14,
+ SDCA_CTL_IT_NDAI_CATEGORY = 0x15,
+ SDCA_CTL_IT_NDAI_CODINGTYPE = 0x16,
+ SDCA_CTL_IT_NDAI_PACKETTYPE = 0x17,
+};
+
+/**
+ * enum sdca_ot_controls - SDCA Controls for Output Terminal
+ *
+ * Control Selectors for Output Terminal from SDCA specification v1.0
+ * section 6.2.2.3.
+ */
+enum sdca_ot_controls {
+ SDCA_CTL_OT_USAGE = 0x04,
+ SDCA_CTL_OT_LATENCY = 0x08,
+ SDCA_CTL_OT_DATAPORT_SELECTOR = 0x11,
+ SDCA_CTL_OT_MATCHING_GUID = 0x12,
+ SDCA_CTL_OT_KEEP_ALIVE = 0x13,
+ SDCA_CTL_OT_NDAI_STREAM = 0x14,
+ SDCA_CTL_OT_NDAI_CATEGORY = 0x15,
+ SDCA_CTL_OT_NDAI_CODINGTYPE = 0x16,
+ SDCA_CTL_OT_NDAI_PACKETTYPE = 0x17,
+};
+
+/**
+ * enum sdca_mu_controls - SDCA Controls for Mixer Unit
+ *
+ * Control Selectors for Mixer Unit from SDCA specification v1.0
+ * section 6.3.4.2.
+ */
+enum sdca_mu_controls {
+ SDCA_CTL_MU_MIXER = 0x01,
+ SDCA_CTL_MU_LATENCY = 0x06,
+};
+
+/**
+ * enum sdca_su_controls - SDCA Controls for Selector Unit
+ *
+ * Control Selectors for Selector Unit from SDCA specification v1.0
+ * section 6.3.8.3.
+ */
+enum sdca_su_controls {
+ SDCA_CTL_SU_SELECTOR = 0x01,
+ SDCA_CTL_SU_LATENCY = 0x02,
+};
+
+/**
+ * enum sdca_fu_controls - SDCA Controls for Feature Unit
+ *
+ * Control Selectors for Feature Unit from SDCA specification v1.0
+ * section 6.3.2.3.
+ */
+enum sdca_fu_controls {
+ SDCA_CTL_FU_MUTE = 0x01,
+ SDCA_CTL_FU_CHANNEL_VOLUME = 0x02,
+ SDCA_CTL_FU_AGC = 0x07,
+ SDCA_CTL_FU_BASS_BOOST = 0x09,
+ SDCA_CTL_FU_LOUDNESS = 0x0A,
+ SDCA_CTL_FU_GAIN = 0x0B,
+ SDCA_CTL_FU_LATENCY = 0x10,
+};
+
+/**
+ * enum sdca_xu_controls - SDCA Controls for Extension Unit
+ *
+ * Control Selectors for Extension Unit from SDCA specification v1.0
+ * section 6.3.10.3.
+ */
+enum sdca_xu_controls {
+ SDCA_CTL_XU_BYPASS = 0x01,
+ SDCA_CTL_XU_LATENCY = 0x06,
+ SDCA_CTL_XU_XU_ID = 0x07,
+ SDCA_CTL_XU_XU_VERSION = 0x08,
+ SDCA_CTL_XU_FDL_CURRENTOWNER = 0x10,
+ SDCA_CTL_XU_FDL_MESSAGEOFFSET = 0x12,
+ SDCA_CTL_XU_FDL_MESSAGELENGTH = 0x13,
+ SDCA_CTL_XU_FDL_STATUS = 0x14,
+ SDCA_CTL_XU_FDL_SET_INDEX = 0x15,
+ SDCA_CTL_XU_FDL_HOST_REQUEST = 0x16,
+};
+
+/**
+ * enum sdca_cs_controls - SDCA Controls for Clock Source
+ *
+ * Control Selectors for Clock Source from SDCA specification v1.0
+ * section 6.4.1.3.
+ */
+enum sdca_cs_controls {
+ SDCA_CTL_CS_CLOCK_VALID = 0x02,
+ SDCA_CTL_CS_SAMPLERATEINDEX = 0x10,
+};
+
+/**
+ * enum sdca_cx_controls - SDCA Controls for Clock Selector
+ *
+ * Control Selectors for Clock Selector from SDCA specification v1.0
+ * section 6.4.2.3.
+ */
+enum sdca_cx_controls {
+ SDCA_CTL_CX_CLOCK_SELECT = 0x01,
+};
+
+/**
+ * enum sdca_pde_controls - SDCA Controls for Power Domain Entity
+ *
+ * Control Selectors for Power Domain Entity from SDCA specification
+ * v1.0 section 6.5.2.2.
+ */
+enum sdca_pde_controls {
+ SDCA_CTL_PDE_REQUESTED_PS = 0x01,
+ SDCA_CTL_PDE_ACTUAL_PS = 0x10,
+};
+
+/**
+ * enum sdca_ge_controls - SDCA Controls for Group Unit
+ *
+ * Control Selectors for Group Unit from SDCA specification v1.0
+ * section 6.5.1.4.
+ */
+enum sdca_ge_controls {
+ SDCA_CTL_GE_SELECTED_MODE = 0x01,
+ SDCA_CTL_GE_DETECTED_MODE = 0x02,
+};
+
+/**
+ * enum sdca_spe_controls - SDCA Controls for Security & Privacy Unit
+ *
+ * Control Selectors for Security & Privacy Unit from SDCA
+ * specification v1.0 Section 6.5.3.2.
+ */
+enum sdca_spe_controls {
+ SDCA_CTL_SPE_PRIVATE = 0x01,
+ SDCA_CTL_SPE_PRIVACY_POLICY = 0x02,
+ SDCA_CTL_SPE_PRIVACY_LOCKSTATE = 0x03,
+ SDCA_CTL_SPE_PRIVACY_OWNER = 0x04,
+ SDCA_CTL_SPE_AUTHTX_CURRENTOWNER = 0x10,
+ SDCA_CTL_SPE_AUTHTX_MESSAGEOFFSET = 0x12,
+ SDCA_CTL_SPE_AUTHTX_MESSAGELENGTH = 0x13,
+ SDCA_CTL_SPE_AUTHRX_CURRENTOWNER = 0x14,
+ SDCA_CTL_SPE_AUTHRX_MESSAGEOFFSET = 0x16,
+ SDCA_CTL_SPE_AUTHRX_MESSAGELENGTH = 0x17,
+};
+
+/**
+ * enum sdca_cru_controls - SDCA Controls for Channel Remapping Unit
+ *
+ * Control Selectors for Channel Remapping Unit from SDCA
+ * specification v1.0 Section 6.3.1.3.
+ */
+enum sdca_cru_controls {
+ SDCA_CTL_CRU_LATENCY = 0x06,
+ SDCA_CTL_CRU_CLUSTERINDEX = 0x10,
+};
+
+/**
+ * enum sdca_udmpu_controls - SDCA Controls for Up-Down Mixer Processing Unit
+ *
+ * Control Selectors for Up-Down Mixer Processing Unit from SDCA
+ * specification v1.0 Section 6.3.9.3.
+ */
+enum sdca_udmpu_controls {
+ SDCA_CTL_UDMPU_LATENCY = 0x06,
+ SDCA_CTL_UDMPU_CLUSTERINDEX = 0x10,
+ SDCA_CTL_UDMPU_ACOUSTIC_ENERGY_LEVEL_MONITOR = 0x11,
+ SDCA_CTL_UDMPU_ULTRASOUND_LOOP_GAIN = 0x12,
+ SDCA_CTL_UDMPU_OPAQUESET_0 = 0x18,
+ SDCA_CTL_UDMPU_OPAQUESET_1 = 0x19,
+ SDCA_CTL_UDMPU_OPAQUESET_2 = 0x1A,
+ SDCA_CTL_UDMPU_OPAQUESET_3 = 0x1B,
+ SDCA_CTL_UDMPU_OPAQUESET_4 = 0x1C,
+ SDCA_CTL_UDMPU_OPAQUESET_5 = 0x1D,
+ SDCA_CTL_UDMPU_OPAQUESET_6 = 0x1E,
+ SDCA_CTL_UDMPU_OPAQUESET_7 = 0x1F,
+ SDCA_CTL_UDMPU_OPAQUESET_8 = 0x20,
+ SDCA_CTL_UDMPU_OPAQUESET_9 = 0x21,
+ SDCA_CTL_UDMPU_OPAQUESET_10 = 0x22,
+ SDCA_CTL_UDMPU_OPAQUESET_11 = 0x23,
+ SDCA_CTL_UDMPU_OPAQUESET_12 = 0x24,
+ SDCA_CTL_UDMPU_OPAQUESET_13 = 0x25,
+ SDCA_CTL_UDMPU_OPAQUESET_14 = 0x26,
+ SDCA_CTL_UDMPU_OPAQUESET_15 = 0x27,
+ SDCA_CTL_UDMPU_OPAQUESET_16 = 0x28,
+ SDCA_CTL_UDMPU_OPAQUESET_17 = 0x29,
+ SDCA_CTL_UDMPU_OPAQUESET_18 = 0x2A,
+ SDCA_CTL_UDMPU_OPAQUESET_19 = 0x2B,
+ SDCA_CTL_UDMPU_OPAQUESET_20 = 0x2C,
+ SDCA_CTL_UDMPU_OPAQUESET_21 = 0x2D,
+ SDCA_CTL_UDMPU_OPAQUESET_22 = 0x2E,
+ SDCA_CTL_UDMPU_OPAQUESET_23 = 0x2F,
+};
+
+/**
+ * enum sdca_mfpu_controls - SDCA Controls for Multi-Function Processing Unit
+ *
+ * Control Selectors for Multi-Function Processing Unit from SDCA
+ * specification v1.0 Section 6.3.3.4.
+ */
+enum sdca_mfpu_controls {
+ SDCA_CTL_MFPU_BYPASS = 0x01,
+ SDCA_CTL_MFPU_ALGORITHM_READY = 0x04,
+ SDCA_CTL_MFPU_ALGORITHM_ENABLE = 0x05,
+ SDCA_CTL_MFPU_LATENCY = 0x08,
+ SDCA_CTL_MFPU_ALGORITHM_PREPARE = 0x09,
+ SDCA_CTL_MFPU_CLUSTERINDEX = 0x10,
+ SDCA_CTL_MFPU_CENTER_FREQUENCY_INDEX = 0x11,
+ SDCA_CTL_MFPU_ULTRASOUND_LEVEL = 0x12,
+ SDCA_CTL_MFPU_AE_NUMBER = 0x13,
+ SDCA_CTL_MFPU_AE_CURRENTOWNER = 0x14,
+ SDCA_CTL_MFPU_AE_MESSAGEOFFSET = 0x16,
+ SDCA_CTL_MFPU_AE_MESSAGELENGTH = 0x17,
+};
+
+/**
+ * enum sdca_smpu_controls - SDCA Controls for Smart Mic Processing Unit
+ *
+ * Control Selectors for Smart Mic Processing Unit from SDCA
+ * specification v1.0 Section 6.3.7.3.
+ */
+enum sdca_smpu_controls {
+ SDCA_CTL_SMPU_LATENCY = 0x06,
+ SDCA_CTL_SMPU_TRIGGER_ENABLE = 0x10,
+ SDCA_CTL_SMPU_TRIGGER_STATUS = 0x11,
+ SDCA_CTL_SMPU_HIST_BUFFER_MODE = 0x12,
+ SDCA_CTL_SMPU_HIST_BUFFER_PREAMBLE = 0x13,
+ SDCA_CTL_SMPU_HIST_ERROR = 0x14,
+ SDCA_CTL_SMPU_TRIGGER_EXTENSION = 0x15,
+ SDCA_CTL_SMPU_TRIGGER_READY = 0x16,
+ SDCA_CTL_SMPU_HIST_CURRENTOWNER = 0x18,
+ SDCA_CTL_SMPU_HIST_MESSAGEOFFSET = 0x1A,
+ SDCA_CTL_SMPU_HIST_MESSAGELENGTH = 0x1B,
+ SDCA_CTL_SMPU_DTODTX_CURRENTOWNER = 0x1C,
+ SDCA_CTL_SMPU_DTODTX_MESSAGEOFFSET = 0x1E,
+ SDCA_CTL_SMPU_DTODTX_MESSAGELENGTH = 0x1F,
+ SDCA_CTL_SMPU_DTODRX_CURRENTOWNER = 0x20,
+ SDCA_CTL_SMPU_DTODRX_MESSAGEOFFSET = 0x22,
+ SDCA_CTL_SMPU_DTODRX_MESSAGELENGTH = 0x23,
+};
+
+/**
+ * enum sdca_sapu_controls - SDCA Controls for Smart Amp Processing Unit
+ *
+ * Control Selectors for Smart Amp Processing Unit from SDCA
+ * specification v1.0 Section 6.3.6.3.
+ */
+enum sdca_sapu_controls {
+ SDCA_CTL_SAPU_LATENCY = 0x05,
+ SDCA_CTL_SAPU_PROTECTION_MODE = 0x10,
+ SDCA_CTL_SAPU_PROTECTION_STATUS = 0x11,
+ SDCA_CTL_SAPU_OPAQUESETREQ_INDEX = 0x12,
+ SDCA_CTL_SAPU_DTODTX_CURRENTOWNER = 0x14,
+ SDCA_CTL_SAPU_DTODTX_MESSAGEOFFSET = 0x16,
+ SDCA_CTL_SAPU_DTODTX_MESSAGELENGTH = 0x17,
+ SDCA_CTL_SAPU_DTODRX_CURRENTOWNER = 0x18,
+ SDCA_CTL_SAPU_DTODRX_MESSAGEOFFSET = 0x1A,
+ SDCA_CTL_SAPU_DTODRX_MESSAGELENGTH = 0x1B,
+};
+/**
+ * enum sdca_ppu_controls - SDCA Controls for Post Processing Unit
+ *
+ * Control Selectors for Post Processing Unit from SDCA specification
+ * v1.0 Section 6.3.5.3.
+ */
+enum sdca_ppu_controls {
+ SDCA_CTL_PPU_LATENCY = 0x06,
+ SDCA_CTL_PPU_POSTURENUMBER = 0x10,
+ SDCA_CTL_PPU_POSTUREEXTENSION = 0x11,
+ SDCA_CTL_PPU_HORIZONTALBALANCE = 0x12,
+ SDCA_CTL_PPU_VERTICALBALANCE = 0x13,
+};
+
+/**
+ * enum sdca_tg_controls - SDCA Controls for Tone Generator Entity
+ *
+ * Control Selectors for Tone Generator from SDCA specification v1.0
+ * Section 6.5.4.4.
+ */
+enum sdca_tg_controls {
+ SDCA_CTL_TG_TONE_DIVIDER = 0x10,
+};
+
+/**
+ * enum sdca_hide_controls - SDCA Controls for HIDE Entity
+ *
+ * Control Selectors for HIDE from SDCA specification v1.0 Section
+ * 6.6.1.2.
+ */
+enum sdca_hide_controls {
+ SDCA_CTL_HIDE_HIDTX_CURRENTOWNER = 0x10,
+ SDCA_CTL_HIDE_HIDTX_MESSAGEOFFSET = 0x12,
+ SDCA_CTL_HIDE_HIDTX_MESSAGELENGTH = 0x13,
+ SDCA_CTL_HIDE_HIDRX_CURRENTOWNER = 0x14,
+ SDCA_CTL_HIDE_HIDRX_MESSAGEOFFSET = 0x16,
+ SDCA_CTL_HIDE_HIDRX_MESSAGELENGTH = 0x17,
+};
+
+/**
+ * enum sdca_entity0_controls - SDCA Controls for Entity 0
+ *
+ * Control Selectors for Entity 0 from SDCA specification v1.0 Section
+ * 6.7.1.1.
+ */
enum sdca_entity0_controls {
SDCA_CTL_ENTITY_0_COMMIT_GROUP_MASK = 0x01,
SDCA_CTL_ENTITY_0_FUNCTION_SDCA_VERSION = 0x04,
@@ -72,4 +469,652 @@ enum sdca_entity0_controls {
SDCA_CTL_ENTITY_0_FUNCTION_BUSY = BIT(7),
};
+#define SDCA_CTL_MIC_BIAS_NAME "Mic Bias"
+#define SDCA_CTL_USAGE_NAME "Usage"
+#define SDCA_CTL_LATENCY_NAME "Latency"
+#define SDCA_CTL_CLUSTERINDEX_NAME "Cluster Index"
+#define SDCA_CTL_DATAPORT_SELECTOR_NAME "Dataport Selector"
+#define SDCA_CTL_MATCHING_GUID_NAME "Matching GUID"
+#define SDCA_CTL_KEEP_ALIVE_NAME "Keep Alive"
+#define SDCA_CTL_NDAI_STREAM_NAME "NDAI Stream"
+#define SDCA_CTL_NDAI_CATEGORY_NAME "NDAI Category"
+#define SDCA_CTL_NDAI_CODINGTYPE_NAME "NDAI Coding Type"
+#define SDCA_CTL_NDAI_PACKETTYPE_NAME "NDAI Packet Type"
+#define SDCA_CTL_MIXER_NAME "Mixer"
+#define SDCA_CTL_SELECTOR_NAME "Selector"
+#define SDCA_CTL_MUTE_NAME "Mute"
+#define SDCA_CTL_CHANNEL_VOLUME_NAME "Channel Volume"
+#define SDCA_CTL_AGC_NAME "AGC"
+#define SDCA_CTL_BASS_BOOST_NAME "Bass Boost"
+#define SDCA_CTL_LOUDNESS_NAME "Loudness"
+#define SDCA_CTL_GAIN_NAME "Gain"
+#define SDCA_CTL_BYPASS_NAME "Bypass"
+#define SDCA_CTL_XU_ID_NAME "XU ID"
+#define SDCA_CTL_XU_VERSION_NAME "XU Version"
+#define SDCA_CTL_FDL_CURRENTOWNER_NAME "FDL Current Owner"
+#define SDCA_CTL_FDL_MESSAGEOFFSET_NAME "FDL Message Offset"
+#define SDCA_CTL_FDL_MESSAGELENGTH_NAME "FDL Message Length"
+#define SDCA_CTL_FDL_STATUS_NAME "FDL Status"
+#define SDCA_CTL_FDL_SET_INDEX_NAME "FDL Set Index"
+#define SDCA_CTL_FDL_HOST_REQUEST_NAME "FDL Host Request"
+#define SDCA_CTL_CLOCK_VALID_NAME "Clock Valid"
+#define SDCA_CTL_SAMPLERATEINDEX_NAME "Sample Rate Index"
+#define SDCA_CTL_CLOCK_SELECT_NAME "Clock Select"
+#define SDCA_CTL_REQUESTED_PS_NAME "Requested PS"
+#define SDCA_CTL_ACTUAL_PS_NAME "Actual PS"
+#define SDCA_CTL_SELECTED_MODE_NAME "Selected Mode"
+#define SDCA_CTL_DETECTED_MODE_NAME "Detected Mode"
+#define SDCA_CTL_PRIVATE_NAME "Private"
+#define SDCA_CTL_PRIVACY_POLICY_NAME "Privacy Policy"
+#define SDCA_CTL_PRIVACY_LOCKSTATE_NAME "Privacy Lockstate"
+#define SDCA_CTL_PRIVACY_OWNER_NAME "Privacy Owner"
+#define SDCA_CTL_AUTHTX_CURRENTOWNER_NAME "AuthTX Current Owner"
+#define SDCA_CTL_AUTHTX_MESSAGEOFFSET_NAME "AuthTX Message Offset"
+#define SDCA_CTL_AUTHTX_MESSAGELENGTH_NAME "AuthTX Message Length"
+#define SDCA_CTL_AUTHRX_CURRENTOWNER_NAME "AuthRX Current Owner"
+#define SDCA_CTL_AUTHRX_MESSAGEOFFSET_NAME "AuthRX Message Offset"
+#define SDCA_CTL_AUTHRX_MESSAGELENGTH_NAME "AuthRX Message Length"
+#define SDCA_CTL_ACOUSTIC_ENERGY_LEVEL_MONITOR_NAME "Acoustic Energy Level Monitor"
+#define SDCA_CTL_ULTRASOUND_LOOP_GAIN_NAME "Ultrasound Loop Gain"
+#define SDCA_CTL_OPAQUESET_0_NAME "Opaqueset 0"
+#define SDCA_CTL_OPAQUESET_1_NAME "Opaqueset 1"
+#define SDCA_CTL_OPAQUESET_2_NAME "Opaqueset 2"
+#define SDCA_CTL_OPAQUESET_3_NAME "Opaqueset 3"
+#define SDCA_CTL_OPAQUESET_4_NAME "Opaqueset 4"
+#define SDCA_CTL_OPAQUESET_5_NAME "Opaqueset 5"
+#define SDCA_CTL_OPAQUESET_6_NAME "Opaqueset 6"
+#define SDCA_CTL_OPAQUESET_7_NAME "Opaqueset 7"
+#define SDCA_CTL_OPAQUESET_8_NAME "Opaqueset 8"
+#define SDCA_CTL_OPAQUESET_9_NAME "Opaqueset 9"
+#define SDCA_CTL_OPAQUESET_10_NAME "Opaqueset 10"
+#define SDCA_CTL_OPAQUESET_11_NAME "Opaqueset 11"
+#define SDCA_CTL_OPAQUESET_12_NAME "Opaqueset 12"
+#define SDCA_CTL_OPAQUESET_13_NAME "Opaqueset 13"
+#define SDCA_CTL_OPAQUESET_14_NAME "Opaqueset 14"
+#define SDCA_CTL_OPAQUESET_15_NAME "Opaqueset 15"
+#define SDCA_CTL_OPAQUESET_16_NAME "Opaqueset 16"
+#define SDCA_CTL_OPAQUESET_17_NAME "Opaqueset 17"
+#define SDCA_CTL_OPAQUESET_18_NAME "Opaqueset 18"
+#define SDCA_CTL_OPAQUESET_19_NAME "Opaqueset 19"
+#define SDCA_CTL_OPAQUESET_20_NAME "Opaqueset 20"
+#define SDCA_CTL_OPAQUESET_21_NAME "Opaqueset 21"
+#define SDCA_CTL_OPAQUESET_22_NAME "Opaqueset 22"
+#define SDCA_CTL_OPAQUESET_23_NAME "Opaqueset 23"
+#define SDCA_CTL_ALGORITHM_READY_NAME "Algorithm Ready"
+#define SDCA_CTL_ALGORITHM_ENABLE_NAME "Algorithm Enable"
+#define SDCA_CTL_ALGORITHM_PREPARE_NAME "Algorithm Prepare"
+#define SDCA_CTL_CENTER_FREQUENCY_INDEX_NAME "Center Frequency Index"
+#define SDCA_CTL_ULTRASOUND_LEVEL_NAME "Ultrasound Level"
+#define SDCA_CTL_AE_NUMBER_NAME "AE Number"
+#define SDCA_CTL_AE_CURRENTOWNER_NAME "AE Current Owner"
+#define SDCA_CTL_AE_MESSAGEOFFSET_NAME "AE Message Offset"
+#define SDCA_CTL_AE_MESSAGELENGTH_NAME "AE Message Length"
+#define SDCA_CTL_TRIGGER_ENABLE_NAME "Trigger Enable"
+#define SDCA_CTL_TRIGGER_STATUS_NAME "Trigger Status"
+#define SDCA_CTL_HIST_BUFFER_MODE_NAME "Hist Buffer Mode"
+#define SDCA_CTL_HIST_BUFFER_PREAMBLE_NAME "Hist Buffer Preamble"
+#define SDCA_CTL_HIST_ERROR_NAME "Hist Error"
+#define SDCA_CTL_TRIGGER_EXTENSION_NAME "Trigger Extension"
+#define SDCA_CTL_TRIGGER_READY_NAME "Trigger Ready"
+#define SDCA_CTL_HIST_CURRENTOWNER_NAME "Hist Current Owner"
+#define SDCA_CTL_HIST_MESSAGEOFFSET_NAME "Hist Message Offset"
+#define SDCA_CTL_HIST_MESSAGELENGTH_NAME "Hist Message Length"
+#define SDCA_CTL_DTODTX_CURRENTOWNER_NAME "DTODTX Current Owner"
+#define SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME "DTODTX Message Offset"
+#define SDCA_CTL_DTODTX_MESSAGELENGTH_NAME "DTODTX Message Length"
+#define SDCA_CTL_DTODRX_CURRENTOWNER_NAME "DTODRX Current Owner"
+#define SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME "DTODRX Message Offset"
+#define SDCA_CTL_DTODRX_MESSAGELENGTH_NAME "DTODRX Message Length"
+#define SDCA_CTL_PROTECTION_MODE_NAME "Protection Mode"
+#define SDCA_CTL_PROTECTION_STATUS_NAME "Protection Status"
+#define SDCA_CTL_OPAQUESETREQ_INDEX_NAME "Opaqueset Req Index"
+#define SDCA_CTL_DTODTX_CURRENTOWNER_NAME "DTODTX Current Owner"
+#define SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME "DTODTX Message Offset"
+#define SDCA_CTL_DTODTX_MESSAGELENGTH_NAME "DTODTX Message Length"
+#define SDCA_CTL_DTODRX_CURRENTOWNER_NAME "DTODRX Current Owner"
+#define SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME "DTODRX Message Offset"
+#define SDCA_CTL_DTODRX_MESSAGELENGTH_NAME "DTODRX Message Length"
+#define SDCA_CTL_POSTURENUMBER_NAME "Posture Number"
+#define SDCA_CTL_POSTUREEXTENSION_NAME "Posture Extension"
+#define SDCA_CTL_HORIZONTALBALANCE_NAME "Horizontal Balance"
+#define SDCA_CTL_VERTICALBALANCE_NAME "Vertical Balance"
+#define SDCA_CTL_TONE_DIVIDER_NAME "Tone Divider"
+#define SDCA_CTL_HIDTX_CURRENTOWNER_NAME "HIDTX Current Owner"
+#define SDCA_CTL_HIDTX_MESSAGEOFFSET_NAME "HIDTX Message Offset"
+#define SDCA_CTL_HIDTX_MESSAGELENGTH_NAME "HIDTX Message Length"
+#define SDCA_CTL_HIDRX_CURRENTOWNER_NAME "HIDRX Current Owner"
+#define SDCA_CTL_HIDRX_MESSAGEOFFSET_NAME "HIDRX Message Offset"
+#define SDCA_CTL_HIDRX_MESSAGELENGTH_NAME "HIDRX Message Length"
+#define SDCA_CTL_COMMIT_GROUP_MASK_NAME "Commit Group Mask"
+#define SDCA_CTL_FUNCTION_SDCA_VERSION_NAME "Function SDCA Version"
+#define SDCA_CTL_FUNCTION_TYPE_NAME "Function Type"
+#define SDCA_CTL_FUNCTION_MANUFACTURER_ID_NAME "Function Manufacturer ID"
+#define SDCA_CTL_FUNCTION_ID_NAME "Function ID"
+#define SDCA_CTL_FUNCTION_VERSION_NAME "Function Version"
+#define SDCA_CTL_FUNCTION_EXTENSION_ID_NAME "Function Extension ID"
+#define SDCA_CTL_FUNCTION_EXTENSION_VERSION_NAME "Function Extension Version"
+#define SDCA_CTL_FUNCTION_STATUS_NAME "Function Status"
+#define SDCA_CTL_FUNCTION_ACTION_NAME "Function Action"
+#define SDCA_CTL_DEVICE_MANUFACTURER_ID_NAME "Device Manufacturer ID"
+#define SDCA_CTL_DEVICE_PART_ID_NAME "Device Part ID"
+#define SDCA_CTL_DEVICE_VERSION_NAME "Device Version"
+#define SDCA_CTL_DEVICE_SDCA_VERSION_NAME "Device SDCA Version"
+
+/**
+ * enum sdca_access_mode - SDCA Control access mode
+ *
+ * Access modes as described in the SDCA specification v1.0 section
+ * 7.1.8.2.
+ */
+enum sdca_access_mode {
+ SDCA_ACCESS_MODE_RW = 0x0,
+ SDCA_ACCESS_MODE_DUAL = 0x1,
+ SDCA_ACCESS_MODE_RW1C = 0x2,
+ SDCA_ACCESS_MODE_RO = 0x3,
+ SDCA_ACCESS_MODE_RW1S = 0x4,
+ SDCA_ACCESS_MODE_DC = 0x5,
+};
+
+/**
+ * enum sdca_access_layer - SDCA Control access layer
+ *
+ * Access layers as described in the SDCA specification v1.0 section
+ * 7.1.9.
+ */
+enum sdca_access_layer {
+ SDCA_ACCESS_LAYER_USER = 1 << 0,
+ SDCA_ACCESS_LAYER_APPLICATION = 1 << 1,
+ SDCA_ACCESS_LAYER_CLASS = 1 << 2,
+ SDCA_ACCESS_LAYER_PLATFORM = 1 << 3,
+ SDCA_ACCESS_LAYER_DEVICE = 1 << 4,
+ SDCA_ACCESS_LAYER_EXTENSION = 1 << 5,
+};
+
+/**
+ * struct sdca_control_range - SDCA Control range table
+ * @cols: Number of columns in the range table.
+ * @rows: Number of rows in the range table.
+ * @data: Array of values contained in the range table.
+ */
+struct sdca_control_range {
+ unsigned int cols;
+ unsigned int rows;
+ u32 *data;
+};
+
+/**
+ * struct sdca_control - information for one SDCA Control
+ * @label: Name for the Control, from SDCA Specification v1.0, section 7.1.7.
+ * @sel: Identifier used for addressing.
+ * @value: Holds the Control value for constants and defaults.
+ * @nbits: Number of bits used in the Control.
+ * @interrupt_position: SCDA interrupt line that will alert to changes on this
+ * Control.
+ * @cn_list: A bitmask showing the valid Control Numbers within this Control,
+ * Control Numbers typically represent channels.
+ * @range: Buffer describing valid range of values for the Control.
+ * @mode: Access mode of the Control.
+ * @layers: Bitmask of access layers of the Control.
+ * @deferrable: Indicates if the access to the Control can be deferred.
+ * @has_default: Indicates the Control has a default value to be written.
+ * @has_fixed: Indicates the Control only supports a single value.
+ */
+struct sdca_control {
+ const char *label;
+ int sel;
+
+ int value;
+ int nbits;
+ int interrupt_position;
+ u64 cn_list;
+
+ struct sdca_control_range range;
+ enum sdca_access_mode mode;
+ u8 layers;
+
+ bool deferrable;
+ bool has_default;
+ bool has_fixed;
+};
+
+/**
+ * enum sdca_terminal_type - SDCA Terminal Types
+ *
+ * Indicate what a Terminal Entity is used for, see in section 6.2.3
+ * of the SDCA v1.0 specification.
+ */
+enum sdca_terminal_type {
+ /* Table 77 - Data Port*/
+ SDCA_TERM_TYPE_GENERIC = 0x101,
+ SDCA_TERM_TYPE_ULTRASOUND = 0x180,
+ SDCA_TERM_TYPE_CAPTURE_DIRECT_PCM_MIC = 0x181,
+ SDCA_TERM_TYPE_RAW_PDM_MIC = 0x182,
+ SDCA_TERM_TYPE_SPEECH = 0x183,
+ SDCA_TERM_TYPE_VOICE = 0x184,
+ SDCA_TERM_TYPE_SECONDARY_PCM_MIC = 0x185,
+ SDCA_TERM_TYPE_ACOUSTIC_CONTEXT_AWARENESS = 0x186,
+ SDCA_TERM_TYPE_DTOD_STREAM = 0x187,
+ SDCA_TERM_TYPE_REFERENCE_STREAM = 0x188,
+ SDCA_TERM_TYPE_SENSE_CAPTURE = 0x189,
+ SDCA_TERM_TYPE_STREAMING_MIC = 0x18A,
+ SDCA_TERM_TYPE_OPTIMIZATION_STREAM = 0x190,
+ SDCA_TERM_TYPE_PDM_RENDER_STREAM = 0x191,
+ SDCA_TERM_TYPE_COMPANION_DATA = 0x192,
+ /* Table 78 - Transducer */
+ SDCA_TERM_TYPE_MICROPHONE_TRANSDUCER = 0x201,
+ SDCA_TERM_TYPE_MICROPHONE_ARRAY_TRANSDUCER = 0x205,
+ SDCA_TERM_TYPE_PRIMARY_FULL_RANGE_SPEAKER = 0x380,
+ SDCA_TERM_TYPE_PRIMARY_LFE_SPEAKER = 0x381,
+ SDCA_TERM_TYPE_PRIMARY_TWEETER_SPEAKER = 0x382,
+ SDCA_TERM_TYPE_PRIMARY_ULTRASOUND_SPEAKER = 0x383,
+ SDCA_TERM_TYPE_SECONDARY_FULL_RANGE_SPEAKER = 0x390,
+ SDCA_TERM_TYPE_SECONDARY_LFE_SPEAKER = 0x391,
+ SDCA_TERM_TYPE_SECONDARY_TWEETER_SPEAKER = 0x392,
+ SDCA_TERM_TYPE_SECONDARY_ULTRASOUND_SPEAKER = 0x393,
+ SDCA_TERM_TYPE_TERTIARY_FULL_RANGE_SPEAKER = 0x3A0,
+ SDCA_TERM_TYPE_TERTIARY_LFE_SPEAKER = 0x3A1,
+ SDCA_TERM_TYPE_TERTIARY_TWEETER_SPEAKER = 0x3A2,
+ SDCA_TERM_TYPE_TERTIARY_ULTRASOUND_SPEAKER = 0x3A3,
+ SDCA_TERM_TYPE_SPDIF = 0x605,
+ SDCA_TERM_TYPE_NDAI_DISPLAY_AUDIO = 0x610,
+ SDCA_TERM_TYPE_NDAI_USB = 0x612,
+ SDCA_TERM_TYPE_NDAI_BLUETOOTH_MAIN = 0x614,
+ SDCA_TERM_TYPE_NDAI_BLUETOOTH_ALTERNATE = 0x615,
+ SDCA_TERM_TYPE_NDAI_BLUETOOTH_BOTH = 0x616,
+ SDCA_TERM_TYPE_LINEIN_STEREO = 0x680,
+ SDCA_TERM_TYPE_LINEIN_FRONT_LR = 0x681,
+ SDCA_TERM_TYPE_LINEIN_CENTER_LFE = 0x682,
+ SDCA_TERM_TYPE_LINEIN_SURROUND_LR = 0x683,
+ SDCA_TERM_TYPE_LINEIN_REAR_LR = 0x684,
+ SDCA_TERM_TYPE_LINEOUT_STEREO = 0x690,
+ SDCA_TERM_TYPE_LINEOUT_FRONT_LR = 0x691,
+ SDCA_TERM_TYPE_LINEOUT_CENTER_LFE = 0x692,
+ SDCA_TERM_TYPE_LINEOUT_SURROUND_LR = 0x693,
+ SDCA_TERM_TYPE_LINEOUT_REAR_LR = 0x694,
+ SDCA_TERM_TYPE_MIC_JACK = 0x6A0,
+ SDCA_TERM_TYPE_STEREO_JACK = 0x6B0,
+ SDCA_TERM_TYPE_FRONT_LR_JACK = 0x6B1,
+ SDCA_TERM_TYPE_CENTER_LFE_JACK = 0x6B2,
+ SDCA_TERM_TYPE_SURROUND_LR_JACK = 0x6B3,
+ SDCA_TERM_TYPE_REAR_LR_JACK = 0x6B4,
+ SDCA_TERM_TYPE_HEADPHONE_JACK = 0x6C0,
+ SDCA_TERM_TYPE_HEADSET_JACK = 0x6D0,
+ /* Table 79 - System */
+ SDCA_TERM_TYPE_SENSE_DATA = 0x280,
+ SDCA_TERM_TYPE_PRIVACY_SIGNALING = 0x741,
+ SDCA_TERM_TYPE_PRIVACY_INDICATORS = 0x747,
+};
+
+/**
+ * enum sdca_connector_type - SDCA Connector Types
+ *
+ * Indicate the type of Connector that a Terminal Entity represents,
+ * see section 6.2.4 of the SDCA v1.0 specification.
+ */
+enum sdca_connector_type {
+ SDCA_CONN_TYPE_UNKNOWN = 0x00,
+ SDCA_CONN_TYPE_2P5MM_JACK = 0x01,
+ SDCA_CONN_TYPE_3P5MM_JACK = 0x02,
+ SDCA_CONN_TYPE_QUARTER_INCH_JACK = 0x03,
+ SDCA_CONN_TYPE_XLR = 0x05,
+ SDCA_CONN_TYPE_SPDIF_OPTICAL = 0x06,
+ SDCA_CONN_TYPE_RCA = 0x07,
+ SDCA_CONN_TYPE_DIN = 0x0E,
+ SDCA_CONN_TYPE_MINI_DIN = 0x0F,
+ SDCA_CONN_TYPE_EIAJ_OPTICAL = 0x13,
+ SDCA_CONN_TYPE_HDMI = 0x14,
+ SDCA_CONN_TYPE_DISPLAYPORT = 0x17,
+ SDCA_CONN_TYPE_LIGHTNING = 0x1B,
+ SDCA_CONN_TYPE_USB_C = 0x1E,
+ SDCA_CONN_TYPE_OTHER = 0xFF,
+};
+
+/**
+ * struct sdca_entity_iot - information specific to Input/Output Entities
+ * @clock: Pointer to the Entity providing this Terminal's clock.
+ * @type: Usage of the Terminal Entity.
+ * @connector: Physical Connector of the Terminal Entity.
+ * @reference: Physical Jack number of the Terminal Entity.
+ * @num_transducer: Number of transducers attached to the Terminal Entity.
+ * @is_dataport: Boolean indicating if this Terminal represents a Dataport.
+ */
+struct sdca_entity_iot {
+ struct sdca_entity *clock;
+
+ enum sdca_terminal_type type;
+ enum sdca_connector_type connector;
+ int reference;
+ int num_transducer;
+
+ bool is_dataport;
+};
+
+/**
+ * enum sdca_clock_type - SDCA Clock Types
+ *
+ * Indicate the synchronicity of an Clock Entity, see section 6.4.1.3
+ * of the SDCA v1.0 specification.
+ */
+enum sdca_clock_type {
+ SDCA_CLOCK_TYPE_EXTERNAL = 0x00,
+ SDCA_CLOCK_TYPE_INTERNAL_ASYNC = 0x01,
+ SDCA_CLOCK_TYPE_INTERNAL_SYNC = 0x02,
+ SDCA_CLOCK_TYPE_INTERNAL_SOURCE_SYNC = 0x03,
+};
+
+/**
+ * struct sdca_entity_cs - information specific to Clock Source Entities
+ * @type: Synchronicity of the Clock Source.
+ * @max_delay: The maximum delay in microseconds before the clock is stable.
+ */
+struct sdca_entity_cs {
+ enum sdca_clock_type type;
+ unsigned int max_delay;
+};
+
+/**
+ * enum sdca_pde_power_state - SDCA Power States
+ *
+ * SDCA Power State values from SDCA specification v1.0 Section 7.12.4.
+ */
+enum sdca_pde_power_state {
+ SDCA_PDE_PS0 = 0x0,
+ SDCA_PDE_PS1 = 0x1,
+ SDCA_PDE_PS2 = 0x2,
+ SDCA_PDE_PS3 = 0x3,
+ SDCA_PDE_PS4 = 0x4,
+};
+
+/**
+ * struct sdca_pde_delay - describes the delay changing between 2 power states
+ * @from_ps: The power state being exited.
+ * @to_ps: The power state being entered.
+ * @us: The delay in microseconds switching between the two states.
+ */
+struct sdca_pde_delay {
+ int from_ps;
+ int to_ps;
+ unsigned int us;
+};
+
+/**
+ * struct sdca_entity_pde - information specific to Power Domain Entities
+ * @managed: Dynamically allocated array pointing to each Entity
+ * controlled by this PDE.
+ * @max_delay: Dynamically allocated array of delays for switching
+ * between power states.
+ * @num_managed: Number of Entities controlled by this PDE.
+ * @num_max_delay: Number of delays specified for state changes.
+ */
+struct sdca_entity_pde {
+ struct sdca_entity **managed;
+ struct sdca_pde_delay *max_delay;
+ int num_managed;
+ int num_max_delay;
+};
+
+/**
+ * enum sdca_entity_type - SDCA Entity Type codes
+ * @SDCA_ENTITY_TYPE_ENTITY_0: Entity 0, not actually from the
+ * specification but useful internally as an Entity structure
+ * is allocated for Entity 0, to hold Entity 0 controls.
+ * @SDCA_ENTITY_TYPE_IT: Input Terminal.
+ * @SDCA_ENTITY_TYPE_OT: Output Terminal.
+ * @SDCA_ENTITY_TYPE_MU: Mixer Unit.
+ * @SDCA_ENTITY_TYPE_SU: Selector Unit.
+ * @SDCA_ENTITY_TYPE_FU: Feature Unit.
+ * @SDCA_ENTITY_TYPE_XU: Extension Unit.
+ * @SDCA_ENTITY_TYPE_CS: Clock Source.
+ * @SDCA_ENTITY_TYPE_CX: Clock selector.
+ * @SDCA_ENTITY_TYPE_PDE: Power-Domain Entity.
+ * @SDCA_ENTITY_TYPE_GE: Group Entity.
+ * @SDCA_ENTITY_TYPE_SPE: Security & Privacy Entity.
+ * @SDCA_ENTITY_TYPE_CRU: Channel Remapping Unit.
+ * @SDCA_ENTITY_TYPE_UDMPU: Up-Down Mixer Processing Unit.
+ * @SDCA_ENTITY_TYPE_MFPU: Multi-Function Processing Unit.
+ * @SDCA_ENTITY_TYPE_SMPU: Smart Microphone Processing Unit.
+ * @SDCA_ENTITY_TYPE_SAPU: Smart Amp Processing Unit.
+ * @SDCA_ENTITY_TYPE_PPU: Posture Processing Unit.
+ * @SDCA_ENTITY_TYPE_TG: Tone Generator.
+ * @SDCA_ENTITY_TYPE_HIDE: Human Interface Device Entity.
+ *
+ * SDCA Entity Types from SDCA specification v1.0 Section 6.1.2
+ * all Entity Types not described are reserved.
+ */
+enum sdca_entity_type {
+ SDCA_ENTITY_TYPE_ENTITY_0 = 0x00,
+ SDCA_ENTITY_TYPE_IT = 0x02,
+ SDCA_ENTITY_TYPE_OT = 0x03,
+ SDCA_ENTITY_TYPE_MU = 0x05,
+ SDCA_ENTITY_TYPE_SU = 0x06,
+ SDCA_ENTITY_TYPE_FU = 0x07,
+ SDCA_ENTITY_TYPE_XU = 0x0A,
+ SDCA_ENTITY_TYPE_CS = 0x0B,
+ SDCA_ENTITY_TYPE_CX = 0x0C,
+ SDCA_ENTITY_TYPE_PDE = 0x11,
+ SDCA_ENTITY_TYPE_GE = 0x12,
+ SDCA_ENTITY_TYPE_SPE = 0x13,
+ SDCA_ENTITY_TYPE_CRU = 0x20,
+ SDCA_ENTITY_TYPE_UDMPU = 0x21,
+ SDCA_ENTITY_TYPE_MFPU = 0x22,
+ SDCA_ENTITY_TYPE_SMPU = 0x23,
+ SDCA_ENTITY_TYPE_SAPU = 0x24,
+ SDCA_ENTITY_TYPE_PPU = 0x25,
+ SDCA_ENTITY_TYPE_TG = 0x30,
+ SDCA_ENTITY_TYPE_HIDE = 0x31,
+};
+
+/**
+ * struct sdca_entity - information for one SDCA Entity
+ * @label: String such as "OT 12".
+ * @id: Identifier used for addressing.
+ * @type: Type code for the Entity.
+ * @sources: Dynamically allocated array pointing to each input Entity
+ * connected to this Entity.
+ * @controls: Dynamically allocated array of Controls.
+ * @num_sources: Number of sources for the Entity.
+ * @num_controls: Number of Controls for the Entity.
+ * @iot: Input/Output Terminal specific Entity properties.
+ * @cs: Clock Source specific Entity properties.
+ * @pde: Power Domain Entity specific Entity properties.
+ */
+struct sdca_entity {
+ const char *label;
+ int id;
+ enum sdca_entity_type type;
+
+ struct sdca_entity **sources;
+ struct sdca_control *controls;
+ int num_sources;
+ int num_controls;
+ union {
+ struct sdca_entity_iot iot;
+ struct sdca_entity_cs cs;
+ struct sdca_entity_pde pde;
+ };
+};
+
+/**
+ * enum sdca_channel_purpose - SDCA Channel Purpose code
+ *
+ * Channel Purpose codes as described in the SDCA specification v1.0
+ * section 11.4.3.
+ */
+enum sdca_channel_purpose {
+ /* Table 210 - Purpose */
+ SDCA_CHAN_PURPOSE_GENERIC_AUDIO = 0x01,
+ SDCA_CHAN_PURPOSE_VOICE = 0x02,
+ SDCA_CHAN_PURPOSE_SPEECH = 0x03,
+ SDCA_CHAN_PURPOSE_AMBIENT = 0x04,
+ SDCA_CHAN_PURPOSE_REFERENCE = 0x05,
+ SDCA_CHAN_PURPOSE_ULTRASOUND = 0x06,
+ SDCA_CHAN_PURPOSE_SENSE = 0x08,
+ SDCA_CHAN_PURPOSE_SILENCE = 0xFE,
+ SDCA_CHAN_PURPOSE_NON_AUDIO = 0xFF,
+ /* Table 211 - Amp Sense */
+ SDCA_CHAN_PURPOSE_SENSE_V1 = 0x09,
+ SDCA_CHAN_PURPOSE_SENSE_V2 = 0x0A,
+ SDCA_CHAN_PURPOSE_SENSE_V12_INTERLEAVED = 0x10,
+ SDCA_CHAN_PURPOSE_SENSE_V21_INTERLEAVED = 0x11,
+ SDCA_CHAN_PURPOSE_SENSE_V12_PACKED = 0x12,
+ SDCA_CHAN_PURPOSE_SENSE_V21_PACKED = 0x13,
+ SDCA_CHAN_PURPOSE_SENSE_V1212_INTERLEAVED = 0x14,
+ SDCA_CHAN_PURPOSE_SENSE_V2121_INTERLEAVED = 0x15,
+ SDCA_CHAN_PURPOSE_SENSE_V1122_INTERLEAVED = 0x16,
+ SDCA_CHAN_PURPOSE_SENSE_V2211_INTERLEAVED = 0x17,
+ SDCA_CHAN_PURPOSE_SENSE_V1212_PACKED = 0x18,
+ SDCA_CHAN_PURPOSE_SENSE_V2121_PACKED = 0x19,
+ SDCA_CHAN_PURPOSE_SENSE_V1122_PACKED = 0x1A,
+ SDCA_CHAN_PURPOSE_SENSE_V2211_PACKED = 0x1B,
+};
+
+/**
+ * enum sdca_channel_relationship - SDCA Channel Relationship code
+ *
+ * Channel Relationship codes as described in the SDCA specification
+ * v1.0 section 11.4.2.
+ */
+enum sdca_channel_relationship {
+ /* Table 206 - Streaming */
+ SDCA_CHAN_REL_UNDEFINED = 0x00,
+ SDCA_CHAN_REL_GENERIC_MONO = 0x01,
+ SDCA_CHAN_REL_GENERIC_LEFT = 0x02,
+ SDCA_CHAN_REL_GENERIC_RIGHT = 0x03,
+ SDCA_CHAN_REL_GENERIC_TOP = 0x48,
+ SDCA_CHAN_REL_GENERIC_BOTTOM = 0x49,
+ SDCA_CHAN_REL_CAPTURE_DIRECT = 0x4E,
+ SDCA_CHAN_REL_RENDER_DIRECT = 0x4F,
+ SDCA_CHAN_REL_FRONT_LEFT = 0x0B,
+ SDCA_CHAN_REL_FRONT_RIGHT = 0x0C,
+ SDCA_CHAN_REL_FRONT_CENTER = 0x0D,
+ SDCA_CHAN_REL_SIDE_LEFT = 0x12,
+ SDCA_CHAN_REL_SIDE_RIGHT = 0x13,
+ SDCA_CHAN_REL_BACK_LEFT = 0x16,
+ SDCA_CHAN_REL_BACK_RIGHT = 0x17,
+ SDCA_CHAN_REL_LOW_FREQUENCY_EFFECTS = 0x43,
+ SDCA_CHAN_REL_SOUNDWIRE_MIC = 0x55,
+ SDCA_CHAN_REL_SENSE_TRANSDUCER_1 = 0x58,
+ SDCA_CHAN_REL_SENSE_TRANSDUCER_2 = 0x59,
+ SDCA_CHAN_REL_SENSE_TRANSDUCER_12 = 0x5A,
+ SDCA_CHAN_REL_SENSE_TRANSDUCER_21 = 0x5B,
+ SDCA_CHAN_REL_ECHOREF_NONE = 0x70,
+ SDCA_CHAN_REL_ECHOREF_1 = 0x71,
+ SDCA_CHAN_REL_ECHOREF_2 = 0x72,
+ SDCA_CHAN_REL_ECHOREF_3 = 0x73,
+ SDCA_CHAN_REL_ECHOREF_4 = 0x74,
+ SDCA_CHAN_REL_ECHOREF_ALL = 0x75,
+ SDCA_CHAN_REL_ECHOREF_LFE_ALL = 0x76,
+ /* Table 207 - Speaker */
+ SDCA_CHAN_REL_PRIMARY_TRANSDUCER = 0x50,
+ SDCA_CHAN_REL_SECONDARY_TRANSDUCER = 0x51,
+ SDCA_CHAN_REL_TERTIARY_TRANSDUCER = 0x52,
+ SDCA_CHAN_REL_LOWER_LEFT_ALLTRANSDUCER = 0x60,
+ SDCA_CHAN_REL_LOWER_RIGHT_ALLTRANSDUCER = 0x61,
+ SDCA_CHAN_REL_UPPER_LEFT_ALLTRANSDUCER = 0x62,
+ SDCA_CHAN_REL_UPPER_RIGHT_ALLTRANSDUCER = 0x63,
+ SDCA_CHAN_REL_LOWER_LEFT_PRIMARY = 0x64,
+ SDCA_CHAN_REL_LOWER_RIGHT_PRIMARY = 0x65,
+ SDCA_CHAN_REL_UPPER_LEFT_PRIMARY = 0x66,
+ SDCA_CHAN_REL_UPPER_RIGHT_PRIMARY = 0x67,
+ SDCA_CHAN_REL_LOWER_LEFT_SECONDARY = 0x68,
+ SDCA_CHAN_REL_LOWER_RIGHT_SECONDARY = 0x69,
+ SDCA_CHAN_REL_UPPER_LEFT_SECONDARY = 0x6A,
+ SDCA_CHAN_REL_UPPER_RIGHT_SECONDARY = 0x6B,
+ SDCA_CHAN_REL_LOWER_LEFT_TERTIARY = 0x6C,
+ SDCA_CHAN_REL_LOWER_RIGHT_TERTIARY = 0x6D,
+ SDCA_CHAN_REL_UPPER_LEFT_TERTIARY = 0x6E,
+ SDCA_CHAN_REL_UPPER_RIGHT_TERTIARY = 0x6F,
+ SDCA_CHAN_REL_DERIVED_LOWER_LEFT_PRIMARY = 0x94,
+ SDCA_CHAN_REL_DERIVED_LOWER_RIGHT_PRIMARY = 0x95,
+ SDCA_CHAN_REL_DERIVED_UPPER_LEFT_PRIMARY = 0x96,
+ SDCA_CHAN_REL_DERIVED_UPPER_RIGHT_PRIMARY = 0x97,
+ SDCA_CHAN_REL_DERIVED_LOWER_LEFT_SECONDARY = 0x98,
+ SDCA_CHAN_REL_DERIVED_LOWER_RIGHT_SECONDARY = 0x99,
+ SDCA_CHAN_REL_DERIVED_UPPER_LEFT_SECONDARY = 0x9A,
+ SDCA_CHAN_REL_DERIVED_UPPER_RIGHT_SECONDARY = 0x9B,
+ SDCA_CHAN_REL_DERIVED_LOWER_LEFT_TERTIARY = 0x9C,
+ SDCA_CHAN_REL_DERIVED_LOWER_RIGHT_TERTIARY = 0x9D,
+ SDCA_CHAN_REL_DERIVED_UPPER_LEFT_TERTIARY = 0x9E,
+ SDCA_CHAN_REL_DERIVED_UPPER_RIGHT_TERTIARY = 0x9F,
+ SDCA_CHAN_REL_DERIVED_MONO_PRIMARY = 0xA0,
+ SDCA_CHAN_REL_DERIVED_MONO_SECONDARY = 0xAB,
+ SDCA_CHAN_REL_DERIVED_MONO_TERTIARY = 0xAC,
+ /* Table 208 - Equipment */
+ SDCA_CHAN_REL_EQUIPMENT_LEFT = 0x02,
+ SDCA_CHAN_REL_EQUIPMENT_RIGHT = 0x03,
+ SDCA_CHAN_REL_EQUIPMENT_COMBINED = 0x47,
+ SDCA_CHAN_REL_EQUIPMENT_TOP = 0x48,
+ SDCA_CHAN_REL_EQUIPMENT_BOTTOM = 0x49,
+ SDCA_CHAN_REL_EQUIPMENT_TOP_LEFT = 0x4A,
+ SDCA_CHAN_REL_EQUIPMENT_BOTTOM_LEFT = 0x4B,
+ SDCA_CHAN_REL_EQUIPMENT_TOP_RIGHT = 0x4C,
+ SDCA_CHAN_REL_EQUIPMENT_BOTTOM_RIGHT = 0x4D,
+ SDCA_CHAN_REL_EQUIPMENT_SILENCED_OUTPUT = 0x57,
+ /* Table 209 - Other */
+ SDCA_CHAN_REL_ARRAY = 0x04,
+ SDCA_CHAN_REL_MIC = 0x53,
+ SDCA_CHAN_REL_RAW = 0x54,
+ SDCA_CHAN_REL_SILENCED_MIC = 0x56,
+ SDCA_CHAN_REL_MULTI_SOURCE_1 = 0x78,
+ SDCA_CHAN_REL_MULTI_SOURCE_2 = 0x79,
+ SDCA_CHAN_REL_MULTI_SOURCE_3 = 0x7A,
+ SDCA_CHAN_REL_MULTI_SOURCE_4 = 0x7B,
+};
+
+/**
+ * struct sdca_channel - a single Channel with a Cluster
+ * @id: Identifier used for addressing.
+ * @purpose: Indicates the purpose of the Channel, usually to give
+ * semantic meaning to the audio, eg. voice, ultrasound.
+ * @relationship: Indicates the relationship of this Channel to others
+ * in the Cluster, often used to identify the physical position of the
+ * Channel eg. left.
+ */
+struct sdca_channel {
+ int id;
+ enum sdca_channel_purpose purpose;
+ enum sdca_channel_relationship relationship;
+};
+
+/**
+ * struct sdca_cluster - information about an SDCA Channel Cluster
+ * @id: Identifier used for addressing.
+ * @num_channels: Number of Channels within this Cluster.
+ * @channels: Dynamically allocated array of Channels.
+ */
+struct sdca_cluster {
+ int id;
+ int num_channels;
+ struct sdca_channel *channels;
+};
+
+/**
+ * struct sdca_function_data - top-level information for one SDCA function
+ * @desc: Pointer to short descriptor from initial parsing.
+ * @init_table: Pointer to a table of initialization writes.
+ * @entities: Dynamically allocated array of Entities.
+ * @clusters: Dynamically allocated array of Channel Clusters.
+ * @num_init_table: Number of initialization writes.
+ * @num_entities: Number of Entities reported in this Function.
+ * @num_clusters: Number of Channel Clusters reported in this Function.
+ * @busy_max_delay: Maximum Function busy delay in microseconds, before an
+ * error should be reported.
+ */
+struct sdca_function_data {
+ struct sdca_function_desc *desc;
+
+ struct sdca_init_write *init_table;
+ struct sdca_entity *entities;
+ struct sdca_cluster *clusters;
+ int num_init_table;
+ int num_entities;
+ int num_clusters;
+
+ unsigned int busy_max_delay;
+};
+
+int sdca_parse_function(struct device *dev,
+ struct sdca_function_desc *desc,
+ struct sdca_function_data *function);
+
#endif
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index 892f70532363..69a9c9c4d0e9 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -142,14 +142,14 @@ int simple_util_parse_daifmt(struct device *dev,
struct device_node *codec,
char *prefix,
unsigned int *retfmt);
-int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np,
+int simple_util_parse_tdm_width_map(struct simple_util_priv *priv, struct device_node *np,
struct simple_util_dai *dai);
__printf(3, 4)
-int simple_util_set_dailink_name(struct device *dev,
+int simple_util_set_dailink_name(struct simple_util_priv *priv,
struct snd_soc_dai_link *dai_link,
const char *fmt, ...);
-int simple_util_parse_card_name(struct snd_soc_card *card,
+int simple_util_parse_card_name(struct simple_util_priv *priv,
char *prefix);
int simple_util_parse_clk(struct device *dev,
@@ -201,7 +201,7 @@ void simple_util_remove(struct platform_device *pdev);
int graph_util_card_probe(struct snd_soc_card *card);
int graph_util_is_ports0(struct device_node *port);
-int graph_util_parse_dai(struct device *dev, struct device_node *ep,
+int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep,
struct snd_soc_dai_link_component *dlc, int *is_single_link);
void graph_util_parse_link_direction(struct device_node *np,
diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h
index 12cd7b5a2202..7cf52c8c9cf3 100644
--- a/include/sound/soc-dapm.h
+++ b/include/sound/soc-dapm.h
@@ -514,8 +514,6 @@ int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, const char
int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm, const char *pin);
int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, const char *pin);
unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
-
-/* Mostly internal - should not normally be used */
void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
/* dapm path query */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index fcdb5adfcd5e..16e4e488521c 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -539,6 +539,7 @@ int snd_soc_calc_bclk(int fs, int sample_size, int channels, int tdm_slots);
int snd_soc_params_to_bclk(const struct snd_pcm_hw_params *parms);
int snd_soc_tdm_params_to_bclk(const struct snd_pcm_hw_params *params,
int tdm_width, int tdm_slots, int slot_multiple);
+int snd_soc_ret(const struct device *dev, int ret, const char *fmt, ...);
/* set runtime hw params */
static inline int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index 72d2060904f6..0f1e585635bb 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -161,7 +161,6 @@ struct tasdevice_priv {
struct mutex codec_lock;
struct regmap *regmap;
struct device *dev;
- struct tm tm;
enum device_catlog_id catlog_id;
unsigned char cal_binaryname[TASDEVICE_MAX_CHANNELS][64];
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
index 3e3fb258dd54..06ff30537f47 100644
--- a/include/uapi/sound/intel/avs/tokens.h
+++ b/include/uapi/sound/intel/avs/tokens.h
@@ -77,6 +77,13 @@ enum avs_tplg_token {
AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32 = 430,
AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16 = 431,
AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16 = 432,
+ AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32 = 433,
+ AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32 = 434,
+ AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32 = 435,
+ AVS_TKN_MODCFG_WHM_VINDEX_U8 = 436,
+ AVS_TKN_MODCFG_WHM_DMA_TYPE_U32 = 437,
+ AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32 = 438,
+ AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32 = 439,
/* struct avs_tplg_pplcfg */
AVS_TKN_PPLCFG_ID_U32 = 1401,
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index f564ec7af194..9a8ead75be17 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -108,6 +108,10 @@ static const struct config_entry config_table[] = {
{}
}
},
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+ },
#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
{
diff --git a/sound/pci/hda/tas2781-spi.h b/sound/pci/hda/tas2781-spi.h
index ecfc3c8bb821..7a0faceeb675 100644
--- a/sound/pci/hda/tas2781-spi.h
+++ b/sound/pci/hda/tas2781-spi.h
@@ -88,7 +88,6 @@ struct tasdevice_priv {
struct mutex codec_lock;
struct regmap *regmap;
struct device *dev;
- struct tm tm;
unsigned char crc8_lkp_tbl[CRC8_TABLE_SIZE];
unsigned char coef_binaryname[64];
diff --git a/sound/pci/hda/tas2781_hda_i2c.c b/sound/pci/hda/tas2781_hda_i2c.c
index 0e42b87dadb8..be9a90f643eb 100644
--- a/sound/pci/hda/tas2781_hda_i2c.c
+++ b/sound/pci/hda/tas2781_hda_i2c.c
@@ -594,7 +594,6 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
efi_guid_t efi_guid = EFI_GUID(0x02f9af02, 0x7734, 0x4233, 0xb4, 0x3d,
0x93, 0xfe, 0x5a, 0xa3, 0x5d, 0xb3);
static efi_char16_t efi_name[] = L"CALI_DATA";
- struct tm *tm = &tas_priv->tm;
unsigned int attr, crc;
unsigned int *tmp_val;
efi_status_t status;
@@ -629,10 +628,9 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
crc, tmp_val[21]);
if (crc == tmp_val[21]) {
- time64_to_tm(tmp_val[20], 0, tm);
- dev_dbg(tas_priv->dev, "%4ld-%2d-%2d, %2d:%2d:%2d\n",
- tm->tm_year, tm->tm_mon, tm->tm_mday,
- tm->tm_hour, tm->tm_min, tm->tm_sec);
+ time64_t seconds = tmp_val[20];
+
+ dev_dbg(tas_priv->dev, "%ptTsr\n", &seconds);
tasdevice_apply_calibration(tas_priv);
} else
tas_priv->cali_data.total_sz = 0;
diff --git a/sound/pci/hda/tas2781_hda_spi.c b/sound/pci/hda/tas2781_hda_spi.c
index a42fa990e7b9..d91b19602485 100644
--- a/sound/pci/hda/tas2781_hda_spi.c
+++ b/sound/pci/hda/tas2781_hda_spi.c
@@ -802,7 +802,6 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
static efi_char16_t efi_name[] = TASDEVICE_CALIBRATION_DATA_NAME;
unsigned char data[TASDEVICE_CALIBRATION_DATA_SIZE], *buf;
unsigned int attr, crc, offset, *tmp_val;
- struct tm *tm = &tas_priv->tm;
unsigned long total_sz = 0;
efi_status_t status;
@@ -849,7 +848,6 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
if (crc != tmp_val[3 + tmp_val[1] * 6])
return 0;
- time64_to_tm(tmp_val[2], 0, tm);
for (int j = 0; j < tmp_val[1]; j++) {
offset = j * 6 + 3;
if (tmp_val[offset] == tas_priv->index) {
@@ -882,7 +880,6 @@ static int tas2781_save_calibration(struct tasdevice_priv *tas_priv)
*/
crc = crc32(~0, data, 84) ^ ~0;
if (crc == tmp_val[21]) {
- time64_to_tm(tmp_val[20], 0, tm);
for (int i = 0; i < CALIB_MAX; i++)
tas_priv->cali_data[i] =
tmp_val[tas_priv->index * 5 + i];
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index 803521178279..c7daae392d74 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -161,15 +161,15 @@ config SND_SOC_AMD_SOUNDWIRE
If unsure select "N".
config SND_SOC_AMD_PS
- tristate "AMD Audio Coprocessor-v6.3 Pink Sardine support"
+ tristate "AMD Audio Coprocessor-v6.3/v7.0/v7.1 support"
select SND_SOC_AMD_SOUNDWIRE_LINK_BASELINE
select SND_SOC_ACPI_AMD_MATCH
depends on X86 && PCI && ACPI
help
- This option enables Audio Coprocessor i.e ACP v6.3 support on
- AMD Pink sardine platform. By enabling this flag build will be
- triggered for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire
- DMA driver.
+ This option enables Audio Coprocessor i.e ACP6.3/ACP7.0/ACP7.1
+ variants support. By enabling this flag build will be triggered
+ for ACP PCI driver, ACP PDM DMA driver, ACP SoundWire DMA
+ driver.
Say m if you have such a device.
If unsure select "N".
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 03f3fcbba5af..53793ec7c7b4 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -156,6 +156,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH
select SND_SOC_RT712_SDCA_SDW
select SND_SOC_RT712_SDCA_DMIC_SDW
select SND_SOC_RT1316_SDW
+ select SND_SOC_RT1320_SDW
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
select SND_SOC_RT722_SDCA_SDW
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index bb2702036338..7c75892e678b 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -22,7 +22,7 @@ snd-acp70-y := acp70.o
snd-acp-mach-y := acp-mach-common.o
snd-acp-legacy-mach-y := acp-legacy-mach.o acp3x-es83xx/acp3x-es83xx.o
snd-acp-sof-mach-y := acp-sof-mach.o
-snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o
+snd-soc-acpi-amd-match-y := amd-acp63-acpi-match.o amd-acp70-acpi-match.o
snd-acp-sdw-mach-y := acp-sdw-mach-common.o
snd-acp-sdw-sof-mach-y += acp-sdw-sof-mach.o
snd-acp-sdw-legacy-mach-y += acp-sdw-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
index 9280cd30d19c..2020c5cfb3d5 100644
--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -28,6 +28,8 @@ static void log_quirks(struct device *dev)
SOC_JACK_JDSRC(soc_sdw_quirk));
if (soc_sdw_quirk & ASOC_SDW_ACP_DMIC)
dev_dbg(dev, "quirk SOC_SDW_ACP_DMIC enabled\n");
+ if (soc_sdw_quirk & ASOC_SDW_CODEC_SPKR)
+ dev_dbg(dev, "quirk ASOC_SDW_CODEC_SPKR enabled\n");
}
static int soc_sdw_quirk_cb(const struct dmi_system_id *id)
@@ -45,6 +47,38 @@ static const struct dmi_system_id soc_sdw_quirk_table[] = {
},
.driver_data = (void *)RT711_JD2,
},
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D80"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D81"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D82"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
+ {
+ .callback = soc_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0D83"),
+ },
+ .driver_data = (void *)(ASOC_SDW_CODEC_SPKR),
+ },
{}
};
@@ -122,6 +156,13 @@ static int create_sdw_dailink(struct snd_soc_card *card,
if (ret)
return ret;
break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(soc_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
default:
return -EINVAL;
}
@@ -221,6 +262,8 @@ static int create_sdw_dailinks(struct snd_soc_card *card,
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
sdw_platform_component->name = "amd_ps_sdw_dma.0";
break;
default:
@@ -266,6 +309,8 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
switch (amd_ctx->acp_rev) {
case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
pdm_cpu->name = "acp_ps_pdm_dma.0";
pdm_platform->name = "acp_ps_pdm_dma.0";
break;
diff --git a/sound/soc/amd/acp/acp-sdw-mach-common.c b/sound/soc/amd/acp/acp-sdw-mach-common.c
index 6f5c39ed1a18..e5f394dc2f4c 100644
--- a/sound/soc/amd/acp/acp-sdw-mach-common.c
+++ b/sound/soc/amd/acp/acp-sdw-mach-common.c
@@ -59,6 +59,40 @@ int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct dev
}
EXPORT_SYMBOL_NS_GPL(get_acp63_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev)
+{
+ switch (sdw_link_id) {
+ case AMD_SDW0:
+ case AMD_SDW1:
+ switch (be_id) {
+ case SOC_SDW_JACK_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_TX;
+ break;
+ case SOC_SDW_JACK_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO0_RX;
+ break;
+ case SOC_SDW_AMP_OUT_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_TX;
+ break;
+ case SOC_SDW_AMP_IN_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO1_RX;
+ break;
+ case SOC_SDW_DMIC_DAI_ID:
+ *cpu_pin_id = ACP70_SW_AUDIO2_RX;
+ break;
+ default:
+ dev_err(dev, "Invalid be id:%d\n", be_id);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(dev, "sdw_link_id:%d, be_id:%d, cpu_pin_id:%d\n", sdw_link_id, be_id, *cpu_pin_id);
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(get_acp70_cpu_pin_id, "SND_SOC_AMD_SDW_MACH");
+
MODULE_DESCRIPTION("AMD SoundWire Common Machine driver");
MODULE_AUTHOR("Vijendar Mukunda <Vijendar.Mukunda@amd.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
index 2b0aa270a3e9..eb5d4a5baef2 100644
--- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
@@ -19,6 +19,7 @@
#include <linux/io.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
+#include <linux/string_choices.h>
#include "../acp-mach.h"
#include "acp3x-es83xx.h"
@@ -241,9 +242,9 @@ static int acp3x_es83xx_configure_gpios(struct acp3x_es83xx_private *priv)
dev_info(priv->codec_dev, "speaker gpio %d active %s, headphone gpio %d active %s\n",
priv->enable_spk_gpio.crs_entry_index,
- priv->enable_spk_gpio.active_low ? "low" : "high",
+ str_low_high(priv->enable_spk_gpio.active_low),
priv->enable_hp_gpio.crs_entry_index,
- priv->enable_hp_gpio.active_low ? "low" : "high");
+ str_low_high(priv->enable_hp_gpio.active_low));
return 0;
}
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
new file mode 100644
index 000000000000..e87ccfeee5bd
--- /dev/null
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -0,0 +1,160 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * amd-acp70-acpi-match.c - tables and support for ACP 7.0 & ACP7.1
+ * ACPI enumeration.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ */
+
+#include <sound/soc-acpi.h>
+#include "../mach-config.h"
+
+static const struct snd_soc_acpi_endpoint single_endpoint = {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .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
+};
+
+static const struct snd_soc_acpi_adr_device rt711_rt1316_group_adr[] = {
+ {
+ .adr = 0x000030025D071101ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt711"
+ },
+ {
+ .adr = 0x000030025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_l_endpoint,
+ .name_prefix = "rt1316-1"
+ },
+ {
+ .adr = 0x000032025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1316-2"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt714_adr[] = {
+ {
+ .adr = 0x130025d071401ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt714"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_4_in_1_sdca[] = {
+ { .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_rt1316_group_adr),
+ .adr_d = rt711_rt1316_group_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt714_adr),
+ .adr_d = rt714_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_endpoint rt722_endpoints[] = {
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 1,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072201ull,
+ .num_endpoints = ARRAY_SIZE(rt722_endpoints),
+ .endpoints = rt722_endpoints,
+ .name_prefix = "rt722"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt1320_1_single_adr[] = {
+ {
+ .adr = 0x000130025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_only[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {}
+};
+
+static const struct snd_soc_acpi_link_adr acp70_rt722_l0_rt1320_l1[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt722_0_single_adr),
+ .adr_d = rt722_0_single_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_single_adr),
+ .adr_d = rt1320_1_single_adr,
+ },
+ {}
+};
+
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_rt722_l0_rt1320_l1,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sdw",
+ },
+ {
+ .link_mask = BIT(0) | BIT(1),
+ .links = acp70_4_in_1_sdca,
+ .drv_name = "amd_sdw",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
+
+MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h
index b7bae107c13e..1f24e0e06487 100644
--- a/sound/soc/amd/acp/soc_amd_sdw_common.h
+++ b/sound/soc/amd/acp/soc_amd_sdw_common.h
@@ -19,9 +19,12 @@
#define AMD_SDW_MAX_GROUPS 9
#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
#define ASOC_SDW_FOUR_SPK BIT(4)
#define ASOC_SDW_ACP_DMIC BIT(5)
+#define ASOC_SDW_CODEC_SPKR BIT(15)
#define AMD_SDW0 0
#define AMD_SDW1 1
@@ -38,11 +41,20 @@
#define ACP_DMIC_BE_ID 4
+#define ACP70_SW_AUDIO0_TX 0
+#define ACP70_SW_AUDIO1_TX 1
+#define ACP70_SW_AUDIO2_TX 2
+
+#define ACP70_SW_AUDIO0_RX 3
+#define ACP70_SW_AUDIO1_RX 4
+#define ACP70_SW_AUDIO2_RX 5
+
struct amd_mc_ctx {
unsigned int acp_rev;
unsigned int max_sdw_links;
};
int get_acp63_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
+int get_acp70_cpu_pin_id(u32 sdw_link_id, int be_id, int *cpu_pin_id, struct device *dev);
#endif
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
index a86c76f781f9..fdf016a64bbf 100644
--- a/sound/soc/amd/mach-config.h
+++ b/sound/soc/amd/mach-config.h
@@ -26,6 +26,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
struct config_entry {
u32 flags;
diff --git a/sound/soc/amd/ps/Makefile b/sound/soc/amd/ps/Makefile
index b5efb1c5382c..778ee4726389 100644
--- a/sound/soc/amd/ps/Makefile
+++ b/sound/soc/amd/ps/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
# Pink Sardine platform Support
-snd-pci-ps-y := pci-ps.o
+snd-pci-ps-y := pci-ps.o ps-common.o
snd-ps-pdm-dma-y := ps-pdm-dma.o
snd-soc-ps-mach-y := ps-mach.o
snd-ps-sdw-dma-y := ps-sdw-dma.o
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index e54eabaa4d3e..85feae45c44c 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -1,8 +1,8 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
- * AMD ALSA SoC PDM Driver
+ * AMD Common ACP header file for ACP6.3, ACP7.0 & ACP7.1 platforms
*
- * Copyright (C) 2022, 2023 Advanced Micro Devices, Inc. All rights reserved.
+ * Copyright (C) 2022, 2023, 2025 Advanced Micro Devices, Inc. All rights reserved.
*/
#include <linux/soundwire/sdw_amd.h>
@@ -11,15 +11,18 @@
#define ACP_DEVICE_ID 0x15E2
#define ACP63_REG_START 0x1240000
#define ACP63_REG_END 0x125C000
+#define ACP63_PCI_REV 0x63
+#define ACP70_PCI_REV 0x70
+#define ACP71_PCI_REV 0x71
#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
-#define ACP_PGFSM_CNTL_POWER_ON_MASK 1
-#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0
-#define ACP_PGFSM_STATUS_MASK 3
-#define ACP_POWERED_ON 0
-#define ACP_POWER_ON_IN_PROGRESS 1
-#define ACP_POWERED_OFF 2
-#define ACP_POWER_OFF_IN_PROGRESS 3
+#define ACP63_PGFSM_CNTL_POWER_ON_MASK 1
+#define ACP63_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP63_PGFSM_STATUS_MASK 3
+#define ACP63_POWERED_ON 0
+#define ACP63_POWER_ON_IN_PROGRESS 1
+#define ACP63_POWERED_OFF 2
+#define ACP63_POWER_OFF_IN_PROGRESS 3
#define ACP_ERROR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
@@ -60,7 +63,7 @@
#define AMD_SDW_MAX_MANAGERS 2
/* time in ms for acp timeout */
-#define ACP_TIMEOUT 500
+#define ACP63_TIMEOUT 500
#define ACP_SDW0_STAT BIT(21)
#define ACP_SDW1_STAT BIT(2)
@@ -72,13 +75,13 @@
#define ACP_AUDIO0_RX_THRESHOLD 0x1b
#define ACP_AUDIO1_RX_THRESHOLD 0x19
#define ACP_AUDIO2_RX_THRESHOLD 0x17
-#define ACP_P1_AUDIO1_TX_THRESHOLD BIT(6)
-#define ACP_P1_AUDIO1_RX_THRESHOLD BIT(5)
-#define ACP_SDW_DMA_IRQ_MASK 0x1F800000
-#define ACP_P1_SDW_DMA_IRQ_MASK 0x60
+#define ACP63_P1_AUDIO1_TX_THRESHOLD BIT(6)
+#define ACP63_P1_AUDIO1_RX_THRESHOLD BIT(5)
+#define ACP63_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP63_P1_SDW_DMA_IRQ_MASK 0x60
#define ACP63_SDW0_DMA_MAX_STREAMS 6
#define ACP63_SDW1_DMA_MAX_STREAMS 2
-#define ACP_P1_AUDIO_TX_THRESHOLD 6
+#define ACP63_P1_AUDIO_TX_THRESHOLD 6
/*
* Below entries describes SDW0 instance DMA stream id and DMA irq bit mapping
@@ -91,8 +94,8 @@
* 4 (SDW0_AUDIO1_RX) 25
* 5 (SDW0_AUDIO2_RX) 23
*/
-#define SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
-#define SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+#define ACP63_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP63_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
/*
* Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
@@ -101,7 +104,7 @@
* 0 (SDW1_AUDIO1_TX) 6
* 1 (SDW1_AUDIO1_RX) 5
*/
-#define SDW1_DMA_IRQ_MASK(i) (ACP_P1_AUDIO_TX_THRESHOLD - (i))
+#define ACP63_SDW1_DMA_IRQ_MASK(i) (ACP63_P1_AUDIO_TX_THRESHOLD - (i))
#define ACP_DELAY_US 5
#define ACP_SDW_RING_BUFF_ADDR_OFFSET (128 * 1024)
@@ -129,6 +132,61 @@
#define SDW_MAX_BUFFER (SDW_PLAYBACK_MAX_PERIOD_SIZE * SDW_PLAYBACK_MAX_NUM_PERIODS)
#define SDW_MIN_BUFFER SDW_MAX_BUFFER
+#define ACP_HW_OPS(acp_data, cb) ((acp_data)->hw_ops->cb)
+
+#define ACP70_PGFSM_CNTL_POWER_ON_MASK 0x1F
+#define ACP70_PGFSM_CNTL_POWER_OFF_MASK 0
+#define ACP70_PGFSM_STATUS_MASK 0xFF
+#define ACP70_TIMEOUT 2000
+#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
+#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
+#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
+#define ACP70_SDW0_PME_STAT BIT(26)
+#define ACP70_SDW1_PME_STAT BIT(27)
+
+#define ACP70_SDW0_DMA_MAX_STREAMS 6
+#define ACP70_SDW1_DMA_MAX_STREAMS ACP70_SDW0_DMA_MAX_STREAMS
+#define ACP70_SDW_DMA_IRQ_MASK 0x1F800000
+#define ACP70_P1_SDW_DMA_IRQ_MASK 0x1F8
+
+#define ACP70_P1_AUDIO0_TX_THRESHOLD 0x8
+#define ACP70_P1_AUDIO1_TX_THRESHOLD 0x6
+#define ACP70_P1_AUDIO2_TX_THRESHOLD 0x4
+#define ACP70_P1_AUDIO0_RX_THRESHOLD 0x7
+#define ACP70_P1_AUDIO1_RX_THRESHOLD 0x5
+#define ACP70_P1_AUDIO2_RX_THRESHOLD 0x3
+
+#define ACP70_SDW0_DMA_TX_IRQ_MASK(i) (ACP_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW0_DMA_RX_IRQ_MASK(i) (ACP_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+/*
+ * Below entries describes SDW1 instance DMA stream id and DMA irq bit mapping
+ * in ACP_EXTENAL_INTR_CNTL1 register for ACP70/ACP71 platforms
+ * Stream id IRQ Bit
+ * 0 (SDW1_AUDIO0_TX) 8
+ * 1 (SDW1_AUDIO1_TX) 6
+ * 2 (SDW1_AUDIO2_TX) 4
+ * 3 (SDW1_AUDIO0_RX) 7
+ * 4 (SDW1_AUDIO1_RX) 5
+ * 5 (SDW1_AUDIO2_RX) 3
+ */
+#define ACP70_SDW1_DMA_TX_IRQ_MASK(i) (ACP70_P1_AUDIO0_TX_THRESHOLD - (2 * (i)))
+#define ACP70_SDW1_DMA_RX_IRQ_MASK(i) (ACP70_P1_AUDIO0_RX_THRESHOLD - (2 * ((i) - 3)))
+
+#define ACP70_SW0_AUDIO0_TX_EN ACP_SW0_AUDIO0_TX_EN
+#define ACP70_SW0_AUDIO1_TX_EN ACP_SW0_AUDIO1_TX_EN
+#define ACP70_SW0_AUDIO2_TX_EN ACP_SW0_AUDIO2_TX_EN
+#define ACP70_SW0_AUDIO0_RX_EN ACP_SW0_AUDIO0_RX_EN
+#define ACP70_SW0_AUDIO1_RX_EN ACP_SW0_AUDIO1_RX_EN
+#define ACP70_SW0_AUDIO2_RX_EN ACP_SW0_AUDIO2_RX_EN
+
+#define ACP70_SW1_AUDIO0_TX_EN 0x0003C10
+#define ACP70_SW1_AUDIO1_TX_EN 0x0003C50
+#define ACP70_SW1_AUDIO2_TX_EN 0x0003C6C
+#define ACP70_SW1_AUDIO0_RX_EN 0x0003C88
+#define ACP70_SW1_AUDIO1_RX_EN 0x0003D28
+#define ACP70_SW1_AUDIO2_RX_EN 0x0003D44
+
enum acp_config {
ACP_CONFIG_0 = 0,
ACP_CONFIG_1,
@@ -146,20 +204,34 @@ enum acp_config {
ACP_CONFIG_13,
ACP_CONFIG_14,
ACP_CONFIG_15,
+ ACP_CONFIG_16,
+ ACP_CONFIG_17,
+ ACP_CONFIG_18,
+ ACP_CONFIG_19,
+ ACP_CONFIG_20,
+};
+
+enum amd_acp63_sdw0_channel {
+ ACP63_SDW0_AUDIO0_TX = 0,
+ ACP63_SDW0_AUDIO1_TX,
+ ACP63_SDW0_AUDIO2_TX,
+ ACP63_SDW0_AUDIO0_RX,
+ ACP63_SDW0_AUDIO1_RX,
+ ACP63_SDW0_AUDIO2_RX,
};
-enum amd_sdw0_channel {
- ACP_SDW0_AUDIO0_TX = 0,
- ACP_SDW0_AUDIO1_TX,
- ACP_SDW0_AUDIO2_TX,
- ACP_SDW0_AUDIO0_RX,
- ACP_SDW0_AUDIO1_RX,
- ACP_SDW0_AUDIO2_RX,
+enum amd_acp63_sdw1_channel {
+ ACP63_SDW1_AUDIO1_TX,
+ ACP63_SDW1_AUDIO1_RX,
};
-enum amd_sdw1_channel {
- ACP_SDW1_AUDIO1_TX,
- ACP_SDW1_AUDIO1_RX,
+enum amd_acp70_sdw_channel {
+ ACP70_SDW_AUDIO0_TX = 0,
+ ACP70_SDW_AUDIO1_TX,
+ ACP70_SDW_AUDIO2_TX,
+ ACP70_SDW_AUDIO0_RX,
+ ACP70_SDW_AUDIO1_RX,
+ ACP70_SDW_AUDIO2_RX,
};
struct pdm_stream_instance {
@@ -180,8 +252,11 @@ struct pdm_dev_data {
struct sdw_dma_dev_data {
void __iomem *acp_base;
struct mutex *acp_lock; /* used to protect acp common register access */
- struct snd_pcm_substream *sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
- struct snd_pcm_substream *sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
+ u32 acp_rev;
+ struct snd_pcm_substream *acp63_sdw0_dma_stream[ACP63_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp63_sdw1_dma_stream[ACP63_SDW1_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw0_dma_stream[ACP70_SDW0_DMA_MAX_STREAMS];
+ struct snd_pcm_substream *acp70_sdw1_dma_stream[ACP70_SDW1_DMA_MAX_STREAMS];
};
struct acp_sdw_dma_stream {
@@ -212,10 +287,35 @@ struct sdw_dma_ring_buf_reg {
u32 pos_high_reg;
};
+struct acp63_dev_data;
+
+/**
+ * struct acp_hw_ops - ACP PCI driver platform specific ops
+ * @acp_init: ACP initialization
+ * @acp_deinit: ACP de-initialization
+ * @acp_get_config: function to read the acp pin configuration
+ * @acp_sdw_dma_irq_thread: ACP SoundWire DMA interrupt thread
+ * acp_suspend: ACP system level suspend callback
+ * acp_resume: ACP system level resume callback
+ * acp_suspend_runtime: ACP runtime suspend callback
+ * acp_resume_runtime: ACP runtime resume callback
+ */
+struct acp_hw_ops {
+ int (*acp_init)(void __iomem *acp_base, struct device *dev);
+ int (*acp_deinit)(void __iomem *acp_base, struct device *dev);
+ void (*acp_get_config)(struct pci_dev *pci, struct acp63_dev_data *acp_data);
+ void (*acp_sdw_dma_irq_thread)(struct acp63_dev_data *acp_data);
+ int (*acp_suspend)(struct device *dev);
+ int (*acp_resume)(struct device *dev);
+ int (*acp_suspend_runtime)(struct device *dev);
+ int (*acp_resume_runtime)(struct device *dev);
+};
+
/**
* struct acp63_dev_data - acp pci driver context
* @acp63_base: acp mmio base
* @res: resource
+ * @hw_ops: ACP pci driver platform-specific ops
* @pdm_dev: ACP PDM controller platform device
* @dmic_codec: platform device for DMIC Codec
* sdw_dma_dev: platform device for SoundWire DMA controller
@@ -229,16 +329,25 @@ struct sdw_dma_ring_buf_reg {
* @is_pdm_config: flat set to true when PDM configuration is selected from BIOS
* @is_sdw_config: flag set to true when SDW configuration is selected from BIOS
* @sdw_en_stat: flag set to true when any one of the SoundWire manager instance is enabled
+ * @acp70_sdw0_wake_event: flag set to true when wake irq asserted for SW0 instance
+ * @acp70_sdw1_wake_event: flag set to true when wake irq asserted for SW1 instance
* @addr: pci ioremap address
* @reg_range: ACP reigister range
* @acp_rev: ACP PCI revision id
- * @sdw0-dma_intr_stat: DMA interrupt status array for SoundWire manager-SW0 instance
- * @sdw_dma_intr_stat: DMA interrupt status array for SoundWire manager-SW1 instance
+ * @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW0 instance
+ * @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
+ * manager-SW1 instance
+ * @acp70_sdw0-dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW0 instance
+ * @acp70_sdw_dma_intr_stat: DMA interrupt status array for ACP7.0 platform SoundWire
+ * manager-SW1 instance
*/
struct acp63_dev_data {
void __iomem *acp63_base;
struct resource *res;
+ struct acp_hw_ops *hw_ops;
struct platform_device *pdm_dev;
struct platform_device *dmic_codec_dev;
struct platform_device *sdw_dma_dev;
@@ -253,11 +362,80 @@ struct acp63_dev_data {
bool is_pdm_config;
bool is_sdw_config;
bool sdw_en_stat;
+ bool acp70_sdw0_wake_event;
+ bool acp70_sdw1_wake_event;
u32 addr;
u32 reg_range;
u32 acp_rev;
- u16 sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
- u16 sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
+ u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
+ u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
+ u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS];
+ u16 acp70_sdw1_dma_intr_stat[ACP70_SDW1_DMA_MAX_STREAMS];
};
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops);
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops);
+
+static inline int acp_hw_init(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_init)
+ return ACP_HW_OPS(adata, acp_init)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_deinit(struct acp63_dev_data *adata, struct device *dev)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_deinit)
+ return ACP_HW_OPS(adata, acp_deinit)(adata->acp63_base, dev);
+ return -EOPNOTSUPP;
+}
+
+static inline void acp_hw_get_config(struct pci_dev *pci, struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_get_config)
+ ACP_HW_OPS(adata, acp_get_config)(pci, adata);
+}
+
+static inline void acp_hw_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ if (adata && adata->hw_ops && adata->hw_ops->acp_sdw_dma_irq_thread)
+ ACP_HW_OPS(adata, acp_sdw_dma_irq_thread)(adata);
+}
+
+static inline int acp_hw_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend)
+ return ACP_HW_OPS(adata, acp_suspend)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume)
+ return ACP_HW_OPS(adata, acp_resume)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_suspend_runtime(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_suspend_runtime)
+ return ACP_HW_OPS(adata, acp_suspend_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
+static inline int acp_hw_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata = dev_get_drvdata(dev);
+
+ if (adata && adata->hw_ops && adata->hw_ops->acp_resume_runtime)
+ return ACP_HW_OPS(adata, acp_resume_runtime)(dev);
+ return -EOPNOTSUPP;
+}
+
int snd_amd_acp_find_config(struct pci_dev *pci);
diff --git a/sound/soc/amd/ps/pci-ps.c b/sound/soc/amd/ps/pci-ps.c
index 8b556950b855..2ff8e67c19bd 100644
--- a/sound/soc/amd/ps/pci-ps.c
+++ b/sound/soc/amd/ps/pci-ps.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * AMD Pink Sardine ACP PCI Driver
+ * AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
*
- * Copyright 2022 Advanced Micro Devices, Inc.
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
*/
#include <linux/pci.h>
@@ -21,109 +21,168 @@
#include "acp63.h"
-static int acp63_power_on(void __iomem *acp_base)
+static void handle_acp70_sdw_wake_event(struct acp63_dev_data *adata)
{
- u32 val;
-
- val = readl(acp_base + ACP_PGFSM_STATUS);
-
- if (!val)
- return val;
+ struct amd_sdw_manager *amd_manager;
- if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
- writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+ if (adata->acp70_sdw0_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw0_wake_event = 0;
+ }
- return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP_TIMEOUT);
+ if (adata->acp70_sdw1_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw1_wake_event = 0;
+ }
}
-static int acp63_reset(void __iomem *acp_base)
+static short int check_and_handle_acp70_sdw_wake_irq(struct acp63_dev_data *adata)
{
- u32 val;
- int ret;
+ u32 ext_intr_stat1;
+ int irq_flag = 0;
+ bool sdw_wake_irq = false;
- writel(1, acp_base + ACP_SOFT_RESET);
-
- ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
- val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
- DELAY_US, ACP_TIMEOUT);
- if (ret)
- return ret;
-
- writel(0, acp_base + ACP_SOFT_RESET);
-
- return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
-}
-
-static void acp63_enable_interrupts(void __iomem *acp_base)
-{
- writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
- writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
-}
+ ext_intr_stat1 = readl(adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
+ writel(ACP70_SDW0_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
-static void acp63_disable_interrupts(void __iomem *acp_base)
-{
- writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
- writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
- writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
-}
+ if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
+ writel(ACP70_SDW1_HOST_WAKE_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
-static int acp63_init(void __iomem *acp_base, struct device *dev)
-{
- int ret;
+ if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW0_WAKE_EN);
+ writel(ACP70_SDW0_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
- ret = acp63_power_on(acp_base);
- if (ret) {
- dev_err(dev, "ACP power on failed\n");
- return ret;
+ if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
+ writel(0, adata->acp63_base + ACP_SW1_WAKE_EN);
+ writel(ACP70_SDW1_PME_STAT, adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
}
- writel(0x01, acp_base + ACP_CONTROL);
- ret = acp63_reset(acp_base);
- if (ret) {
- dev_err(dev, "ACP reset failed\n");
- return ret;
+
+ if (sdw_wake_irq) {
+ handle_acp70_sdw_wake_event(adata);
+ irq_flag = 1;
}
- acp63_enable_interrupts(acp_base);
- writel(0, acp_base + ACP_ZSC_DSP_CTRL);
- return 0;
+ return irq_flag;
}
-static int acp63_deinit(void __iomem *acp_base, struct device *dev)
+static short int check_and_handle_sdw_dma_irq(struct acp63_dev_data *adata, u32 ext_intr_stat,
+ u32 ext_intr_stat1)
{
- int ret;
+ u32 stream_id = 0;
+ u16 sdw_dma_irq_flag = 0;
+ u16 index;
- acp63_disable_interrupts(acp_base);
- ret = acp63_reset(acp_base);
- if (ret) {
- dev_err(dev, "ACP reset failed\n");
- return ret;
+ if (ext_intr_stat & ACP63_SDW_DMA_IRQ_MASK) {
+ for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat & BIT(index)) {
+ writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
+ switch (index) {
+ case ACP_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_TX;
+ break;
+ case ACP_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_TX;
+ break;
+ case ACP_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_TX;
+ break;
+ case ACP_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO0_RX;
+ break;
+ case ACP_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO1_RX;
+ break;
+ case ACP_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP63_SDW0_AUDIO2_RX;
+ break;
+ }
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 1;
+ break;
+ }
+ sdw_dma_irq_flag = 1;
+ }
+ }
}
- writel(0, acp_base + ACP_CONTROL);
- writel(1, acp_base + ACP_ZSC_DSP_CTRL);
- return 0;
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_RX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_RX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_RX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ if (ext_intr_stat1 & ACP63_P1_AUDIO1_TX_THRESHOLD) {
+ writel(ACP63_P1_AUDIO1_TX_THRESHOLD,
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ adata->acp63_sdw1_dma_intr_stat[ACP63_SDW1_AUDIO1_TX] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ if (ext_intr_stat1 & ACP70_P1_SDW_DMA_IRQ_MASK) {
+ for (index = ACP70_P1_AUDIO2_RX_THRESHOLD;
+ index <= ACP70_P1_AUDIO0_TX_THRESHOLD; index++) {
+ if (ext_intr_stat1 & BIT(index)) {
+ writel(BIT(index),
+ adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
+ switch (index) {
+ case ACP70_P1_AUDIO0_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_TX;
+ break;
+ case ACP70_P1_AUDIO1_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_TX;
+ break;
+ case ACP70_P1_AUDIO2_TX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_TX;
+ break;
+ case ACP70_P1_AUDIO0_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO0_RX;
+ break;
+ case ACP70_P1_AUDIO1_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO1_RX;
+ break;
+ case ACP70_P1_AUDIO2_RX_THRESHOLD:
+ stream_id = ACP70_SDW_AUDIO2_RX;
+ break;
+ }
+
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 1;
+ sdw_dma_irq_flag = 1;
+ }
+ }
+ }
+ break;
+ }
+ return sdw_dma_irq_flag;
}
static irqreturn_t acp63_irq_thread(int irq, void *context)
{
- struct sdw_dma_dev_data *sdw_dma_data;
struct acp63_dev_data *adata = context;
- u32 stream_index;
-
- sdw_dma_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
- for (stream_index = 0; stream_index < ACP63_SDW0_DMA_MAX_STREAMS; stream_index++) {
- if (adata->sdw0_dma_intr_stat[stream_index]) {
- if (sdw_dma_data->sdw0_dma_stream[stream_index])
- snd_pcm_period_elapsed(sdw_dma_data->sdw0_dma_stream[stream_index]);
- adata->sdw0_dma_intr_stat[stream_index] = 0;
- }
- }
- for (stream_index = 0; stream_index < ACP63_SDW1_DMA_MAX_STREAMS; stream_index++) {
- if (adata->sdw1_dma_intr_stat[stream_index]) {
- if (sdw_dma_data->sdw1_dma_stream[stream_index])
- snd_pcm_period_elapsed(sdw_dma_data->sdw1_dma_stream[stream_index]);
- adata->sdw1_dma_intr_stat[stream_index] = 0;
- }
- }
+ acp_hw_sdw_dma_irq_thread(adata);
return IRQ_HANDLED;
}
@@ -133,10 +192,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
struct pdm_dev_data *ps_pdm_data;
struct amd_sdw_manager *amd_manager;
u32 ext_intr_stat, ext_intr_stat1;
- u32 stream_id = 0;
u16 irq_flag = 0;
u16 sdw_dma_irq_flag = 0;
- u16 index;
adata = dev_id;
if (!adata)
@@ -173,6 +230,9 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
irq_flag = 1;
}
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ irq_flag = check_and_handle_acp70_sdw_wake_irq(adata);
+
if (ext_intr_stat & BIT(PDM_DMA_STAT)) {
ps_pdm_data = dev_get_drvdata(&adata->pdm_dev->dev);
writel(BIT(PDM_DMA_STAT), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
@@ -180,51 +240,8 @@ static irqreturn_t acp63_irq_handler(int irq, void *dev_id)
snd_pcm_period_elapsed(ps_pdm_data->capture_stream);
irq_flag = 1;
}
- if (ext_intr_stat & ACP_SDW_DMA_IRQ_MASK) {
- for (index = ACP_AUDIO2_RX_THRESHOLD; index <= ACP_AUDIO0_TX_THRESHOLD; index++) {
- if (ext_intr_stat & BIT(index)) {
- writel(BIT(index), adata->acp63_base + ACP_EXTERNAL_INTR_STAT);
- switch (index) {
- case ACP_AUDIO0_TX_THRESHOLD:
- stream_id = ACP_SDW0_AUDIO0_TX;
- break;
- case ACP_AUDIO1_TX_THRESHOLD:
- stream_id = ACP_SDW0_AUDIO1_TX;
- break;
- case ACP_AUDIO2_TX_THRESHOLD:
- stream_id = ACP_SDW0_AUDIO2_TX;
- break;
- case ACP_AUDIO0_RX_THRESHOLD:
- stream_id = ACP_SDW0_AUDIO0_RX;
- break;
- case ACP_AUDIO1_RX_THRESHOLD:
- stream_id = ACP_SDW0_AUDIO1_RX;
- break;
- case ACP_AUDIO2_RX_THRESHOLD:
- stream_id = ACP_SDW0_AUDIO2_RX;
- break;
- }
-
- adata->sdw0_dma_intr_stat[stream_id] = 1;
- sdw_dma_irq_flag = 1;
- }
- }
- }
-
- if (ext_intr_stat1 & ACP_P1_AUDIO1_RX_THRESHOLD) {
- writel(ACP_P1_AUDIO1_RX_THRESHOLD,
- adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
- adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_RX] = 1;
- sdw_dma_irq_flag = 1;
- }
-
- if (ext_intr_stat1 & ACP_P1_AUDIO1_TX_THRESHOLD) {
- writel(ACP_P1_AUDIO1_TX_THRESHOLD,
- adata->acp63_base + ACP_EXTERNAL_INTR_STAT1);
- adata->sdw1_dma_intr_stat[ACP_SDW1_AUDIO1_TX] = 1;
- sdw_dma_irq_flag = 1;
- }
+ sdw_dma_irq_flag = check_and_handle_sdw_dma_irq(adata, ext_intr_stat, ext_intr_stat1);
if (sdw_dma_irq_flag)
return IRQ_WAKE_THREAD;
@@ -380,7 +397,6 @@ static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *a
const union acpi_object *obj;
acpi_handle handle;
acpi_integer dmic_status;
- u32 config;
bool is_dmic_dev = false;
bool is_sdw_dev = false;
bool wov_en, dmic_en;
@@ -390,30 +406,7 @@ static int get_acp63_device_config(struct pci_dev *pci, struct acp63_dev_data *a
wov_en = true;
dmic_en = false;
- config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
- switch (config) {
- case ACP_CONFIG_4:
- case ACP_CONFIG_5:
- case ACP_CONFIG_10:
- case ACP_CONFIG_11:
- acp_data->is_pdm_config = true;
- break;
- case ACP_CONFIG_2:
- case ACP_CONFIG_3:
- acp_data->is_sdw_config = true;
- break;
- case ACP_CONFIG_6:
- case ACP_CONFIG_7:
- case ACP_CONFIG_12:
- case ACP_CONFIG_8:
- case ACP_CONFIG_13:
- case ACP_CONFIG_14:
- acp_data->is_pdm_config = true;
- acp_data->is_sdw_config = true;
- break;
- default:
- break;
- }
+ acp_hw_get_config(pci, acp_data);
if (acp_data->is_pdm_config) {
pdm_dev = acpi_find_child_device(ACPI_COMPANION(&pci->dev), ACP63_DMIC_ADDR, 0);
@@ -540,11 +533,33 @@ unregister_dmic_codec_dev:
unregister_pdm_dev:
platform_device_unregister(adata->pdm_dev);
de_init:
- if (acp63_deinit(adata->acp63_base, &pci->dev))
+ if (acp_hw_deinit(adata, &pci->dev))
dev_err(&pci->dev, "ACP de-init failed\n");
return ret;
}
+static int acp_hw_init_ops(struct acp63_dev_data *adata, struct pci_dev *pci)
+{
+ adata->hw_ops = devm_kzalloc(&pci->dev, sizeof(struct acp_hw_ops),
+ GFP_KERNEL);
+ if (!adata->hw_ops)
+ return -ENOMEM;
+
+ switch (adata->acp_rev) {
+ case ACP63_PCI_REV:
+ acp63_hw_init_ops(adata->hw_ops);
+ break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ acp70_hw_init_ops(adata->hw_ops);
+ break;
+ default:
+ dev_err(&pci->dev, "ACP device not found\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
static int snd_acp63_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
@@ -560,12 +575,14 @@ static int snd_acp63_probe(struct pci_dev *pci,
if (flag)
return -ENODEV;
- /* Pink Sardine device check */
+ /* ACP PCI revision id check for ACP6.3, ACP7.0 & ACP7.1 platforms */
switch (pci->revision) {
- case 0x63:
+ case ACP63_PCI_REV:
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
break;
default:
- dev_dbg(&pci->dev, "acp63 pci device not found\n");
+ dev_dbg(&pci->dev, "acp63/acp70/acp71 pci device not found\n");
return -ENODEV;
}
if (pci_enable_device(pci)) {
@@ -598,7 +615,12 @@ static int snd_acp63_probe(struct pci_dev *pci,
pci_set_master(pci);
pci_set_drvdata(pci, adata);
mutex_init(&adata->acp_lock);
- ret = acp63_init(adata->acp63_base, &pci->dev);
+ ret = acp_hw_init_ops(adata, pci);
+ if (ret) {
+ dev_err(&pci->dev, "ACP hw ops init failed\n");
+ goto release_regions;
+ }
+ ret = acp_hw_init(adata, &pci->dev);
if (ret)
goto release_regions;
ret = devm_request_threaded_irq(&pci->dev, pci->irq, acp63_irq_handler,
@@ -618,7 +640,11 @@ static int snd_acp63_probe(struct pci_dev *pci,
dev_err(&pci->dev, "ACP platform devices creation failed\n");
goto de_init;
}
- adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
+ if (adata->acp_rev >= ACP70_PCI_REV)
+ adata->machines = snd_soc_acpi_amd_acp70_sdw_machines;
+ else
+ adata->machines = snd_soc_acpi_amd_acp63_sdw_machines;
+
ret = acp63_machine_register(&pci->dev);
if (ret) {
dev_err(&pci->dev, "ACP machine register failed\n");
@@ -632,7 +658,7 @@ skip_pdev_creation:
pm_runtime_allow(&pci->dev);
return 0;
de_init:
- if (acp63_deinit(adata->acp63_base, &pci->dev))
+ if (acp_hw_deinit(adata, &pci->dev))
dev_err(&pci->dev, "ACP de-init failed\n");
release_regions:
pci_release_regions(pci);
@@ -642,90 +668,24 @@ disable_pci:
return ret;
}
-static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
+static int __maybe_unused snd_acp_suspend(struct device *dev)
{
- u32 sdw0_en, sdw1_en;
-
- sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
- sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
- return (sdw0_en || sdw1_en);
-}
-
-static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
-{
- u32 val;
-
- val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
- if (val && adata->sdw->pdev[0])
- pm_request_resume(&adata->sdw->pdev[0]->dev);
-
- val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
- if (val && adata->sdw->pdev[1])
- pm_request_resume(&adata->sdw->pdev[1]->dev);
-}
-
-static int __maybe_unused snd_acp63_suspend(struct device *dev)
-{
- struct acp63_dev_data *adata;
- int ret;
-
- 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) {
- writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
- return 0;
- }
- }
- ret = acp63_deinit(adata->acp63_base, dev);
- if (ret)
- dev_err(dev, "ACP de-init failed\n");
-
- return ret;
+ return acp_hw_suspend(dev);
}
-static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
+static int __maybe_unused snd_acp_runtime_resume(struct device *dev)
{
- struct acp63_dev_data *adata;
- int ret;
-
- adata = dev_get_drvdata(dev);
- 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");
- return ret;
- }
-
- if (!adata->sdw_en_stat)
- handle_acp63_sdw_pme_event(adata);
- return 0;
+ return acp_hw_runtime_resume(dev);
}
-static int __maybe_unused snd_acp63_resume(struct device *dev)
+static int __maybe_unused snd_acp_resume(struct device *dev)
{
- struct acp63_dev_data *adata;
- int ret;
-
- adata = dev_get_drvdata(dev);
- 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");
-
- return ret;
+ return acp_hw_resume(dev);
}
static const struct dev_pm_ops acp63_pm_ops = {
- SET_RUNTIME_PM_OPS(snd_acp63_suspend, snd_acp63_runtime_resume, NULL)
- SET_SYSTEM_SLEEP_PM_OPS(snd_acp63_suspend, snd_acp63_resume)
+ SET_RUNTIME_PM_OPS(snd_acp_suspend, snd_acp_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(snd_acp_suspend, snd_acp_resume)
};
static void snd_acp63_remove(struct pci_dev *pci)
@@ -744,7 +704,7 @@ static void snd_acp63_remove(struct pci_dev *pci)
}
if (adata->mach_dev)
platform_device_unregister(adata->mach_dev);
- ret = acp63_deinit(adata->acp63_base, &pci->dev);
+ ret = acp_hw_deinit(adata, &pci->dev);
if (ret)
dev_err(&pci->dev, "ACP de-init failed\n");
pm_runtime_forbid(&pci->dev);
@@ -775,7 +735,7 @@ module_pci_driver(ps_acp63_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
MODULE_AUTHOR("Syed.SabaKareem@amd.com");
-MODULE_DESCRIPTION("AMD ACP Pink Sardine PCI driver");
+MODULE_DESCRIPTION("AMD common ACP PCI driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
MODULE_IMPORT_NS("SOUNDWIRE_AMD_INIT");
MODULE_IMPORT_NS("SND_AMD_SOUNDWIRE_ACPI");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c
new file mode 100644
index 000000000000..1c89fb5fe1da
--- /dev/null
+++ b/sound/soc/amd/ps/ps-common.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * AMD ACP PCI driver callback routines for ACP6.3, ACP7.0 & ACP7.1
+ * platforms.
+ *
+ * Copyright 2025 Advanced Micro Devices, Inc.
+ * Authors: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+
+#include "acp63.h"
+
+static int acp63_power_on(void __iomem *acp_base)
+{
+ u32 val;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return val;
+
+ if ((val & ACP63_PGFSM_STATUS_MASK) != ACP63_POWER_ON_IN_PROGRESS)
+ writel(ACP63_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static int acp63_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP63_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP63_TIMEOUT);
+}
+
+static void acp63_enable_interrupts(void __iomem *acp_base)
+{
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+}
+
+static void acp63_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp63_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp63_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ acp63_enable_interrupts(acp_base);
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static int acp63_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp63_disable_interrupts(acp_base);
+ ret = acp63_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_CONTROL);
+ writel(1, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp63_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static bool check_acp_sdw_enable_status(struct acp63_dev_data *adata)
+{
+ u32 sdw0_en, sdw1_en;
+
+ sdw0_en = readl(adata->acp63_base + ACP_SW0_EN);
+ sdw1_en = readl(adata->acp63_base + ACP_SW1_EN);
+ return (sdw0_en || sdw1_en);
+}
+
+static void handle_acp63_sdw_pme_event(struct acp63_dev_data *adata)
+{
+ u32 val;
+
+ val = readl(adata->acp63_base + ACP_SW0_WAKE_EN);
+ if (val && adata->sdw->pdev[0])
+ pm_request_resume(&adata->sdw->pdev[0]->dev);
+
+ val = readl(adata->acp63_base + ACP_SW1_WAKE_EN);
+ if (val && adata->sdw->pdev[1])
+ pm_request_resume(&adata->sdw->pdev[1]->dev);
+}
+
+static int __maybe_unused snd_acp63_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ 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) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+
+ if (!adata->sdw_en_stat)
+ handle_acp63_sdw_pme_event(adata);
+ return 0;
+}
+
+static int __maybe_unused snd_acp63_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ return ret;
+}
+
+static void acp63_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP63_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw0_dma_stream[stream_id]);
+ adata->acp63_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP63_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp63_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp63_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp63_sdw1_dma_stream[stream_id]);
+ adata->acp63_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+void acp63_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp63_init;
+ hw_ops->acp_deinit = acp63_deinit;
+ hw_ops->acp_get_config = acp63_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp63_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp63_suspend;
+ hw_ops->acp_resume = snd_acp63_resume;
+ hw_ops->acp_suspend_runtime = snd_acp63_suspend;
+ hw_ops->acp_resume_runtime = snd_acp63_runtime_resume;
+}
+
+static int acp70_power_on(void __iomem *acp_base)
+{
+ u32 val = 0;
+
+ val = readl(acp_base + ACP_PGFSM_STATUS);
+
+ if (!val)
+ return 0;
+ if (val & ACP70_PGFSM_STATUS_MASK)
+ writel(ACP70_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
+
+ return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static int acp70_reset(void __iomem *acp_base)
+{
+ u32 val;
+ int ret;
+
+ writel(1, acp_base + ACP_SOFT_RESET);
+
+ ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
+ val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
+ DELAY_US, ACP70_TIMEOUT);
+ if (ret)
+ return ret;
+
+ writel(0, acp_base + ACP_SOFT_RESET);
+
+ return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP70_TIMEOUT);
+}
+
+static void acp70_enable_sdw_host_wake_interrupts(void __iomem *acp_base)
+{
+ u32 ext_intr_cntl1;
+
+ ext_intr_cntl1 = readl(acp_base + ACP_EXTERNAL_INTR_CNTL1);
+ ext_intr_cntl1 |= ACP70_SDW_HOST_WAKE_MASK;
+ writel(ext_intr_cntl1, acp_base + ACP_EXTERNAL_INTR_CNTL1);
+}
+
+static void acp70_enable_interrupts(void __iomem *acp_base)
+{
+ u32 sdw0_wake_en, sdw1_wake_en;
+
+ writel(1, acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(ACP_ERROR_IRQ, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ sdw0_wake_en = readl(acp_base + ACP_SW0_WAKE_EN);
+ sdw1_wake_en = readl(acp_base + ACP_SW1_WAKE_EN);
+ if (sdw0_wake_en || sdw1_wake_en)
+ acp70_enable_sdw_host_wake_interrupts(acp_base);
+}
+
+static void acp70_disable_interrupts(void __iomem *acp_base)
+{
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK, acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0, acp_base + ACP_EXTERNAL_INTR_ENB);
+}
+
+static int acp70_init(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ ret = acp70_power_on(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_CONTROL);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0, acp_base + ACP_ZSC_DSP_CTRL);
+ acp70_enable_interrupts(acp_base);
+ writel(0x1, acp_base + ACP_PME_EN);
+ return 0;
+}
+
+static int acp70_deinit(void __iomem *acp_base, struct device *dev)
+{
+ int ret;
+
+ acp70_disable_interrupts(acp_base);
+ ret = acp70_reset(acp_base);
+ if (ret) {
+ dev_err(dev, "ACP reset failed\n");
+ return ret;
+ }
+ writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+}
+
+static void acp70_get_config(struct pci_dev *pci, struct acp63_dev_data *acp_data)
+{
+ u32 config;
+
+ config = readl(acp_data->acp63_base + ACP_PIN_CONFIG);
+ dev_dbg(&pci->dev, "ACP config value: %d\n", config);
+ switch (config) {
+ case ACP_CONFIG_4:
+ case ACP_CONFIG_5:
+ case ACP_CONFIG_10:
+ case ACP_CONFIG_11:
+ case ACP_CONFIG_20:
+ acp_data->is_pdm_config = true;
+ break;
+ case ACP_CONFIG_2:
+ case ACP_CONFIG_3:
+ case ACP_CONFIG_16:
+ acp_data->is_sdw_config = true;
+ break;
+ case ACP_CONFIG_6:
+ case ACP_CONFIG_7:
+ case ACP_CONFIG_12:
+ case ACP_CONFIG_8:
+ case ACP_CONFIG_13:
+ case ACP_CONFIG_14:
+ case ACP_CONFIG_17:
+ case ACP_CONFIG_18:
+ case ACP_CONFIG_19:
+ acp_data->is_pdm_config = true;
+ acp_data->is_sdw_config = true;
+ break;
+ default:
+ break;
+ }
+}
+
+static void acp70_sdw_dma_irq_thread(struct acp63_dev_data *adata)
+{
+ struct sdw_dma_dev_data *sdw_data;
+ u32 stream_id;
+
+ sdw_data = dev_get_drvdata(&adata->sdw_dma_dev->dev);
+
+ for (stream_id = 0; stream_id < ACP70_SDW0_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw0_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw0_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw0_dma_stream[stream_id]);
+ adata->acp70_sdw0_dma_intr_stat[stream_id] = 0;
+ }
+ }
+ for (stream_id = 0; stream_id < ACP70_SDW1_DMA_MAX_STREAMS; stream_id++) {
+ if (adata->acp70_sdw1_dma_intr_stat[stream_id]) {
+ if (sdw_data->acp70_sdw1_dma_stream[stream_id])
+ snd_pcm_period_elapsed(sdw_data->acp70_sdw1_dma_stream[stream_id]);
+ adata->acp70_sdw1_dma_intr_stat[stream_id] = 0;
+ }
+ }
+}
+
+static int __maybe_unused snd_acp70_suspend(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ 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) {
+ writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ return 0;
+ }
+ }
+ ret = acp_hw_deinit(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP de-init failed\n");
+
+ return ret;
+}
+
+static int __maybe_unused snd_acp70_runtime_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret) {
+ dev_err(dev, "ACP init failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int __maybe_unused snd_acp70_resume(struct device *dev)
+{
+ struct acp63_dev_data *adata;
+ int ret;
+
+ adata = dev_get_drvdata(dev);
+
+ if (adata->sdw_en_stat) {
+ writel(0, adata->acp63_base + ACP_ZSC_DSP_CTRL);
+ writel(1, adata->acp63_base + ACP_PME_EN);
+ return 0;
+ }
+
+ ret = acp_hw_init(adata, dev);
+ if (ret)
+ dev_err(dev, "ACP init failed\n");
+
+ return ret;
+}
+
+void acp70_hw_init_ops(struct acp_hw_ops *hw_ops)
+{
+ hw_ops->acp_init = acp70_init;
+ hw_ops->acp_deinit = acp70_deinit;
+ hw_ops->acp_get_config = acp70_get_config;
+ hw_ops->acp_sdw_dma_irq_thread = acp70_sdw_dma_irq_thread;
+ hw_ops->acp_suspend = snd_acp70_suspend;
+ hw_ops->acp_resume = snd_acp70_resume;
+ hw_ops->acp_suspend_runtime = snd_acp70_suspend;
+ hw_ops->acp_resume_runtime = snd_acp70_runtime_resume;
+}
diff --git a/sound/soc/amd/ps/ps-pdm-dma.c b/sound/soc/amd/ps/ps-pdm-dma.c
index 318fc260f293..7cdeb34e8f73 100644
--- a/sound/soc/amd/ps/ps-pdm-dma.c
+++ b/sound/soc/amd/ps/ps-pdm-dma.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * AMD ALSA SoC Pink Sardine PDM Driver
+ * AMD ALSA SoC common PDM Driver for ACP6.3, ACP7.0 & ACP7.1 platforms.
*
- * Copyright 2022 Advanced Micro Devices, Inc.
+ * Copyright 2022, 2025 Advanced Micro Devices, Inc.
*/
#include <linux/platform_device.h>
@@ -458,6 +458,6 @@ static struct platform_driver acp63_pdm_dma_driver = {
module_platform_driver(acp63_pdm_dma_driver);
MODULE_AUTHOR("Syed.SabaKareem@amd.com");
-MODULE_DESCRIPTION("AMD PINK SARDINE PDM Driver");
+MODULE_DESCRIPTION("AMD common PDM Driver for ACP6.3, ACP7,0 & ACP7.1 platforms");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/ps/ps-sdw-dma.c b/sound/soc/amd/ps/ps-sdw-dma.c
index b602cca92b8b..21b336109c99 100644
--- a/sound/soc/amd/ps/ps-sdw-dma.c
+++ b/sound/soc/amd/ps/ps-sdw-dma.c
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * AMD ALSA SoC Pink Sardine SoundWire DMA Driver
+ * AMD ALSA SoC common SoundWire DMA Driver for ACP6.3, ACP7.0 and ACP7.1
+ * platforms.
*
- * Copyright 2023 Advanced Micro Devices, Inc.
+ * Copyright 2023, 2025 Advanced Micro Devices, Inc.
*/
#include <linux/err.h>
@@ -18,7 +19,7 @@
#define DRV_NAME "amd_ps_sdw_dma"
-static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+static struct sdw_dma_ring_buf_reg acp63_sdw0_dma_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
{ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
@@ -44,7 +45,7 @@ static struct sdw_dma_ring_buf_reg sdw0_dma_ring_buf_reg[ACP63_SDW0_DMA_MAX_STRE
* For TX/RX streams DMA registers programming for SDW1 instance, it uses ACP_P1_AUDIO1 register
* set as per hardware register documentation
*/
-static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+static struct sdw_dma_ring_buf_reg acp63_sdw1_dma_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
{ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
@@ -55,7 +56,7 @@ static struct sdw_dma_ring_buf_reg sdw1_dma_ring_buf_reg[ACP63_SDW1_DMA_MAX_STRE
ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
};
-static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
+static u32 acp63_sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
ACP_SW0_AUDIO0_TX_EN,
ACP_SW0_AUDIO1_TX_EN,
ACP_SW0_AUDIO2_TX_EN,
@@ -70,11 +71,77 @@ static u32 sdw0_dma_enable_reg[ACP63_SDW0_DMA_MAX_STREAMS] = {
* it uses ACP_SW1_AUDIO1_TX_EN and ACP_SW1_AUDIO1_RX_EN registers
* as per hardware register documentation.
*/
-static u32 sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
+static u32 acp63_sdw1_dma_enable_reg[ACP63_SDW1_DMA_MAX_STREAMS] = {
ACP_SW1_AUDIO1_TX_EN,
ACP_SW1_AUDIO1_RX_EN,
};
+static struct sdw_dma_ring_buf_reg acp70_sdw0_dma_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ {ACP_AUDIO0_TX_DMA_SIZE, ACP_AUDIO0_TX_FIFOADDR, ACP_AUDIO0_TX_FIFOSIZE,
+ ACP_AUDIO0_TX_RINGBUFSIZE, ACP_AUDIO0_TX_RINGBUFADDR, ACP_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_TX_DMA_SIZE, ACP_AUDIO1_TX_FIFOADDR, ACP_AUDIO1_TX_FIFOSIZE,
+ ACP_AUDIO1_TX_RINGBUFSIZE, ACP_AUDIO1_TX_RINGBUFADDR, ACP_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_TX_DMA_SIZE, ACP_AUDIO2_TX_FIFOADDR, ACP_AUDIO2_TX_FIFOSIZE,
+ ACP_AUDIO2_TX_RINGBUFSIZE, ACP_AUDIO2_TX_RINGBUFADDR, ACP_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO0_RX_DMA_SIZE, ACP_AUDIO0_RX_FIFOADDR, ACP_AUDIO0_RX_FIFOSIZE,
+ ACP_AUDIO0_RX_RINGBUFSIZE, ACP_AUDIO0_RX_RINGBUFADDR, ACP_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO1_RX_DMA_SIZE, ACP_AUDIO1_RX_FIFOADDR, ACP_AUDIO1_RX_FIFOSIZE,
+ ACP_AUDIO1_RX_RINGBUFSIZE, ACP_AUDIO1_RX_RINGBUFADDR, ACP_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_AUDIO2_RX_DMA_SIZE, ACP_AUDIO2_RX_FIFOADDR, ACP_AUDIO2_RX_FIFOSIZE,
+ ACP_AUDIO2_RX_RINGBUFSIZE, ACP_AUDIO2_RX_RINGBUFADDR, ACP_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static struct sdw_dma_ring_buf_reg acp70_sdw1_dma_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ {ACP_P1_AUDIO0_TX_DMA_SIZE, ACP_P1_AUDIO0_TX_FIFOADDR, ACP_P1_AUDIO0_TX_FIFOSIZE,
+ ACP_P1_AUDIO0_TX_RINGBUFSIZE, ACP_P1_AUDIO0_TX_RINGBUFADDR,
+ ACP_P1_AUDIO0_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_TX_DMA_SIZE, ACP_P1_AUDIO1_TX_FIFOADDR, ACP_P1_AUDIO1_TX_FIFOSIZE,
+ ACP_P1_AUDIO1_TX_RINGBUFSIZE, ACP_P1_AUDIO1_TX_RINGBUFADDR,
+ ACP_P1_AUDIO1_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_TX_DMA_SIZE, ACP_P1_AUDIO2_TX_FIFOADDR, ACP_P1_AUDIO2_TX_FIFOSIZE,
+ ACP_P1_AUDIO2_TX_RINGBUFSIZE, ACP_P1_AUDIO2_TX_RINGBUFADDR,
+ ACP_P1_AUDIO2_TX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_TX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO0_RX_DMA_SIZE, ACP_P1_AUDIO0_RX_FIFOADDR, ACP_P1_AUDIO0_RX_FIFOSIZE,
+ ACP_P1_AUDIO0_RX_RINGBUFSIZE, ACP_P1_AUDIO0_RX_RINGBUFADDR,
+ ACP_P1_AUDIO0_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO0_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO1_RX_DMA_SIZE, ACP_P1_AUDIO1_RX_FIFOADDR, ACP_P1_AUDIO1_RX_FIFOSIZE,
+ ACP_P1_AUDIO1_RX_RINGBUFSIZE, ACP_P1_AUDIO1_RX_RINGBUFADDR,
+ ACP_P1_AUDIO1_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO1_RX_LINEARPOSITIONCNTR_HIGH},
+ {ACP_P1_AUDIO2_RX_DMA_SIZE, ACP_P1_AUDIO2_RX_FIFOADDR, ACP_P1_AUDIO2_RX_FIFOSIZE,
+ ACP_P1_AUDIO2_RX_RINGBUFSIZE, ACP_P1_AUDIO2_RX_RINGBUFADDR,
+ ACP_P1_AUDIO2_RX_INTR_WATERMARK_SIZE,
+ ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_LOW, ACP_P1_AUDIO2_RX_LINEARPOSITIONCNTR_HIGH}
+};
+
+static u32 acp70_sdw0_dma_enable_reg[ACP70_SDW0_DMA_MAX_STREAMS] = {
+ ACP70_SW0_AUDIO0_TX_EN,
+ ACP70_SW0_AUDIO1_TX_EN,
+ ACP70_SW0_AUDIO2_TX_EN,
+ ACP70_SW0_AUDIO0_RX_EN,
+ ACP70_SW0_AUDIO1_RX_EN,
+ ACP70_SW0_AUDIO2_RX_EN,
+};
+
+static u32 acp70_sdw1_dma_enable_reg[ACP70_SDW1_DMA_MAX_STREAMS] = {
+ ACP70_SW1_AUDIO0_TX_EN,
+ ACP70_SW1_AUDIO1_TX_EN,
+ ACP70_SW1_AUDIO2_TX_EN,
+ ACP70_SW1_AUDIO0_RX_EN,
+ ACP70_SW1_AUDIO1_RX_EN,
+ ACP70_SW1_AUDIO2_RX_EN,
+};
+
static const struct snd_pcm_hardware acp63_sdw_hardware_playback = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
@@ -114,11 +181,10 @@ static const struct snd_pcm_hardware acp63_sdw_hardware_capture = {
.periods_max = SDW_CAPTURE_MAX_NUM_PERIODS,
};
-static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, bool enable)
+static void acp63_enable_disable_sdw_dma_interrupts(void __iomem *acp_base, u32 irq_mask,
+ u32 irq_mask1, bool enable)
{
u32 ext_intr_cntl, ext_intr_cntl1;
- u32 irq_mask = ACP_SDW_DMA_IRQ_MASK;
- u32 irq_mask1 = ACP_P1_SDW_DMA_IRQ_MASK;
if (enable) {
ext_intr_cntl = readl(acp_base + ACP_EXTERNAL_INTR_CNTL);
@@ -167,7 +233,7 @@ static void acp63_config_dma(struct acp_sdw_dma_stream *stream, void __iomem *ac
}
static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id, u32 size,
- u32 manager_instance)
+ u32 manager_instance, u32 acp_rev)
{
u32 reg_dma_size;
u32 reg_fifo_addr;
@@ -180,20 +246,47 @@ static int acp63_configure_sdw_ringbuffer(void __iomem *acp_base, u32 stream_id,
u32 sdw_ring_buf_size;
u32 sdw_mem_window_offset;
- switch (manager_instance) {
- case ACP_SDW0:
- reg_dma_size = sdw0_dma_ring_buf_reg[stream_id].reg_dma_size;
- reg_fifo_addr = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_addr;
- reg_fifo_size = sdw0_dma_ring_buf_reg[stream_id].reg_fifo_size;
- reg_ring_buf_size = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_size;
- reg_ring_buf_addr = sdw0_dma_ring_buf_reg[stream_id].reg_ring_buf_addr;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp63_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp63_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp63_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp63_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp63_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- reg_dma_size = sdw1_dma_ring_buf_reg[stream_id].reg_dma_size;
- reg_fifo_addr = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_addr;
- reg_fifo_size = sdw1_dma_ring_buf_reg[stream_id].reg_fifo_size;
- reg_ring_buf_size = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_size;
- reg_ring_buf_addr = sdw1_dma_ring_buf_reg[stream_id].reg_ring_buf_addr;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (manager_instance) {
+ case ACP_SDW0:
+ reg_dma_size = acp70_sdw0_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw0_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw0_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw0_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ case ACP_SDW1:
+ reg_dma_size = acp70_sdw1_dma_reg[stream_id].reg_dma_size;
+ reg_fifo_addr = acp70_sdw1_dma_reg[stream_id].reg_fifo_addr;
+ reg_fifo_size = acp70_sdw1_dma_reg[stream_id].reg_fifo_size;
+ reg_ring_buf_size = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_size;
+ reg_ring_buf_addr = acp70_sdw1_dma_reg[stream_id].reg_ring_buf_addr;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -265,21 +358,53 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
if (!stream)
return -EINVAL;
stream_id = stream->stream_id;
- switch (stream->instance) {
- case ACP_SDW0:
- sdw_data->sdw0_dma_stream[stream_id] = substream;
- water_mark_size_reg = sdw0_dma_ring_buf_reg[stream_id].water_mark_size_reg;
- acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- irq_mask = BIT(SDW0_DMA_TX_IRQ_MASK(stream_id));
- else
- irq_mask = BIT(SDW0_DMA_RX_IRQ_MASK(stream_id));
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp63_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP63_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP63_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp63_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ irq_mask = BIT(ACP63_SDW1_DMA_IRQ_MASK(stream_id));
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- sdw_data->sdw1_dma_stream[stream_id] = substream;
- acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
- water_mark_size_reg = sdw1_dma_ring_buf_reg[stream_id].water_mark_size_reg;
- irq_mask = BIT(SDW1_DMA_IRQ_MASK(stream_id));
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream_id] = substream;
+ water_mark_size_reg = acp70_sdw0_dma_reg[stream_id].water_mark_size_reg;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW0_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW0_DMA_RX_IRQ_MASK(stream_id));
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream_id] = substream;
+ acp_ext_intr_cntl_reg = ACP_EXTERNAL_INTR_CNTL1;
+ water_mark_size_reg = acp70_sdw1_dma_reg[stream_id].water_mark_size_reg;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ irq_mask = BIT(ACP70_SDW1_DMA_TX_IRQ_MASK(stream_id));
+ else
+ irq_mask = BIT(ACP70_SDW1_DMA_RX_IRQ_MASK(stream_id));
+
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -290,7 +415,7 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
stream->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
acp63_config_dma(stream, sdw_data->acp_base, stream_id);
ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, stream_id, size,
- stream->instance);
+ stream->instance, sdw_data->acp_rev);
if (ret) {
dev_err(component->dev, "Invalid DMA channel\n");
return -EINVAL;
@@ -302,20 +427,42 @@ static int acp63_sdw_dma_hw_params(struct snd_soc_component *component,
return 0;
}
-static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base)
+static u64 acp63_sdw_get_byte_count(struct acp_sdw_dma_stream *stream, void __iomem *acp_base,
+ u32 acp_rev)
{
union acp_sdw_dma_count byte_count;
u32 pos_low_reg, pos_high_reg;
byte_count.bytescount = 0;
- switch (stream->instance) {
- case ACP_SDW0:
- pos_low_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_low_reg;
- pos_high_reg = sdw0_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp63_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
break;
- case ACP_SDW1:
- pos_low_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_low_reg;
- pos_high_reg = sdw1_dma_ring_buf_reg[stream->stream_id].pos_high_reg;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ pos_low_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw0_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ case ACP_SDW1:
+ pos_low_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_low_reg;
+ pos_high_reg = acp70_sdw1_dma_reg[stream->stream_id].pos_high_reg;
+ break;
+ default:
+ goto POINTER_RETURN_BYTES;
+ }
break;
default:
goto POINTER_RETURN_BYTES;
@@ -340,7 +487,7 @@ static snd_pcm_uframes_t acp63_sdw_dma_pointer(struct snd_soc_component *comp,
stream = substream->runtime->private_data;
buffersize = frames_to_bytes(substream->runtime,
substream->runtime->buffer_size);
- bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base);
+ bytescount = acp63_sdw_get_byte_count(stream, sdw_data->acp_base, sdw_data->acp_rev);
if (bytescount > stream->bytescount)
bytescount -= stream->bytescount;
pos = do_div(bytescount, buffersize);
@@ -367,12 +514,31 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component,
stream = substream->runtime->private_data;
if (!stream)
return -EINVAL;
- switch (stream->instance) {
- case ACP_SDW0:
- sdw_data->sdw0_dma_stream[stream->stream_id] = NULL;
+ switch (sdw_data->acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp63_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp63_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- sdw_data->sdw1_dma_stream[stream->stream_id] = NULL;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_data->acp70_sdw0_dma_stream[stream->stream_id] = NULL;
+ break;
+ case ACP_SDW1:
+ sdw_data->acp70_sdw1_dma_stream[stream->stream_id] = NULL;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -382,7 +548,7 @@ static int acp63_sdw_dma_close(struct snd_soc_component *component,
}
static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
- void __iomem *acp_base, bool sdw_dma_enable)
+ void __iomem *acp_base, u32 acp_rev, bool sdw_dma_enable)
{
struct acp_sdw_dma_stream *stream;
u32 stream_id;
@@ -393,12 +559,31 @@ static int acp63_sdw_dma_enable(struct snd_pcm_substream *substream,
stream = substream->runtime->private_data;
stream_id = stream->stream_id;
- switch (stream->instance) {
- case ACP_SDW0:
- sdw_dma_en_reg = sdw0_dma_enable_reg[stream_id];
+ switch (acp_rev) {
+ case ACP63_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp63_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp63_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
break;
- case ACP_SDW1:
- sdw_dma_en_reg = sdw1_dma_enable_reg[stream_id];
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ switch (stream->instance) {
+ case ACP_SDW0:
+ sdw_dma_en_reg = acp70_sdw0_dma_enable_reg[stream_id];
+ break;
+ case ACP_SDW1:
+ sdw_dma_en_reg = acp70_sdw1_dma_enable_reg[stream_id];
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
@@ -422,12 +607,12 @@ static int acp63_sdw_dma_trigger(struct snd_soc_component *comp,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
case SNDRV_PCM_TRIGGER_RESUME:
- ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, true);
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, true);
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
- ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, false);
+ ret = acp63_sdw_dma_enable(substream, sdw_data->acp_base, sdw_data->acp_rev, false);
break;
default:
ret = -EINVAL;
@@ -474,6 +659,7 @@ static int acp63_sdw_platform_probe(struct platform_device *pdev)
return -ENOMEM;
sdw_data->acp_lock = &acp_data->acp_lock;
+ sdw_data->acp_rev = acp_data->acp_rev;
dev_set_drvdata(&pdev->dev, sdw_data);
status = devm_snd_soc_register_component(&pdev->dev,
&acp63_sdw_component,
@@ -495,15 +681,17 @@ static void acp63_sdw_platform_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
}
-static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+static int acp63_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
{
struct acp_sdw_dma_stream *stream;
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
u32 period_bytes, buf_size, water_mark_size_reg;
- u32 stream_count;
+ u32 stream_count, irq_mask, irq_mask1;
int index, instance, ret;
+ irq_mask = ACP63_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP63_P1_SDW_DMA_IRQ_MASK;
for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
if (instance == ACP_SDW0)
stream_count = ACP63_SDW0_DMA_MAX_STREAMS;
@@ -512,13 +700,11 @@ static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
for (index = 0; index < stream_count; index++) {
if (instance == ACP_SDW0) {
- substream = sdw_data->sdw0_dma_stream[index];
- water_mark_size_reg =
- sdw0_dma_ring_buf_reg[index].water_mark_size_reg;
+ substream = sdw_data->acp63_sdw0_dma_stream[index];
+ water_mark_size_reg = acp63_sdw0_dma_reg[index].water_mark_size_reg;
} else {
- substream = sdw_data->sdw1_dma_stream[index];
- water_mark_size_reg =
- sdw1_dma_ring_buf_reg[index].water_mark_size_reg;
+ substream = sdw_data->acp63_sdw1_dma_stream[index];
+ water_mark_size_reg = acp63_sdw1_dma_reg[index].water_mark_size_reg;
}
if (substream && substream->runtime) {
@@ -528,14 +714,56 @@ static int acp_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
buf_size = frames_to_bytes(runtime, runtime->buffer_size);
acp63_config_dma(stream, sdw_data->acp_base, index);
ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
- buf_size, instance);
+ buf_size, instance,
+ ACP63_PCI_REV);
if (ret)
return ret;
writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
}
}
}
- acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, true);
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
+ return 0;
+}
+
+static int acp70_restore_sdw_dma_config(struct sdw_dma_dev_data *sdw_data)
+{
+ struct acp_sdw_dma_stream *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
+ u32 period_bytes, buf_size, water_mark_size_reg;
+ u32 stream_count, irq_mask, irq_mask1;
+ int index, instance, ret;
+
+ irq_mask = ACP70_SDW_DMA_IRQ_MASK;
+ irq_mask1 = ACP70_P1_SDW_DMA_IRQ_MASK;
+ stream_count = ACP70_SDW0_DMA_MAX_STREAMS;
+ for (instance = 0; instance < AMD_SDW_MAX_MANAGERS; instance++) {
+ for (index = 0; index < stream_count; index++) {
+ if (instance == ACP_SDW0) {
+ substream = sdw_data->acp70_sdw0_dma_stream[index];
+ water_mark_size_reg = acp70_sdw0_dma_reg[index].water_mark_size_reg;
+ } else {
+ substream = sdw_data->acp70_sdw1_dma_stream[index];
+ water_mark_size_reg = acp70_sdw1_dma_reg[index].water_mark_size_reg;
+ }
+
+ if (substream && substream->runtime) {
+ runtime = substream->runtime;
+ stream = runtime->private_data;
+ period_bytes = frames_to_bytes(runtime, runtime->period_size);
+ buf_size = frames_to_bytes(runtime, runtime->buffer_size);
+ acp63_config_dma(stream, sdw_data->acp_base, index);
+ ret = acp63_configure_sdw_ringbuffer(sdw_data->acp_base, index,
+ buf_size, instance,
+ sdw_data->acp_rev);
+ if (ret)
+ return ret;
+ writel(period_bytes, sdw_data->acp_base + water_mark_size_reg);
+ }
+ }
+ }
+ acp63_enable_disable_sdw_dma_interrupts(sdw_data->acp_base, irq_mask, irq_mask1, true);
return 0;
}
@@ -544,7 +772,10 @@ static int __maybe_unused acp63_sdw_pcm_resume(struct device *dev)
struct sdw_dma_dev_data *sdw_data;
sdw_data = dev_get_drvdata(dev);
- return acp_restore_sdw_dma_config(sdw_data);
+ if (sdw_data->acp_rev == ACP63_PCI_REV)
+ return acp63_restore_sdw_dma_config(sdw_data);
+ else
+ return acp70_restore_sdw_dma_config(sdw_data);
}
static const struct dev_pm_ops acp63_pm_ops = {
@@ -563,6 +794,6 @@ static struct platform_driver acp63_sdw_dma_driver = {
module_platform_driver(acp63_sdw_dma_driver);
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
-MODULE_DESCRIPTION("AMD ACP6.3 PS SDW DMA Driver");
+MODULE_DESCRIPTION("AMD common SDW DMA Driver for ACP6.3, ACP7.0 & ACP7.1 platforms");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c
index 04304a7ad915..3eb862643b53 100644
--- a/sound/soc/codecs/cpcap.c
+++ b/sound/soc/codecs/cpcap.c
@@ -11,11 +11,21 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include <linux/mfd/motorola-cpcap.h>
#include <sound/core.h>
+#include <linux/input.h>
+#include <sound/jack.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+/* Register 8 - CPCAP_REG_INTS1 --- Interrupt Sense 1 */
+#define CPCAP_BIT_HS_S 9 /* Headset */
+#define CPCAP_BIT_MB2_S 10 /* Mic Bias2 */
+
+/* Register 9 - CPCAP_REG_INTS2 --- Interrupt Sense 2 */
+#define CPCAP_BIT_PTT_S 11 /* Push To Talk */
+
/* Register 512 CPCAP_REG_VAUDIOC --- Audio Regulator and Bias Voltage */
#define CPCAP_BIT_AUDIO_LOW_PWR 6
#define CPCAP_BIT_AUD_LOWPWR_SPEED 5
@@ -260,6 +270,10 @@ struct cpcap_audio {
int codec_clk_id;
int codec_freq;
int codec_format;
+ struct regulator *vaudio;
+ int hsirq;
+ int mb2irq;
+ struct snd_soc_jack jack;
};
static int cpcap_st_workaround(struct snd_soc_dapm_widget *w,
@@ -1626,17 +1640,123 @@ static int cpcap_audio_reset(struct snd_soc_component *component,
return 0;
}
+static irqreturn_t cpcap_hs_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mask = SND_JACK_HEADSET;
+ int val;
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S))) {
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_TXI, val, val);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(regmap, CPCAP_REG_RXOA, val, val);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+
+ /* Give PTTS time to settle */
+ msleep(20);
+
+ if (!regmap_test_bits(regmap, CPCAP_REG_INTS2,
+ BIT(CPCAP_BIT_PTT_S))) {
+ /* Headphones detected. (May also be a headset with the
+ * MFB pressed.)
+ */
+ status = SND_JACK_HEADPHONE;
+ dev_info(component->dev, "HP plugged in\n");
+ } else if (regmap_test_bits(regmap, CPCAP_REG_INTS1,
+ BIT(CPCAP_BIT_MB2_S)) == 1) {
+ status = SND_JACK_HEADSET;
+ dev_info(component->dev, "HS plugged in\n");
+ } else
+ dev_info(component->dev, "Unsupported HS plugged in\n");
+ } else {
+ bool mic = cpcap->jack.status & SND_JACK_MICROPHONE;
+
+ dev_info(component->dev, "H%s disconnect\n", mic ? "S" : "P");
+ val = BIT(CPCAP_BIT_MB_ON2) | BIT(CPCAP_BIT_PTT_CMP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_TXI, val, 0);
+
+ val = BIT(CPCAP_BIT_ST_HS_CP_EN);
+ regmap_update_bits(cpcap->regmap, CPCAP_REG_RXOA, val, 0);
+
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+
+ mask |= SND_JACK_BTN_0;
+ }
+
+ snd_soc_jack_report(&cpcap->jack, status, mask);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cpcap_mb2_irq_thread(int irq, void *data)
+{
+ struct snd_soc_component *component = data;
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cpcap->regmap;
+ int status = 0;
+ int mb2;
+ int ptt;
+
+ if (regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_HS_S)) == 1)
+ return IRQ_HANDLED;
+
+ mb2 = regmap_test_bits(regmap, CPCAP_REG_INTS1, BIT(CPCAP_BIT_MB2_S));
+ ptt = regmap_test_bits(regmap, CPCAP_REG_INTS2, BIT(CPCAP_BIT_PTT_S));
+
+ /* Initial detection might have been with MFB pressed */
+ if (!(cpcap->jack.status & SND_JACK_MICROPHONE)) {
+ if (ptt == 1 && mb2 == 1) {
+ dev_info(component->dev, "MIC plugged in\n");
+ snd_soc_jack_report(&cpcap->jack, SND_JACK_MICROPHONE,
+ SND_JACK_MICROPHONE);
+ }
+
+ return IRQ_HANDLED;
+ }
+
+ if (!mb2 || !ptt)
+ status = SND_JACK_BTN_0;
+
+ snd_soc_jack_report(&cpcap->jack, status, SND_JACK_BTN_0);
+
+ return IRQ_HANDLED;
+}
+
static int cpcap_soc_probe(struct snd_soc_component *component)
{
+ struct platform_device *pdev = to_platform_device(component->dev);
+ struct snd_soc_card *card = component->card;
struct cpcap_audio *cpcap;
int err;
cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL);
if (!cpcap)
return -ENOMEM;
+
snd_soc_component_set_drvdata(component, cpcap);
cpcap->component = component;
+ cpcap->vaudio = devm_regulator_get(component->dev, "VAUDIO");
+ if (IS_ERR(cpcap->vaudio))
+ return dev_err_probe(component->dev, PTR_ERR(cpcap->vaudio),
+ "Cannot get VAUDIO regulator\n");
+
+ err = snd_soc_card_jack_new(card, "Headphones",
+ SND_JACK_HEADSET | SND_JACK_BTN_0,
+ &cpcap->jack);
+ if (err < 0) {
+ dev_err(component->dev, "Cannot create HS jack: %i\n", err);
+ return err;
+ }
+
+ snd_jack_set_key(cpcap->jack.jack, SND_JACK_BTN_0, KEY_MEDIA);
+
cpcap->regmap = dev_get_regmap(component->dev->parent, NULL);
if (!cpcap->regmap)
return -ENODEV;
@@ -1646,17 +1766,95 @@ static int cpcap_soc_probe(struct snd_soc_component *component)
if (err)
return err;
- return cpcap_audio_reset(component, false);
+ cpcap->hsirq = platform_get_irq_byname(pdev, "hs");
+ if (cpcap->hsirq < 0)
+ return cpcap->hsirq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->hsirq, NULL,
+ cpcap_hs_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-hs",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no HS irq%i: %i\n",
+ cpcap->hsirq, err);
+ return err;
+ }
+
+ cpcap->mb2irq = platform_get_irq_byname(pdev, "mb2");
+ if (cpcap->mb2irq < 0)
+ return cpcap->mb2irq;
+
+ err = devm_request_threaded_irq(component->dev, cpcap->mb2irq, NULL,
+ cpcap_mb2_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-mb2",
+ component);
+ if (err) {
+ dev_warn(component->dev, "no MB2 irq%i: %i\n",
+ cpcap->mb2irq, err);
+ return err;
+ }
+
+ err = cpcap_audio_reset(component, false);
+ if (err)
+ return err;
+
+ cpcap_hs_irq_thread(cpcap->hsirq, component);
+
+ enable_irq_wake(cpcap->hsirq);
+ enable_irq_wake(cpcap->mb2irq);
+
+ return 0;
+}
+
+static void cpcap_soc_remove(struct snd_soc_component *component)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ disable_irq_wake(cpcap->hsirq);
+ disable_irq_wake(cpcap->mb2irq);
+}
+
+static int cpcap_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cpcap_audio *cpcap = snd_soc_component_get_drvdata(component);
+
+ /* VAIDIO should be kept in normal mode in order MIC/PTT to work */
+ if (cpcap->jack.status & SND_JACK_MICROPHONE)
+ return 0;
+
+ switch (level) {
+ case SND_SOC_BIAS_OFF:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_NORMAL);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regulator_set_mode(cpcap->vaudio, REGULATOR_MODE_STANDBY);
+ break;
+ case SND_SOC_BIAS_ON:
+ break;
+ }
+
+ return 0;
}
static const struct snd_soc_component_driver soc_codec_dev_cpcap = {
.probe = cpcap_soc_probe,
+ .remove = cpcap_soc_remove,
.controls = cpcap_snd_controls,
.num_controls = ARRAY_SIZE(cpcap_snd_controls),
.dapm_widgets = cpcap_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cpcap_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
+ .set_bias_level = cpcap_set_bias_level,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index 4fd6f97e5a49..0388f115470c 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -85,7 +85,9 @@ static struct snd_soc_dai_driver dmic_dai = {
| SNDRV_PCM_FMTBIT_S16_LE
| SNDRV_PCM_FMTBIT_DSD_U8
| SNDRV_PCM_FMTBIT_DSD_U16_LE
- | SNDRV_PCM_FMTBIT_DSD_U32_LE,
+ | SNDRV_PCM_FMTBIT_DSD_U32_LE
+ | SNDRV_PCM_FMTBIT_DSD_U16_BE
+ | SNDRV_PCM_FMTBIT_DSD_U32_BE,
},
.ops = &dmic_dai_ops,
};
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
index 9247b90d1b99..e033027fd4c7 100644
--- a/sound/soc/codecs/mt6358.c
+++ b/sound/soc/codecs/mt6358.c
@@ -162,47 +162,6 @@ static void capture_gpio_reset(struct mt6358_priv *priv)
0xf << 12, 0x0);
}
-/* use only when not govern by DAPM */
-static int mt6358_set_dcxo(struct mt6358_priv *priv, bool enable)
-{
- regmap_update_bits(priv->regmap, MT6358_DCXO_CW14,
- 0x1 << RG_XO_AUDIO_EN_M_SFT,
- (enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT);
- return 0;
-}
-
-/* use only when not govern by DAPM */
-static int mt6358_set_clksq(struct mt6358_priv *priv, bool enable)
-{
- /* audio clk source from internal dcxo */
- regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6,
- RG_CLKSQ_IN_SEL_TEST_MASK_SFT,
- 0x0);
-
- /* Enable/disable CLKSQ 26MHz */
- regmap_update_bits(priv->regmap, MT6358_AUDENC_ANA_CON6,
- RG_CLKSQ_EN_MASK_SFT,
- (enable ? 1 : 0) << RG_CLKSQ_EN_SFT);
- return 0;
-}
-
-/* use only when not govern by DAPM */
-static int mt6358_set_aud_global_bias(struct mt6358_priv *priv, bool enable)
-{
- regmap_update_bits(priv->regmap, MT6358_AUDDEC_ANA_CON13,
- RG_AUDGLB_PWRDN_VA28_MASK_SFT,
- (enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA28_SFT);
- return 0;
-}
-
-/* use only when not govern by DAPM */
-static int mt6358_set_topck(struct mt6358_priv *priv, bool enable)
-{
- regmap_update_bits(priv->regmap, MT6358_AUD_TOP_CKPDN_CON0,
- 0x0066, enable ? 0x0 : 0x66);
- return 0;
-}
-
static int mt6358_mtkaif_tx_enable(struct mt6358_priv *priv)
{
switch (priv->mtkaif_protocol) {
@@ -252,69 +211,6 @@ static int mt6358_mtkaif_tx_disable(struct mt6358_priv *priv)
return 0;
}
-int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt)
-{
- struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
-
- playback_gpio_set(priv);
- capture_gpio_set(priv);
- mt6358_mtkaif_tx_enable(priv);
-
- mt6358_set_dcxo(priv, true);
- mt6358_set_aud_global_bias(priv, true);
- mt6358_set_clksq(priv, true);
- mt6358_set_topck(priv, true);
-
- /* set dat_miso_loopback on */
- regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
- RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
- 1 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
- regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
- RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
- 1 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
- return 0;
-}
-EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_enable);
-
-int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt)
-{
- struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
-
- /* set dat_miso_loopback off */
- regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
- RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT,
- 0 << RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT);
- regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
- RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT,
- 0 << RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT);
-
- mt6358_set_topck(priv, false);
- mt6358_set_clksq(priv, false);
- mt6358_set_aud_global_bias(priv, false);
- mt6358_set_dcxo(priv, false);
-
- mt6358_mtkaif_tx_disable(priv);
- playback_gpio_reset(priv);
- capture_gpio_reset(priv);
- return 0;
-}
-EXPORT_SYMBOL_GPL(mt6358_mtkaif_calibration_disable);
-
-int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
- int phase_1, int phase_2)
-{
- struct mt6358_priv *priv = snd_soc_component_get_drvdata(cmpnt);
-
- regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
- RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT,
- phase_1 << RG_AUD_PAD_TOP_PHASE_MODE_SFT);
- regmap_update_bits(priv->regmap, MT6358_AUDIO_DIG_CFG,
- RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT,
- phase_2 << RG_AUD_PAD_TOP_PHASE_MODE2_SFT);
- return 0;
-}
-EXPORT_SYMBOL_GPL(mt6358_set_mtkaif_calibration_phase);
-
/* dl pga gain */
enum {
DL_GAIN_8DB = 0,
diff --git a/sound/soc/codecs/mt6358.h b/sound/soc/codecs/mt6358.h
index a5953315eaa2..b729c3899b7e 100644
--- a/sound/soc/codecs/mt6358.h
+++ b/sound/soc/codecs/mt6358.h
@@ -2307,8 +2307,4 @@ enum {
/* set only during init */
int mt6358_set_mtkaif_protocol(struct snd_soc_component *cmpnt,
int mtkaif_protocol);
-int mt6358_mtkaif_calibration_enable(struct snd_soc_component *cmpnt);
-int mt6358_mtkaif_calibration_disable(struct snd_soc_component *cmpnt);
-int mt6358_set_mtkaif_calibration_phase(struct snd_soc_component *cmpnt,
- int phase_1, int phase_2);
#endif /* __MT6358_H__ */
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
index 7052cc0c97d1..4da608ba514d 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <sound/soc.h>
@@ -37,6 +38,13 @@ static const struct i2c_device_id pcm3168a_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+static const struct acpi_device_id pcm3168a_acpi_match[] = {
+ { "PCM3168A" },
+ { "104C3168" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match);
+
static const struct of_device_id pcm3168a_of_match[] = {
{ .compatible = "ti,pcm3168a", },
{ }
@@ -49,6 +57,7 @@ static struct i2c_driver pcm3168a_i2c_driver = {
.id_table = pcm3168a_i2c_id,
.driver = {
.name = "pcm3168a",
+ .acpi_match_table = pcm3168a_acpi_match,
.of_match_table = pcm3168a_of_match,
.pm = &pcm3168a_pm_ops,
},
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index fac0617ab95b..df6836a652ef 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -493,9 +493,9 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
}
break;
case 24:
- if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) ||
- (format == SND_SOC_DAIFMT_DSP_B)) {
- dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n");
+ if (!provider_mode && ((format == SND_SOC_DAIFMT_DSP_A) ||
+ (format == SND_SOC_DAIFMT_DSP_B))) {
+ dev_err(component->dev, "24-bit slots not supported in consumer mode using DSP\n");
return -EINVAL;
}
break;
@@ -743,7 +743,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst),
"failed to acquire RST gpio\n");
- pcm3168a->scki = devm_clk_get(dev, "scki");
+ pcm3168a->scki = devm_clk_get_optional(dev, "scki");
if (IS_ERR(pcm3168a->scki))
return dev_err_probe(dev, PTR_ERR(pcm3168a->scki),
"failed to acquire clock 'scki'\n");
@@ -755,6 +755,9 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
}
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+ /* Fallback to the default if no clk entry available. */
+ if (!pcm3168a->sysclk)
+ pcm3168a->sysclk = 24576000;
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index 25fc13687bc8..7a38fcf63143 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -16,7 +16,7 @@
#include "rt722-sdca.h"
#include "rt722-sdca-sdw.h"
-static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
+static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0x2f01 ... 0x2f0a:
@@ -28,36 +28,52 @@ static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
0):
- case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER,
- 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
- RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
- case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
- return true;
- default:
- return false;
- }
-}
-
-static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
-{
- switch (reg) {
- case 0x2f01:
- case 0x2f54:
- case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, RT722_SDCA_CTL_SELECTED_MODE,
0):
- case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER,
- 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
- RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D,
+ RT722_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_01) ...
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E,
+ RT722_SDCA_CTL_FU_MUTE, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+ SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L) ...
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
+ RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
- return true;
- default:
- return false;
- }
-}
-
-static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
-{
- switch (reg) {
+ return 1;
case 0x2000000 ... 0x2000024:
case 0x2000029 ... 0x200004a:
case 0x2000051 ... 0x2000052:
@@ -74,6 +90,7 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re
case 0x5600000 ... 0x5600007:
case 0x5700000 ... 0x5700004:
case 0x5800000 ... 0x5800004:
+ case 0x5810000:
case 0x5b00003:
case 0x5c00011:
case 0x5d00006:
@@ -81,6 +98,7 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re
case 0x5f00030:
case 0x6100000 ... 0x6100051:
case 0x6100055 ... 0x6100057:
+ case 0x6100060:
case 0x6100062:
case 0x6100064 ... 0x6100065:
case 0x6100067:
@@ -108,15 +126,32 @@ static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int re
RT722_SDCA_CTL_FU_CH_GAIN, CH_L):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44,
RT722_SDCA_CTL_FU_CH_GAIN, CH_R):
- return true;
+ return 2;
default:
- return false;
+ return 0;
}
}
-static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+static struct regmap_sdw_mbq_cfg rt722_mbq_config = {
+ .mbq_size = rt722_sdca_mbq_size,
+};
+
+static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ return rt722_sdca_mbq_size(dev, reg) > 0;
+}
+
+static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case 0x2f01:
+ case 0x2f54:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
+ 0):
+ case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER,
+ 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
+ RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
case 0x2000000:
case 0x200000d:
case 0x2000019:
@@ -135,7 +170,7 @@ static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int re
static const struct regmap_config rt722_sdca_regmap = {
.reg_bits = 32,
- .val_bits = 8,
+ .val_bits = 16,
.readable_reg = rt722_sdca_readable_register,
.volatile_reg = rt722_sdca_volatile_register,
.max_register = 0x44ffffff,
@@ -146,20 +181,6 @@ static const struct regmap_config rt722_sdca_regmap = {
.use_single_write = true,
};
-static const struct regmap_config rt722_sdca_mbq_regmap = {
- .name = "sdw-mbq",
- .reg_bits = 32,
- .val_bits = 16,
- .readable_reg = rt722_sdca_mbq_readable_register,
- .volatile_reg = rt722_sdca_mbq_volatile_register,
- .max_register = 0x41000312,
- .reg_defaults = rt722_sdca_mbq_defaults,
- .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_defaults),
- .cache_type = REGCACHE_MAPLE,
- .use_single_read = true,
- .use_single_write = true,
-};
-
static int rt722_sdca_update_status(struct sdw_slave *slave,
enum sdw_slave_status status)
{
@@ -203,6 +224,8 @@ static int rt722_sdca_read_prop(struct sdw_slave *slave)
unsigned long addr;
struct sdw_dpn_prop *dpn;
+ sdw_slave_read_lane_mapping(slave);
+
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
@@ -369,18 +392,14 @@ static const struct sdw_slave_ops rt722_sdca_slave_ops = {
static int rt722_sdca_sdw_probe(struct sdw_slave *slave,
const struct sdw_device_id *id)
{
- struct regmap *regmap, *mbq_regmap;
+ struct regmap *regmap;
/* Regmap Initialization */
- mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap);
- if (IS_ERR(mbq_regmap))
- return PTR_ERR(mbq_regmap);
-
- regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap);
+ regmap = devm_regmap_init_sdw_mbq_cfg(slave, &rt722_sdca_regmap, &rt722_mbq_config);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- return rt722_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+ return rt722_sdca_init(&slave->dev, regmap, slave);
}
static int rt722_sdca_sdw_remove(struct sdw_slave *slave)
@@ -418,7 +437,6 @@ static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev)
cancel_delayed_work_sync(&rt722->jack_btn_check_work);
regcache_cache_only(rt722->regmap, true);
- regcache_cache_only(rt722->mbq_regmap, true);
return 0;
}
@@ -488,8 +506,6 @@ regmap_sync:
slave->unattach_request = 0;
regcache_cache_only(rt722->regmap, false);
regcache_sync(rt722->regmap);
- regcache_cache_only(rt722->mbq_regmap, false);
- regcache_sync(rt722->mbq_regmap);
return 0;
}
diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h
index 5b43e86f75d1..80b014456940 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.h
+++ b/sound/soc/codecs/rt722-sdca-sdw.h
@@ -31,50 +31,9 @@ static const struct reg_default rt722_sdca_reg_defaults[] = {
{ 0x2f5b, 0x07 },
{ 0x2f5c, 0x27 },
{ 0x2f5d, 0x07 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
- 0), 0x09 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
- 0), 0x09 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE,
- 0), 0x03 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE,
- 0), 0x03 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
- 0), 0x09 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
- 0x03 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0),
- 0x00 },
- { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
- 0x09 },
- { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R),
- 0x01 },
- { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
- 0x03 },
- { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
-};
-
-static const struct reg_default rt722_sdca_mbq_defaults[] = {
{ 0x200003c, 0xc214 },
{ 0x2000046, 0x8004 },
+ { 0x5810000, 0x702d },
{ 0x6100006, 0x0005 },
{ 0x6100010, 0x2630 },
{ 0x6100011, 0x152f },
@@ -86,27 +45,34 @@ static const struct reg_default rt722_sdca_mbq_defaults[] = {
{ 0x6100028, 0x2a2a },
{ 0x6100029, 0x4141 },
{ 0x6100055, 0x0000 },
- { 0x5810000, 0x702d },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
CH_L), 0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME,
CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
CH_L), 0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME,
CH_R), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE,
+ 0), 0x03 },
{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN,
CH_L), 0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN,
CH_R), 0x0000 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
- CH_01), 0x0000 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
- CH_02), 0x0000 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
- CH_03), 0x0000 },
- { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
- CH_04), 0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01),
0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02),
@@ -115,10 +81,41 @@ static const struct reg_default rt722_sdca_mbq_defaults[] = {
0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04),
0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME,
+ CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX,
+ 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0),
+ 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L),
+ 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R),
+ 0x01 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L),
0x0000 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R),
0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0),
+ 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
};
#endif /* __RT722_SDW_H__ */
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
index e17a142d03b9..f093ce841b3f 100644
--- a/sound/soc/codecs/rt722-sdca.c
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -25,11 +25,13 @@
#include "rt722-sdca.h"
+#define RT722_NID_ADDR(nid, reg) ((nid) << 20 | (reg))
+
int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
unsigned int nid, unsigned int reg, unsigned int value)
{
- struct regmap *regmap = rt722->mbq_regmap;
- unsigned int addr = (nid << 20) | reg;
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
int ret;
ret = regmap_write(regmap, addr, value);
@@ -45,8 +47,8 @@ int rt722_sdca_index_read(struct rt722_sdca_priv *rt722,
unsigned int nid, unsigned int reg, unsigned int *value)
{
int ret;
- struct regmap *regmap = rt722->mbq_regmap;
- unsigned int addr = (nid << 20) | reg;
+ struct regmap *regmap = rt722->regmap;
+ unsigned int addr = RT722_NID_ADDR(nid, reg);
ret = regmap_read(regmap, addr, value);
if (ret < 0)
@@ -361,8 +363,8 @@ static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
strstr(ucontrol->id.name, "FU0F Capture Volume"))
adc_vol_flag = 1;
- regmap_read(rt722->mbq_regmap, mc->reg, &lvalue);
- regmap_read(rt722->mbq_regmap, mc->rreg, &rvalue);
+ regmap_read(rt722->regmap, mc->reg, &lvalue);
+ regmap_read(rt722->regmap, mc->rreg, &rvalue);
/* L Channel */
gain_l_val = ucontrol->value.integer.value[0];
@@ -402,13 +404,13 @@ static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
return 0;
/* Lch*/
- regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val);
+ regmap_write(rt722->regmap, mc->reg, gain_l_val);
/* Rch */
- regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val);
+ regmap_write(rt722->regmap, mc->rreg, gain_r_val);
- regmap_read(rt722->mbq_regmap, mc->reg, &read_l);
- regmap_read(rt722->mbq_regmap, mc->rreg, &read_r);
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->regmap, mc->rreg, &read_r);
if (read_r == gain_r_val && read_l == gain_l_val)
return changed;
@@ -431,8 +433,8 @@ static int rt722_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
strstr(ucontrol->id.name, "FU0F Capture Volume"))
adc_vol_flag = 1;
- regmap_read(rt722->mbq_regmap, mc->reg, &read_l);
- regmap_read(rt722->mbq_regmap, mc->rreg, &read_r);
+ regmap_read(rt722->regmap, mc->reg, &read_l);
+ regmap_read(rt722->regmap, mc->rreg, &read_r);
if (mc->shift == 8) /* boost gain */
ctl_l = read_l / tendB;
@@ -604,7 +606,7 @@ static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
/* check all channels */
for (i = 0; i < p->count; i++) {
- regmap_read(rt722->mbq_regmap, p->reg_base + i, &regvalue);
+ regmap_read(rt722->regmap, p->reg_base + i, &regvalue);
if (!adc_vol_flag) /* boost gain */
ctl = regvalue / boost_step;
@@ -637,7 +639,7 @@ static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
/* check all channels */
for (i = 0; i < p->count; i++) {
- regmap_read(rt722->mbq_regmap, p->reg_base + i, &regvalue[i]);
+ regmap_read(rt722->regmap, p->reg_base + i, &regvalue[i]);
gain_val[i] = ucontrol->value.integer.value[i];
if (gain_val[i] > p->max)
@@ -658,7 +660,7 @@ static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
return 0;
for (i = 0; i < p->count; i++) {
- err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]);
+ err = regmap_write(rt722->regmap, p->reg_base + i, gain_val[i]);
if (err < 0)
dev_err(&rt722->slave->dev, "%s: %#08x can't be set\n",
__func__, p->reg_base + i);
@@ -739,77 +741,6 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = {
4, 3, boost_vol_tlv),
};
-static int rt722_sdca_adc_mux_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_kcontrol_component(kcontrol);
- struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
- unsigned int val = 0, mask_sft;
-
- if (strstr(ucontrol->id.name, "ADC 22 Mux"))
- mask_sft = 12;
- else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
- mask_sft = 4;
- else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
- mask_sft = 0;
- else
- return -EINVAL;
-
- rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL,
- RT722_HDA_LEGACY_MUX_CTL0, &val);
-
- ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
-
- return 0;
-}
-
-static int rt722_sdca_adc_mux_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_kcontrol_component(kcontrol);
- struct snd_soc_dapm_context *dapm =
- snd_soc_dapm_kcontrol_dapm(kcontrol);
- struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int *item = ucontrol->value.enumerated.item;
- unsigned int val, val2 = 0, change, mask_sft;
-
- if (item[0] >= e->items)
- return -EINVAL;
-
- if (strstr(ucontrol->id.name, "ADC 22 Mux"))
- mask_sft = 12;
- else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
- mask_sft = 4;
- else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
- mask_sft = 0;
- else
- return -EINVAL;
-
- val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
-
- rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL,
- RT722_HDA_LEGACY_MUX_CTL0, &val2);
- val2 = (0x7 << mask_sft) & val2;
-
- if (val == val2)
- change = 0;
- else
- change = 1;
-
- if (change)
- rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL,
- RT722_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft,
- val << mask_sft);
-
- snd_soc_dapm_mux_update_power(dapm, kcontrol,
- item[0], e, NULL);
-
- return change;
-}
-
static const char * const adc22_mux_text[] = {
"MIC2",
"LINE1",
@@ -821,26 +752,26 @@ static const char * const adc07_10_mux_text[] = {
"DMIC2",
};
-static SOC_ENUM_SINGLE_DECL(
- rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text);
+static SOC_ENUM_SINGLE_DECL(rt722_adc22_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 12, adc22_mux_text);
-static SOC_ENUM_SINGLE_DECL(
- rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+static SOC_ENUM_SINGLE_DECL(rt722_adc24_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 4, adc07_10_mux_text);
-static SOC_ENUM_SINGLE_DECL(
- rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+static SOC_ENUM_SINGLE_DECL(rt722_adc25_enum,
+ RT722_NID_ADDR(RT722_VENDOR_HDA_CTL, RT722_HDA_LEGACY_MUX_CTL0),
+ 0, adc07_10_mux_text);
static const struct snd_kcontrol_new rt722_sdca_adc22_mux =
- SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum,
- rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put);
+ SOC_DAPM_ENUM("ADC 22 Mux", rt722_adc22_enum);
static const struct snd_kcontrol_new rt722_sdca_adc24_mux =
- SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum,
- rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put);
+ SOC_DAPM_ENUM("ADC 24 Mux", rt722_adc24_enum);
static const struct snd_kcontrol_new rt722_sdca_adc25_mux =
- SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum,
- rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put);
+ SOC_DAPM_ENUM("ADC 25 Mux", rt722_adc25_enum);
static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
@@ -1335,8 +1266,7 @@ static struct snd_soc_dai_driver rt722_sdca_dai[] = {
}
};
-int rt722_sdca_init(struct device *dev, struct regmap *regmap,
- struct regmap *mbq_regmap, struct sdw_slave *slave)
+int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave)
{
struct rt722_sdca_priv *rt722;
@@ -1347,7 +1277,6 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, rt722);
rt722->slave = slave;
rt722->regmap = regmap;
- rt722->mbq_regmap = mbq_regmap;
mutex_init(&rt722->calibrate_mutex);
mutex_init(&rt722->disable_irq_lock);
@@ -1521,8 +1450,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
if (rt722->first_hw_init) {
regcache_cache_only(rt722->regmap, false);
regcache_cache_bypass(rt722->regmap, true);
- regcache_cache_only(rt722->mbq_regmap, false);
- regcache_cache_bypass(rt722->mbq_regmap, true);
} else {
/*
* PM runtime is only enabled when a Slave reports as Attached
@@ -1550,8 +1477,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
if (rt722->first_hw_init) {
regcache_cache_bypass(rt722->regmap, false);
regcache_mark_dirty(rt722->regmap);
- regcache_cache_bypass(rt722->mbq_regmap, false);
- regcache_mark_dirty(rt722->mbq_regmap);
} else
rt722->first_hw_init = true;
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
index 2464361a7958..04c3b4232ef3 100644
--- a/sound/soc/codecs/rt722-sdca.h
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -17,7 +17,6 @@
struct rt722_sdca_priv {
struct regmap *regmap;
- struct regmap *mbq_regmap;
struct snd_soc_component *component;
struct sdw_slave *slave;
struct sdw_bus_params params;
@@ -229,8 +228,7 @@ enum rt722_sdca_jd_src {
};
int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave);
-int rt722_sdca_init(struct device *dev, struct regmap *regmap,
- struct regmap *mbq_regmap, struct sdw_slave *slave);
+int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave);
int rt722_sdca_index_write(struct rt722_sdca_priv *rt722,
unsigned int nid, unsigned int reg, unsigned int value);
int rt722_sdca_index_read(struct rt722_sdca_priv *rt722,
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
index 850e5de9271e..da2f3cb1cd13 100644
--- a/sound/soc/codecs/tscs454.c
+++ b/sound/soc/codecs/tscs454.c
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/string.h>
+#include <linux/string_choices.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mutex.h>
@@ -737,9 +738,7 @@ static int pll_power_event(struct snd_soc_dapm_widget *w,
ret = snd_soc_component_update_bits(component, R_PLLCTL, msk, val);
if (ret < 0) {
dev_err(component->dev, "Failed to %s PLL %d (%d)\n",
- enable ? "enable" : "disable",
- pll1 ? 1 : 2,
- ret);
+ str_enable_disable(enable), pll1 ? 1 : 2, ret);
return ret;
}
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 910852eb9698..dd0cda394bf1 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -23,6 +23,8 @@
#include "wcd-clsh-v2.h"
#include "wcd-mbhc-v2.h"
+#include <dt-bindings/sound/qcom,wcd934x.h>
+
#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
@@ -307,6 +309,7 @@
{"SLIM TX" #id, NULL, "CDC_IF TX" #id " MUX"}
#define WCD934X_MAX_MICBIAS MIC_BIAS_4
+#define NUM_CODEC_DAIS 9
enum {
SIDO_SOURCE_INTERNAL,
@@ -435,19 +438,6 @@ enum {
};
enum {
- AIF1_PB = 0,
- AIF1_CAP,
- AIF2_PB,
- AIF2_CAP,
- AIF3_PB,
- AIF3_CAP,
- AIF4_PB,
- AIF4_VIFEED,
- AIF4_MAD_TX,
- NUM_CODEC_DAIS,
-};
-
-enum {
INTn_1_INP_SEL_ZERO = 0,
INTn_1_INP_SEL_DEC0,
INTn_1_INP_SEL_DEC1,
diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c
index 0c33f7f3dc25..4891fa0c963f 100644
--- a/sound/soc/codecs/wcd937x-sdw.c
+++ b/sound/soc/codecs/wcd937x-sdw.c
@@ -19,7 +19,7 @@
#include <sound/soc.h>
#include "wcd937x.h"
-static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
+static struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD937X_HPH_L, WCD937X_HPH_PORT, BIT(0)),
WCD_SDW_CH(WCD937X_HPH_R, WCD937X_HPH_PORT, BIT(1)),
WCD_SDW_CH(WCD937X_CLSH, WCD937X_CLSH_PORT, BIT(0)),
@@ -30,7 +30,7 @@ static const struct wcd937x_sdw_ch_info wcd937x_sdw_rx_ch_info[] = {
WCD_SDW_CH(WCD937X_DSD_R, WCD937X_DSD_PORT, BIT(1)),
};
-static const struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = {
+static struct wcd937x_sdw_ch_info wcd937x_sdw_tx_ch_info[] = {
WCD_SDW_CH(WCD937X_ADC1, WCD937X_ADC_1_PORT, BIT(0)),
WCD_SDW_CH(WCD937X_ADC2, WCD937X_ADC_2_3_PORT, BIT(0)),
WCD_SDW_CH(WCD937X_ADC3, WCD937X_ADC_2_3_PORT, BIT(0)),
@@ -1019,14 +1019,16 @@ static int wcd9370_probe(struct sdw_slave *pdev,
{
struct device *dev = &pdev->dev;
struct wcd937x_sdw_priv *wcd;
- int ret;
+ u8 master_ch_mask[WCD937X_MAX_SWR_CH_IDS];
+ int master_ch_mask_size = 0;
+ int ret, i;
wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
if (!wcd)
return -ENOMEM;
/* Port map index starts at 0, however the data port for this codec start at index 1 */
- if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) {
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
wcd->is_tx = true;
ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
&pdev->m_port_map[1],
@@ -1048,10 +1050,36 @@ static int wcd9370_probe(struct sdw_slave *pdev,
SDW_SCP_INT1_PARITY;
pdev->prop.lane_control_support = true;
pdev->prop.simple_clk_stop_capable = true;
+
+ memset(master_ch_mask, 0, WCD937X_MAX_SWR_CH_IDS);
+
if (wcd->is_tx) {
- pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS - 1, 0);
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,tx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,tx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ } else {
+ master_ch_mask_size = of_property_count_u8_elems(dev->of_node,
+ "qcom,rx-channel-mapping");
+
+ if (master_ch_mask_size)
+ ret = of_property_read_u8_array(dev->of_node, "qcom,rx-channel-mapping",
+ master_ch_mask, master_ch_mask_size);
+ }
+
+ if (ret < 0)
+ dev_info(dev, "Static channel mapping not specified using device channel maps\n");
+
+ if (wcd->is_tx) {
+ pdev->prop.source_ports = GENMASK(WCD937X_MAX_TX_SWR_PORTS, 0);
pdev->prop.src_dpn_prop = wcd937x_dpn_prop;
wcd->ch_info = &wcd937x_sdw_tx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]);
+
pdev->prop.wake_capable = true;
wcd->regmap = devm_regmap_init_sdw(pdev, &wcd937x_regmap_config);
@@ -1065,6 +1093,9 @@ static int wcd9370_probe(struct sdw_slave *pdev,
pdev->prop.sink_ports = GENMASK(WCD937X_MAX_SWR_PORTS - 1, 0);
pdev->prop.sink_dpn_prop = wcd937x_dpn_prop;
wcd->ch_info = &wcd937x_sdw_rx_ch_info[0];
+
+ for (i = 0; i < master_ch_mask_size; i++)
+ wcd->ch_info[i].master_ch_mask = WCD937X_SWRM_CH_MASK(master_ch_mask[i]);
}
diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c
index c9d5e67bf66e..e8d3fddbc7b1 100644
--- a/sound/soc/codecs/wcd937x.c
+++ b/sound/soc/codecs/wcd937x.c
@@ -1197,13 +1197,21 @@ static int wcd937x_connect_port(struct wcd937x_sdw_priv *wcd, u8 port_idx, u8 ch
const struct wcd937x_sdw_ch_info *ch_info = &wcd->ch_info[ch_id];
u8 port_num = ch_info->port_num;
u8 ch_mask = ch_info->ch_mask;
+ u8 mstr_port_num, mstr_ch_mask;
+ struct sdw_slave *sdev = wcd->sdev;
port_config->num = port_num;
- if (enable)
+ mstr_port_num = sdev->m_port_map[port_num];
+ mstr_ch_mask = ch_info->master_ch_mask;
+
+ if (enable) {
port_config->ch_mask |= ch_mask;
- else
+ wcd->master_channel_map[mstr_port_num] |= mstr_ch_mask;
+ } else {
port_config->ch_mask &= ~ch_mask;
+ wcd->master_channel_map[mstr_port_num] &= ~mstr_ch_mask;
+ }
return 0;
}
@@ -2689,10 +2697,51 @@ static int wcd937x_codec_set_sdw_stream(struct snd_soc_dai *dai,
return 0;
}
+static int wcd937x_get_channel_map(const struct snd_soc_dai *dai,
+ unsigned int *tx_num, unsigned int *tx_slot,
+ unsigned int *rx_num, unsigned int *rx_slot)
+{
+ struct wcd937x_priv *wcd937x = dev_get_drvdata(dai->dev);
+ struct wcd937x_sdw_priv *wcd = wcd937x->sdw_priv[dai->id];
+ int i;
+
+ switch (dai->id) {
+ case AIF1_PB:
+ if (!rx_slot || !rx_num) {
+ dev_err(dai->dev, "Invalid rx_slot %p or rx_num %p\n",
+ rx_slot, rx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ rx_slot[i] = wcd->master_channel_map[i];
+
+ *rx_num = i;
+ break;
+ case AIF1_CAP:
+ if (!tx_slot || !tx_num) {
+ dev_err(dai->dev, "Invalid tx_slot %p or tx_num %p\n",
+ tx_slot, tx_num);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < SDW_MAX_PORTS; i++)
+ tx_slot[i] = wcd->master_channel_map[i];
+
+ *tx_num = i;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops wcd937x_sdw_dai_ops = {
.hw_params = wcd937x_codec_hw_params,
.hw_free = wcd937x_codec_free,
.set_stream = wcd937x_codec_set_sdw_stream,
+ .get_channel_map = wcd937x_get_channel_map,
};
static struct snd_soc_dai_driver wcd937x_dais[] = {
diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h
index 4afa48dcaf74..4ef57c496c37 100644
--- a/sound/soc/codecs/wcd937x.h
+++ b/sound/soc/codecs/wcd937x.h
@@ -489,6 +489,7 @@
#define WCD937X_MAX_MICBIAS 3
#define WCD937X_MAX_BULK_SUPPLY 4
#define WCD937X_MAX_SWR_CH_IDS 15
+#define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
enum wcd937x_tx_sdw_ports {
WCD937X_ADC_1_PORT = 1,
@@ -510,12 +511,14 @@ enum wcd937x_rx_sdw_ports {
struct wcd937x_sdw_ch_info {
int port_num;
unsigned int ch_mask;
+ unsigned int master_ch_mask;
};
#define WCD_SDW_CH(id, pn, cmask) \
[id] = { \
.port_num = pn, \
.ch_mask = cmask, \
+ .master_ch_mask = cmask, \
}
struct wcd937x_priv;
@@ -524,9 +527,11 @@ struct wcd937x_sdw_priv {
struct sdw_stream_config sconfig;
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WCD937X_MAX_SWR_PORTS];
- const struct wcd937x_sdw_ch_info *ch_info;
+ struct wcd937x_sdw_ch_info *ch_info;
bool port_enable[WCD937X_MAX_SWR_CH_IDS];
+ unsigned int master_channel_map[SDW_MAX_PORTS];
int active_ports;
+ int num_ports;
bool is_tx;
struct wcd937x_priv *wcd937x;
struct irq_domain *slave_irq;
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index 7da8a10bd0a9..4e2ae542cee3 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -1229,7 +1229,7 @@ static int wcd9380_probe(struct sdw_slave *pdev,
* Port map index starts with 0, however the data port for this codec
* are from index 1
*/
- if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) {
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
wcd->is_tx = true;
ret = of_property_read_u32_array(dev->of_node, "qcom,tx-port-mapping",
&pdev->m_port_map[1],
diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c
index fca95777a75a..36868fad3e8b 100644
--- a/sound/soc/codecs/wcd939x-sdw.c
+++ b/sound/soc/codecs/wcd939x-sdw.c
@@ -1429,7 +1429,7 @@ static int wcd9390_probe(struct sdw_slave *pdev, const struct sdw_device_id *id)
* Port map index starts with 0, however the data port for this codec
* are from index 1
*/
- if (of_property_read_bool(dev->of_node, "qcom,tx-port-mapping")) {
+ if (of_property_present(dev->of_node, "qcom,tx-port-mapping")) {
wcd->is_tx = true;
ret = of_property_read_u32_array(dev->of_node,
"qcom,tx-port-mapping",
diff --git a/sound/soc/fsl/fsl_micfil.c b/sound/soc/fsl/fsl_micfil.c
index 1075598a6647..73d8910a6188 100644
--- a/sound/soc/fsl/fsl_micfil.c
+++ b/sound/soc/fsl/fsl_micfil.c
@@ -19,6 +19,7 @@
#include <linux/dma/imx-dma.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/core.h>
@@ -78,6 +79,7 @@ struct fsl_micfil {
struct fsl_micfil_verid verid;
struct fsl_micfil_param param;
bool mclk_flag; /* mclk enable flag */
+ bool dec_bypass;
};
struct fsl_micfil_soc_data {
@@ -129,7 +131,7 @@ static struct fsl_micfil_soc_data fsl_micfil_imx943 = {
.fifos = 8,
.fifo_depth = 32,
.dataline = 0xf,
- .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_DSD_U32_BE,
.use_edma = true,
.use_verid = true,
.volume_sx = false,
@@ -183,6 +185,8 @@ static int micfil_set_quality(struct fsl_micfil *micfil)
case QUALITY_VLOW2:
qsel = MICFIL_QSEL_VLOW2_QUALITY;
break;
+ default:
+ return -EINVAL;
}
return regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
@@ -722,14 +726,14 @@ static int fsl_micfil_trigger(struct snd_pcm_substream *substream, int cmd,
if (ret)
return ret;
- if (micfil->vad_enabled)
+ if (micfil->vad_enabled && !micfil->dec_bypass)
fsl_micfil_hwvad_enable(micfil);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (micfil->vad_enabled)
+ if (micfil->vad_enabled && !micfil->dec_bypass)
fsl_micfil_hwvad_disable(micfil);
/* Disable the module */
@@ -776,8 +780,9 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
{
struct fsl_micfil *micfil = snd_soc_dai_get_drvdata(dai);
unsigned int channels = params_channels(params);
+ snd_pcm_format_t format = params_format(params);
unsigned int rate = params_rate(params);
- int clk_div = 8;
+ int clk_div = 8, mclk_rate, div_multiply_k;
int osr = MICFIL_OSR_DEFAULT;
int ret;
@@ -799,7 +804,39 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
micfil->mclk_flag = true;
- ret = clk_set_rate(micfil->mclk, rate * clk_div * osr * 8);
+ /* floor(K * CLKDIV) */
+ switch (micfil->quality) {
+ case QUALITY_HIGH:
+ div_multiply_k = clk_div >> 1;
+ break;
+ case QUALITY_LOW:
+ case QUALITY_VLOW1:
+ div_multiply_k = clk_div << 1;
+ break;
+ case QUALITY_VLOW2:
+ div_multiply_k = clk_div << 2;
+ break;
+ case QUALITY_MEDIUM:
+ case QUALITY_VLOW0:
+ default:
+ div_multiply_k = clk_div;
+ break;
+ }
+
+ if (format == SNDRV_PCM_FORMAT_DSD_U32_BE) {
+ micfil->dec_bypass = true;
+ /*
+ * According to equation 29 in RM:
+ * MCLK_CLK_ROOT = PDM CLK rate * 2 * floor(K * CLKDIV)
+ * PDM CLK rate = rate * physical bit width (32)
+ */
+ mclk_rate = rate * div_multiply_k * 32 * 2;
+ } else {
+ micfil->dec_bypass = false;
+ mclk_rate = rate * clk_div * osr * 8;
+ }
+
+ ret = clk_set_rate(micfil->mclk, mclk_rate);
if (ret)
return ret;
@@ -807,6 +844,10 @@ static int fsl_micfil_hw_params(struct snd_pcm_substream *substream,
if (ret)
return ret;
+ regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
+ MICFIL_CTRL2_DEC_BYPASS,
+ micfil->dec_bypass ? MICFIL_CTRL2_DEC_BYPASS : 0);
+
ret = regmap_update_bits(micfil->regmap, REG_MICFIL_CTRL2,
MICFIL_CTRL2_CLKDIV | MICFIL_CTRL2_CICOSR,
FIELD_PREP(MICFIL_CTRL2_CLKDIV, clk_div) |
diff --git a/sound/soc/fsl/fsl_micfil.h b/sound/soc/fsl/fsl_micfil.h
index aa3661ea4ffc..fdfe4e7125bc 100644
--- a/sound/soc/fsl/fsl_micfil.h
+++ b/sound/soc/fsl/fsl_micfil.h
@@ -53,6 +53,7 @@
#define MICFIL_CTRL1_CHEN(ch) BIT(ch)
/* MICFIL Control Register 2 -- REG_MICFILL_CTRL2 0x04 */
+#define MICFIL_CTRL2_DEC_BYPASS BIT(31)
#define MICFIL_CTRL2_QSEL_SHIFT 25
#define MICFIL_CTRL2_QSEL GENMASK(27, 25)
#define MICFIL_QSEL_MEDIUM_QUALITY 0
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 7c422535b01a..a8a3bad3df00 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -20,6 +20,13 @@
#define DPCM_SELECTABLE 1
+#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret)
+static inline int _graph_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
#define ep_to_port(ep) of_get_parent(ep)
static struct device_node *port_to_ports(struct device_node *port)
{
@@ -111,19 +118,17 @@ static int graph_parse_node(struct simple_util_priv *priv,
dai = simple_props_to_dai_codec(dai_props, 0);
}
- ret = graph_util_parse_dai(dev, ep, dlc, cpu);
+ ret = graph_util_parse_dai(priv, ep, dlc, cpu);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_tdm(ep, dai);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_clk(dev, ep, dai, dlc);
- if (ret < 0)
- return ret;
-
- return 0;
+end:
+ return graph_ret(priv, ret);
}
static int graph_link_init(struct simple_util_priv *priv,
@@ -148,7 +153,7 @@ static int graph_link_init(struct simple_util_priv *priv,
ret = simple_util_parse_daifmt(dev, ep_cpu, ep_codec,
NULL, &dai_link->dai_fmt);
if (ret < 0)
- return ret;
+ goto end;
graph_util_parse_link_direction(top, &playback_only, &capture_only);
graph_util_parse_link_direction(port_cpu, &playback_only, &capture_only);
@@ -183,7 +188,9 @@ static int graph_link_init(struct simple_util_priv *priv,
if (priv->ops)
dai_link->ops = priv->ops;
- return simple_util_set_dailink_name(dev, dai_link, name);
+ ret = simple_util_set_dailink_name(priv, dai_link, name);
+end:
+ return graph_ret(priv, ret);
}
static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
@@ -215,7 +222,7 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
if (ret)
- return ret;
+ goto end;
snprintf(dai_name, sizeof(dai_name),
"fe.%pOFP.%s", cpus->of_node, cpus->dai_name);
@@ -248,7 +255,7 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
ret = graph_parse_node(priv, codec_ep, li, NULL);
if (ret < 0)
- return ret;
+ goto end;
snprintf(dai_name, sizeof(dai_name),
"be.%pOFP.%s", codecs->of_node, codecs->dai_name);
@@ -267,8 +274,8 @@ static int graph_dai_link_of_dpcm(struct simple_util_priv *priv,
ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
li->link++;
-
- return ret;
+end:
+ return graph_ret(priv, ret);
}
static int graph_dai_link_of(struct simple_util_priv *priv,
@@ -288,11 +295,11 @@ static int graph_dai_link_of(struct simple_util_priv *priv,
ret = graph_parse_node(priv, cpu_ep, li, &is_single_links);
if (ret < 0)
- return ret;
+ goto end;
ret = graph_parse_node(priv, codec_ep, li, NULL);
if (ret < 0)
- return ret;
+ goto end;
snprintf(dai_name, sizeof(dai_name),
"%s-%s", cpus->dai_name, codecs->dai_name);
@@ -302,11 +309,11 @@ static int graph_dai_link_of(struct simple_util_priv *priv,
ret = graph_link_init(priv, cpu_ep, codec_ep, li, dai_name);
if (ret < 0)
- return ret;
+ goto end;
li->link++;
-
- return 0;
+end:
+ return graph_ret(priv, ret);
}
static inline bool parse_as_dpcm_link(struct simple_util_priv *priv,
@@ -383,13 +390,13 @@ static int __graph_for_each_link(struct simple_util_priv *priv,
}
if (ret < 0)
- return ret;
+ goto end;
codec_port_old = codec_port;
}
}
-
- return 0;
+end:
+ return graph_ret(priv, ret);
}
static int graph_for_each_link(struct simple_util_priv *priv,
@@ -422,7 +429,7 @@ static int graph_for_each_link(struct simple_util_priv *priv,
break;
}
- return ret;
+ return graph_ret(priv, ret);
}
static int graph_count_noml(struct simple_util_priv *priv,
@@ -431,11 +438,10 @@ static int graph_count_noml(struct simple_util_priv *priv,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
- if (li->link >= SNDRV_MAX_LINKS) {
- dev_err(dev, "too many links\n");
- return -EINVAL;
- }
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
/*
* DON'T REMOVE platforms
@@ -450,8 +456,9 @@ static int graph_count_noml(struct simple_util_priv *priv,
li->link += 1; /* 1xCPU-Codec */
dev_dbg(dev, "Count As Normal\n");
-
- return 0;
+ ret = 0;
+end:
+ return graph_ret(priv, ret);
}
static int graph_count_dpcm(struct simple_util_priv *priv,
@@ -460,11 +467,10 @@ static int graph_count_dpcm(struct simple_util_priv *priv,
struct link_info *li)
{
struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
- if (li->link >= SNDRV_MAX_LINKS) {
- dev_err(dev, "too many links\n");
- return -EINVAL;
- }
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
if (li->cpu) {
/*
@@ -483,8 +489,9 @@ static int graph_count_dpcm(struct simple_util_priv *priv,
}
dev_dbg(dev, "Count As DPCM\n");
-
- return 0;
+ ret = 0;
+end:
+ return graph_ret(priv, ret);
}
static int graph_get_dais_count(struct simple_util_priv *priv,
@@ -544,40 +551,41 @@ static int graph_get_dais_count(struct simple_util_priv *priv,
int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
{
struct snd_soc_card *card = simple_priv_to_card(priv);
- int ret;
+ int ret = -ENOMEM;
struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
if (!li)
- return -ENOMEM;
+ goto end;
card->owner = THIS_MODULE;
card->dev = dev;
ret = graph_get_dais_count(priv, li);
if (ret < 0)
- return ret;
+ goto end;
+ ret = -EINVAL;
if (!li->link)
- return -EINVAL;
+ goto end;
ret = simple_util_init_priv(priv, li);
if (ret < 0)
- return ret;
+ goto end;
priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
if (IS_ERR(priv->pa_gpio)) {
ret = PTR_ERR(priv->pa_gpio);
dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
- return ret;
+ goto end;
}
ret = simple_util_parse_widgets(card, NULL);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_routing(card, NULL);
if (ret < 0)
- return ret;
+ goto end;
memset(li, 0, sizeof(*li));
ret = graph_for_each_link(priv, li,
@@ -586,7 +594,7 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
if (ret < 0)
goto err;
- ret = simple_util_parse_card_name(card, NULL);
+ ret = simple_util_parse_card_name(priv, NULL);
if (ret < 0)
goto err;
@@ -599,10 +607,9 @@ int audio_graph_parse_of(struct simple_util_priv *priv, struct device *dev)
goto err;
return 0;
-
err:
simple_util_clean_reference(card);
-
+end:
return dev_err_probe(dev, ret, "parse error\n");
}
EXPORT_SYMBOL_GPL(audio_graph_parse_of);
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index ee94b256b770..5dcc78c551a2 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -234,6 +234,13 @@ enum graph_type {
#define GRAPH_NODENAME_DPCM "dpcm"
#define GRAPH_NODENAME_C2C "codec2codec"
+#define graph_ret(priv, ret) _graph_ret(priv, __func__, ret)
+static inline int _graph_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
#define ep_to_port(ep) of_get_parent(ep)
static struct device_node *port_to_ports(struct device_node *port)
{
@@ -409,21 +416,21 @@ static int __graph_parse_node(struct simple_util_priv *priv,
dai = simple_props_to_dai_codec(dai_props, idx);
}
- ret = graph_util_parse_dai(dev, ep, dlc, &is_single_links);
+ ret = graph_util_parse_dai(priv, ep, dlc, &is_single_links);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_tdm(ep, dai);
if (ret < 0)
- return ret;
+ goto end;
- ret = simple_util_parse_tdm_width_map(dev, ep, dai);
+ ret = simple_util_parse_tdm_width_map(priv, ep, dai);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_clk(dev, ep, dai, dlc);
if (ret < 0)
- return ret;
+ goto end;
/*
* set DAI Name
@@ -443,22 +450,22 @@ static int __graph_parse_node(struct simple_util_priv *priv,
case GRAPH_NORMAL:
/* run is_cpu only. see audio_graph2_link_normal() */
if (is_cpu)
- simple_util_set_dailink_name(dev, dai_link, "%s%s-%s%s",
+ simple_util_set_dailink_name(priv, dai_link, "%s%s-%s%s",
cpus->dai_name, cpu_multi,
codecs->dai_name, codec_multi);
break;
case GRAPH_DPCM:
if (is_cpu)
- simple_util_set_dailink_name(dev, dai_link, "fe.%pOFP.%s%s",
+ simple_util_set_dailink_name(priv, dai_link, "fe.%pOFP.%s%s",
cpus->of_node, cpus->dai_name, cpu_multi);
else
- simple_util_set_dailink_name(dev, dai_link, "be.%pOFP.%s%s",
+ simple_util_set_dailink_name(priv, dai_link, "be.%pOFP.%s%s",
codecs->of_node, codecs->dai_name, codec_multi);
break;
case GRAPH_C2C:
/* run is_cpu only. see audio_graph2_link_c2c() */
if (is_cpu)
- simple_util_set_dailink_name(dev, dai_link, "c2c.%s%s-%s%s",
+ simple_util_set_dailink_name(priv, dai_link, "c2c.%s%s-%s%s",
cpus->dai_name, cpu_multi,
codecs->dai_name, codec_multi);
break;
@@ -488,11 +495,12 @@ static int __graph_parse_node(struct simple_util_priv *priv,
simple_util_canonicalize_cpu(cpus, is_single_links);
simple_util_canonicalize_platform(platforms, cpus);
}
-
- return 0;
+end:
+ return graph_ret(priv, ret);
}
-static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,
+static int graph_parse_node_multi_nm(struct simple_util_priv *priv,
+ struct snd_soc_dai_link *dai_link,
int *nm_idx, int cpu_idx,
struct device_node *mcpu_port)
{
@@ -534,10 +542,10 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,
struct device_node *mcodec_port_top __free(device_node) = ep_to_port(mcodec_ep_top);
struct device_node *mcodec_ports __free(device_node) = port_to_ports(mcodec_port_top);
int nm_max = max(dai_link->num_cpus, dai_link->num_codecs);
- int ret = 0;
+ int ret = -EINVAL;
if (cpu_idx > dai_link->num_cpus)
- return -EINVAL;
+ goto end;
for_each_of_graph_port_endpoint(mcpu_port, mcpu_ep_n) {
int codec_idx = 0;
@@ -578,8 +586,8 @@ static int graph_parse_node_multi_nm(struct snd_soc_dai_link *dai_link,
if (ret < 0)
break;
}
-
- return ret;
+end:
+ return graph_ret(priv, ret);
}
static int graph_parse_node_multi(struct simple_util_priv *priv,
@@ -633,7 +641,7 @@ static int graph_parse_node_multi(struct simple_util_priv *priv,
/* CPU:Codec = N:M */
if (is_cpu && dai_link->ch_maps) {
- ret = graph_parse_node_multi_nm(dai_link, &nm_idx, idx, port);
+ ret = graph_parse_node_multi_nm(priv, dai_link, &nm_idx, idx, port);
if (ret < 0)
goto multi_err;
}
@@ -643,7 +651,7 @@ static int graph_parse_node_multi(struct simple_util_priv *priv,
ret = -EINVAL;
multi_err:
- return ret;
+ return graph_ret(priv, ret);
}
static int graph_parse_node_single(struct simple_util_priv *priv,
@@ -651,7 +659,7 @@ static int graph_parse_node_single(struct simple_util_priv *priv,
struct device_node *ep,
struct link_info *li, int is_cpu)
{
- return __graph_parse_node(priv, gtype, ep, li, is_cpu, 0);
+ return graph_ret(priv, __graph_parse_node(priv, gtype, ep, li, is_cpu, 0));
}
static int graph_parse_node(struct simple_util_priv *priv,
@@ -660,11 +668,14 @@ static int graph_parse_node(struct simple_util_priv *priv,
struct link_info *li, int is_cpu)
{
struct device_node *port __free(device_node) = ep_to_port(ep);
+ int ret;
if (graph_lnk_is_multi(port))
- return graph_parse_node_multi(priv, gtype, port, li, is_cpu);
+ ret = graph_parse_node_multi(priv, gtype, port, li, is_cpu);
else
- return graph_parse_node_single(priv, gtype, ep, li, is_cpu);
+ ret = graph_parse_node_single(priv, gtype, ep, li, is_cpu);
+
+ return graph_ret(priv, ret);
}
static void graph_parse_daifmt(struct device_node *node, unsigned int *daifmt)
@@ -842,18 +853,19 @@ int audio_graph2_link_normal(struct simple_util_priv *priv,
*/
ret = graph_parse_node(priv, GRAPH_NORMAL, codec_ep, li, 0);
if (ret < 0)
- return ret;
+ goto end;
/*
* call CPU, and set DAI Name
*/
ret = graph_parse_node(priv, GRAPH_NORMAL, cpu_ep, li, 1);
if (ret < 0)
- return ret;
+ goto end;
graph_link_init(priv, lnk, cpu_ep, codec_ep, li, 1);
- return ret;
+end:
+ return graph_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(audio_graph2_link_normal);
@@ -946,7 +958,7 @@ int audio_graph2_link_dpcm(struct simple_util_priv *priv,
graph_link_init(priv, lnk, cpu_ep, codec_ep, li, is_cpu);
- return ret;
+ return graph_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(audio_graph2_link_dpcm);
@@ -992,8 +1004,13 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv,
struct snd_soc_pcm_stream *c2c_conf;
c2c_conf = devm_kzalloc(dev, sizeof(*c2c_conf), GFP_KERNEL);
- if (!c2c_conf)
- return ret;
+ if (!c2c_conf) {
+ /*
+ * Clang doesn't allow to use "goto end" before calling __free(),
+ * because it bypasses the initialization. Use graph_ret() directly.
+ */
+ return graph_ret(priv, -ENOMEM);
+ }
c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */
c2c_conf->rates = SNDRV_PCM_RATE_8000_384000;
@@ -1019,18 +1036,18 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv,
*/
ret = graph_parse_node(priv, GRAPH_C2C, codec1_ep, li, 0);
if (ret < 0)
- return ret;
+ goto end;
/*
* call CPU, and set DAI Name
*/
ret = graph_parse_node(priv, GRAPH_C2C, codec0_ep, li, 1);
if (ret < 0)
- return ret;
+ goto end;
graph_link_init(priv, lnk, codec0_ep, codec1_ep, li, 1);
-
- return ret;
+end:
+ return graph_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(audio_graph2_link_c2c);
@@ -1078,7 +1095,7 @@ static int graph_link(struct simple_util_priv *priv,
li->link++;
err:
- return ret;
+ return graph_ret(priv, ret);
}
static int graph_counter(struct device_node *lnk)
@@ -1249,7 +1266,7 @@ static int graph_count(struct simple_util_priv *priv,
li->link++;
err:
- return ret;
+ return graph_ret(priv, ret);
}
static int graph_for_each_link(struct simple_util_priv *priv,
@@ -1266,7 +1283,7 @@ static int graph_for_each_link(struct simple_util_priv *priv,
struct device_node *node = dev->of_node;
struct device_node *lnk;
enum graph_type gtype;
- int rc, ret;
+ int rc, ret = 0;
/* loop for all listed CPU port */
of_for_each_phandle(&it, rc, node, "links", NULL, 0) {
@@ -1276,10 +1293,10 @@ static int graph_for_each_link(struct simple_util_priv *priv,
ret = func(priv, hooks, gtype, lnk, li);
if (ret < 0)
- return ret;
+ break;
}
- return 0;
+ return graph_ret(priv, ret);
}
int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev,
@@ -1332,7 +1349,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev,
if (ret < 0)
goto err;
- ret = simple_util_parse_card_name(card, NULL);
+ ret = simple_util_parse_card_name(priv, NULL);
if (ret < 0)
goto err;
@@ -1355,7 +1372,7 @@ err:
if (ret < 0)
dev_err_probe(dev, ret, "parse error\n");
- return ret;
+ return graph_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(audio_graph2_parse_of);
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index dd414634b4ac..51e0e434514d 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -15,6 +15,13 @@
#include <sound/pcm_params.h>
#include <sound/simple_card_utils.h>
+#define simple_ret(priv, ret) _simple_ret(priv, __func__, ret)
+static inline int _simple_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
int simple_util_get_sample_fmt(struct simple_util_data *data)
{
int i;
@@ -133,33 +140,42 @@ int simple_util_parse_daifmt(struct device *dev,
}
EXPORT_SYMBOL_GPL(simple_util_parse_daifmt);
-int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np,
+int simple_util_parse_tdm_width_map(struct simple_util_priv *priv, struct device_node *np,
struct simple_util_dai *dai)
{
+ struct device *dev = simple_priv_to_dev(priv);
int n, i, ret;
u32 *p;
+ /*
+ * NOTE
+ *
+ * Clang doesn't allow to use "goto end" before calling __free(),
+ * because it bypasses the initialization. Use simple_ret() directly.
+ */
+
n = of_property_count_elems_of_size(np, "dai-tdm-slot-width-map", sizeof(u32));
if (n <= 0)
return 0;
+
if (n % 3) {
dev_err(dev, "Invalid number of cells for dai-tdm-slot-width-map\n");
- return -EINVAL;
+ return simple_ret(priv, -EINVAL); /* see NOTE */
}
+ ret = -ENOMEM;
dai->tdm_width_map = devm_kcalloc(dev, n, sizeof(*dai->tdm_width_map), GFP_KERNEL);
if (!dai->tdm_width_map)
- return -ENOMEM;
+ return simple_ret(priv, ret); /* see NOTE */
- u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values),
- GFP_KERNEL);
+ u32 *array_values __free(kfree) = kcalloc(n, sizeof(*array_values), GFP_KERNEL);
if (!array_values)
- return -ENOMEM;
+ goto end;
ret = of_property_read_u32_array(np, "dai-tdm-slot-width-map", array_values, n);
if (ret < 0) {
dev_err(dev, "Could not read dai-tdm-slot-width-map: %d\n", ret);
- return ret;
+ goto end;
}
p = array_values;
@@ -170,15 +186,17 @@ int simple_util_parse_tdm_width_map(struct device *dev, struct device_node *np,
}
dai->n_tdm_widths = i;
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(simple_util_parse_tdm_width_map);
-int simple_util_set_dailink_name(struct device *dev,
+int simple_util_set_dailink_name(struct simple_util_priv *priv,
struct snd_soc_dai_link *dai_link,
const char *fmt, ...)
{
+ struct device *dev = simple_priv_to_dev(priv);
va_list ap;
char *name = NULL;
int ret = -ENOMEM;
@@ -194,13 +212,14 @@ int simple_util_set_dailink_name(struct device *dev,
dai_link->stream_name = name;
}
- return ret;
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(simple_util_set_dailink_name);
-int simple_util_parse_card_name(struct snd_soc_card *card,
+int simple_util_parse_card_name(struct simple_util_priv *priv,
char *prefix)
{
+ struct snd_soc_card *card = simple_priv_to_card(priv);
int ret;
if (!prefix)
@@ -214,13 +233,13 @@ int simple_util_parse_card_name(struct snd_soc_card *card,
snprintf(prop, sizeof(prop), "%sname", prefix);
ret = snd_soc_of_parse_card_name(card, prop);
if (ret < 0)
- return ret;
+ goto end;
}
if (!card->name && card->dai_link)
card->name = card->dai_link->name;
-
- return 0;
+end:
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(simple_util_parse_card_name);
@@ -348,7 +367,8 @@ cpu_err:
break;
simple_clk_disable(dai);
}
- return ret;
+
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(simple_util_startup);
@@ -379,16 +399,19 @@ void simple_util_shutdown(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(simple_util_shutdown);
-static int simple_set_clk_rate(struct device *dev,
- struct simple_util_dai *simple_dai,
- unsigned long rate)
+static int simple_set_clk_rate(struct simple_util_priv *priv,
+ struct simple_util_dai *simple_dai,
+ unsigned long rate)
{
+ struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
+
if (!simple_dai)
return 0;
if (simple_dai->clk_fixed && rate != simple_dai->sysclk) {
dev_err(dev, "dai %s invalid clock rate %lu\n", simple_dai->name, rate);
- return -EINVAL;
+ goto end;
}
if (!simple_dai->clk)
@@ -397,12 +420,15 @@ static int simple_set_clk_rate(struct device *dev,
if (clk_get_rate(simple_dai->clk) == rate)
return 0;
- return clk_set_rate(simple_dai->clk, rate);
+ ret = clk_set_rate(simple_dai->clk, rate);
+end:
+ return simple_ret(priv, ret);
}
-static int simple_set_tdm(struct snd_soc_dai *dai,
- struct simple_util_dai *simple_dai,
- struct snd_pcm_hw_params *params)
+static int simple_set_tdm(struct simple_util_priv *priv,
+ struct snd_soc_dai *dai,
+ struct simple_util_dai *simple_dai,
+ struct snd_pcm_hw_params *params)
{
int sample_bits = params_width(params);
int slot_width, slot_count;
@@ -430,12 +456,8 @@ static int simple_set_tdm(struct snd_soc_dai *dai,
simple_dai->rx_slot_mask,
slot_count,
slot_width);
- if (ret && ret != -ENOTSUPP) {
- dev_err(dai->dev, "simple-card: set_tdm_slot error: %d\n", ret);
- return ret;
- }
- return 0;
+ return simple_ret(priv, ret);
}
int simple_util_hw_params(struct snd_pcm_substream *substream,
@@ -457,15 +479,15 @@ int simple_util_hw_params(struct snd_pcm_substream *substream,
mclk = params_rate(params) * mclk_fs;
for_each_prop_dai_codec(props, i, pdai) {
- ret = simple_set_clk_rate(rtd->dev, pdai, mclk);
+ ret = simple_set_clk_rate(priv, pdai, mclk);
if (ret < 0)
- return ret;
+ goto end;
}
for_each_prop_dai_cpu(props, i, pdai) {
- ret = simple_set_clk_rate(rtd->dev, pdai, mclk);
+ ret = simple_set_clk_rate(priv, pdai, mclk);
if (ret < 0)
- return ret;
+ goto end;
}
/* Ensure sysclk is set on all components in case any
@@ -476,39 +498,40 @@ int simple_util_hw_params(struct snd_pcm_substream *substream,
ret = snd_soc_component_set_sysclk(component, 0, 0,
mclk, SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP)
- return ret;
+ goto end;
}
for_each_rtd_codec_dais(rtd, i, sdai) {
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;
+ goto end;
}
for_each_rtd_cpu_dais(rtd, i, sdai) {
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;
+ goto end;
}
}
for_each_prop_dai_codec(props, i, pdai) {
sdai = snd_soc_rtd_to_codec(rtd, i);
- ret = simple_set_tdm(sdai, pdai, params);
+ ret = simple_set_tdm(priv, sdai, pdai, params);
if (ret < 0)
- return ret;
+ goto end;
}
for_each_prop_dai_cpu(props, i, pdai) {
sdai = snd_soc_rtd_to_cpu(rtd, i);
- ret = simple_set_tdm(sdai, pdai, params);
+ ret = simple_set_tdm(priv, sdai, pdai, params);
if (ret < 0)
- return ret;
+ goto end;
}
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(simple_util_hw_params);
@@ -536,7 +559,8 @@ int simple_util_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
}
EXPORT_SYMBOL_GPL(simple_util_be_hw_params_fixup);
-static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simple_dai)
+static int simple_init_dai(struct simple_util_priv *priv,
+ struct snd_soc_dai *dai, struct simple_util_dai *simple_dai)
{
int ret;
@@ -548,7 +572,7 @@ static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simp
simple_dai->clk_direction);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "simple-card: set_sysclk error\n");
- return ret;
+ goto end;
}
}
@@ -560,11 +584,12 @@ static int simple_init_dai(struct snd_soc_dai *dai, struct simple_util_dai *simp
simple_dai->slot_width);
if (ret && ret != -ENOTSUPP) {
dev_err(dai->dev, "simple-card: set_tdm_slot error\n");
- return ret;
+ goto end;
}
}
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
static inline int simple_component_is_codec(struct snd_soc_component *component)
@@ -572,7 +597,8 @@ static inline int simple_component_is_codec(struct snd_soc_component *component)
return component->driver->endianness;
}
-static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
+static int simple_init_for_codec2codec(struct simple_util_priv *priv,
+ struct snd_soc_pcm_runtime *rtd,
struct simple_dai_props *dai_props)
{
struct snd_soc_dai_link *dai_link = rtd->dai_link;
@@ -604,12 +630,13 @@ static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
if (ret < 0) {
dev_err(rtd->dev, "simple-card: no valid dai_link params\n");
- return ret;
+ goto end;
}
+ ret = -ENOMEM;
c2c_params = devm_kzalloc(rtd->dev, sizeof(*c2c_params), GFP_KERNEL);
if (!c2c_params)
- return -ENOMEM;
+ goto end;
c2c_params->formats = hw.formats;
c2c_params->rates = hw.rates;
@@ -621,7 +648,9 @@ static int simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
dai_link->c2c_params = c2c_params;
dai_link->num_c2c_params = 1;
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd)
@@ -632,21 +661,19 @@ int simple_util_dai_init(struct snd_soc_pcm_runtime *rtd)
int i, ret;
for_each_prop_dai_codec(props, i, dai) {
- ret = simple_init_dai(snd_soc_rtd_to_codec(rtd, i), dai);
+ ret = simple_init_dai(priv, snd_soc_rtd_to_codec(rtd, i), dai);
if (ret < 0)
- return ret;
+ goto end;
}
for_each_prop_dai_cpu(props, i, dai) {
- ret = simple_init_dai(snd_soc_rtd_to_cpu(rtd, i), dai);
+ ret = simple_init_dai(priv, snd_soc_rtd_to_cpu(rtd, i), dai);
if (ret < 0)
- return ret;
+ goto end;
}
- ret = simple_init_for_codec2codec(rtd, props);
- if (ret < 0)
- return ret;
-
- return 0;
+ ret = simple_init_for_codec2codec(priv, rtd, props);
+end:
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(simple_util_dai_init);
@@ -831,7 +858,7 @@ int simple_util_init_aux_jacks(struct simple_util_priv *priv, char *prefix)
priv->aux_jacks = devm_kcalloc(card->dev, num,
sizeof(struct snd_soc_jack), GFP_KERNEL);
if (!priv->aux_jacks)
- return -ENOMEM;
+ return simple_ret(priv, -ENOMEM);
for_each_card_auxs(card, component) {
char id[128];
@@ -992,13 +1019,11 @@ int graph_util_card_probe(struct snd_soc_card *card)
ret = simple_util_init_hp(card, &priv->hp_jack, NULL);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_init_mic(card, &priv->mic_jack, NULL);
- if (ret < 0)
- return ret;
-
- return 0;
+end:
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(graph_util_card_probe);
@@ -1074,9 +1099,10 @@ static int graph_get_dai_id(struct device_node *ep)
return id;
}
-int graph_util_parse_dai(struct device *dev, struct device_node *ep,
+int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep,
struct snd_soc_dai_link_component *dlc, int *is_single_link)
{
+ struct device *dev = simple_priv_to_dev(priv);
struct of_phandle_args args = {};
struct snd_soc_dai *dai;
int ret;
@@ -1092,10 +1118,12 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep,
args.np = ep;
dai = snd_soc_get_dai_via_args(&args);
if (dai) {
+ ret = -ENOMEM;
+ dlc->of_node = node;
dlc->dai_name = snd_soc_dai_name_get(dai);
dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
if (!dlc->dai_args)
- return -ENOMEM;
+ goto end;
goto parse_dai_end;
}
@@ -1126,13 +1154,14 @@ int graph_util_parse_dai(struct device *dev, struct device_node *ep,
*/
ret = snd_soc_get_dlc(&args, dlc);
if (ret < 0)
- return ret;
+ goto end;
parse_dai_end:
if (is_single_link)
*is_single_link = of_graph_get_endpoint_count(node) == 1;
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(graph_util_parse_dai);
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index afe7e79ffdbd..5af6d1b308f2 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -29,7 +29,16 @@ static const struct snd_soc_ops simple_ops = {
.hw_params = simple_util_hw_params,
};
-static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_link_component *dlc)
+#define simple_ret(priv, ret) _simple_ret(priv, __func__, ret)
+static inline int _simple_ret(struct simple_util_priv *priv,
+ const char *func, int ret)
+{
+ return snd_soc_ret(simple_priv_to_dev(priv), ret, "at %s()\n", func);
+}
+
+static int simple_parse_platform(struct simple_util_priv *priv,
+ struct device_node *node,
+ struct snd_soc_dai_link_component *dlc)
{
struct of_phandle_args args;
int ret;
@@ -43,7 +52,7 @@ static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_li
*/
ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
if (ret)
- return ret;
+ return simple_ret(priv, ret);
/* dai_name is not required and may not exist for plat component */
@@ -52,11 +61,12 @@ static int simple_parse_platform(struct device_node *node, struct snd_soc_dai_li
return 0;
}
-static int simple_parse_dai(struct device *dev,
+static int simple_parse_dai(struct simple_util_priv *priv,
struct device_node *node,
struct snd_soc_dai_link_component *dlc,
int *is_single_link)
{
+ struct device *dev = simple_priv_to_dev(priv);
struct of_phandle_args args;
struct snd_soc_dai *dai;
int ret;
@@ -70,17 +80,18 @@ static int simple_parse_dai(struct device *dev,
*/
ret = of_parse_phandle_with_args(node, DAI, CELL, 0, &args);
if (ret)
- return ret;
+ goto end;
/*
* Try to find from DAI args
*/
dai = snd_soc_get_dai_via_args(&args);
if (dai) {
+ ret = -ENOMEM;
dlc->dai_name = snd_soc_dai_name_get(dai);
dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
if (!dlc->dai_args)
- return -ENOMEM;
+ goto end;
goto parse_dai_end;
}
@@ -106,13 +117,14 @@ static int simple_parse_dai(struct device *dev,
*/
ret = snd_soc_get_dlc(&args, dlc);
if (ret < 0)
- return ret;
+ goto end;
parse_dai_end:
if (is_single_link)
*is_single_link = !args.args_count;
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
static void simple_parse_convert(struct device *dev,
@@ -149,19 +161,17 @@ static int simple_parse_node(struct simple_util_priv *priv,
dai = simple_props_to_dai_codec(dai_props, 0);
}
- ret = simple_parse_dai(dev, np, dlc, cpu);
+ ret = simple_parse_dai(priv, np, dlc, cpu);
if (ret)
- return ret;
+ goto end;
ret = simple_util_parse_clk(dev, np, dai, dlc);
if (ret)
- return ret;
+ goto end;
ret = simple_util_parse_tdm(np, dai);
- if (ret)
- return ret;
-
- return 0;
+end:
+ return simple_ret(priv, ret);
}
static int simple_link_init(struct simple_util_priv *priv,
@@ -183,7 +193,7 @@ static int simple_link_init(struct simple_util_priv *priv,
ret = simple_util_parse_daifmt(dev, node, codec,
prefix, &dai_link->dai_fmt);
if (ret < 0)
- return ret;
+ goto end;
graph_util_parse_link_direction(top, &playback_only, &capture_only);
graph_util_parse_link_direction(node, &playback_only, &capture_only);
@@ -213,7 +223,9 @@ static int simple_link_init(struct simple_util_priv *priv,
dai_link->init = simple_util_dai_init;
dai_link->ops = &simple_ops;
- return simple_util_set_dailink_name(dev, dai_link, name);
+ ret = simple_util_set_dailink_name(priv, dai_link, name);
+end:
+ return simple_ret(priv, ret);
}
static int simple_dai_link_of_dpcm(struct simple_util_priv *priv,
@@ -290,7 +302,7 @@ static int simple_dai_link_of_dpcm(struct simple_util_priv *priv,
out_put_node:
li->link++;
- return ret;
+ return simple_ret(priv, ret);
}
static int simple_dai_link_of(struct simple_util_priv *priv,
@@ -330,7 +342,7 @@ static int simple_dai_link_of(struct simple_util_priv *priv,
if (ret < 0)
goto dai_link_of_err;
- ret = simple_parse_platform(plat, platforms);
+ ret = simple_parse_platform(priv, plat, platforms);
if (ret < 0)
goto dai_link_of_err;
@@ -345,7 +357,7 @@ static int simple_dai_link_of(struct simple_util_priv *priv,
dai_link_of_err:
li->link++;
- return ret;
+ return simple_ret(priv, ret);
}
static int __simple_for_each_link(struct simple_util_priv *priv,
@@ -443,10 +455,10 @@ static int __simple_for_each_link(struct simple_util_priv *priv,
node = of_get_next_child(top, node);
} while (!is_top && node);
- error:
+error:
of_node_put(node);
- return ret;
+ return simple_ret(priv, ret);
}
static int simple_for_each_link(struct simple_util_priv *priv,
@@ -479,7 +491,7 @@ static int simple_for_each_link(struct simple_util_priv *priv,
break;
}
- return ret;
+ return simple_ret(priv, ret);
}
static void simple_depopulate_aux(void *data)
@@ -500,9 +512,11 @@ static int simple_populate_aux(struct simple_util_priv *priv)
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret)
- return ret;
+ goto end;
- return devm_add_action_or_reset(dev, simple_depopulate_aux, priv);
+ ret = devm_add_action_or_reset(dev, simple_depopulate_aux, priv);
+end:
+ return simple_ret(priv, ret);
}
static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li)
@@ -512,15 +526,15 @@ static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li)
ret = simple_util_parse_widgets(card, PREFIX);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_routing(card, PREFIX);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_parse_pin_switches(card, PREFIX);
if (ret < 0)
- return ret;
+ goto end;
/* Single/Muti DAI link(s) & New style of DT node */
memset(li, 0, sizeof(*li));
@@ -528,19 +542,19 @@ static int simple_parse_of(struct simple_util_priv *priv, struct link_info *li)
simple_dai_link_of,
simple_dai_link_of_dpcm);
if (ret < 0)
- return ret;
+ goto end;
- ret = simple_util_parse_card_name(card, PREFIX);
+ ret = simple_util_parse_card_name(priv, PREFIX);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_populate_aux(priv);
if (ret < 0)
- return ret;
+ goto end;
ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs");
-
- return ret;
+end:
+ return simple_ret(priv, ret);
}
static int simple_count_noml(struct simple_util_priv *priv,
@@ -548,12 +562,10 @@ static int simple_count_noml(struct simple_util_priv *priv,
struct device_node *codec,
struct link_info *li, bool is_top)
{
- if (li->link >= SNDRV_MAX_LINKS) {
- struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
- dev_err(dev, "too many links\n");
- return -EINVAL;
- }
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
/*
* DON'T REMOVE platforms
@@ -575,8 +587,9 @@ static int simple_count_noml(struct simple_util_priv *priv,
li->num[li->link].codecs = 1;
li->link += 1;
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
static int simple_count_dpcm(struct simple_util_priv *priv,
@@ -584,12 +597,10 @@ static int simple_count_dpcm(struct simple_util_priv *priv,
struct device_node *codec,
struct link_info *li, bool is_top)
{
- if (li->link >= SNDRV_MAX_LINKS) {
- struct device *dev = simple_priv_to_dev(priv);
+ int ret = -EINVAL;
- dev_err(dev, "too many links\n");
- return -EINVAL;
- }
+ if (li->link >= SNDRV_MAX_LINKS)
+ goto end;
if (li->cpu) {
/*
@@ -606,8 +617,9 @@ static int simple_count_dpcm(struct simple_util_priv *priv,
li->link++; /* dummy-Codec */
}
-
- return 0;
+ ret = 0;
+end:
+ return simple_ret(priv, ret);
}
static int simple_get_dais_count(struct simple_util_priv *priv,
@@ -683,17 +695,15 @@ static int simple_soc_probe(struct snd_soc_card *card)
ret = simple_util_init_hp(card, &priv->hp_jack, PREFIX);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_init_mic(card, &priv->mic_jack, PREFIX);
if (ret < 0)
- return ret;
+ goto end;
ret = simple_util_init_aux_jacks(priv, PREFIX);
- if (ret < 0)
- return ret;
-
- return 0;
+end:
+ return simple_ret(priv, ret);
}
static int simple_probe(struct platform_device *pdev)
@@ -715,20 +725,22 @@ static int simple_probe(struct platform_device *pdev)
card->probe = simple_soc_probe;
card->driver_name = "simple-card";
+ ret = -ENOMEM;
struct link_info *li __free(kfree) = kzalloc(sizeof(*li), GFP_KERNEL);
if (!li)
- return -ENOMEM;
+ goto end;
ret = simple_get_dais_count(priv, li);
if (ret < 0)
- return ret;
+ goto end;
+ ret = -EINVAL;
if (!li->link)
- return -EINVAL;
+ goto end;
ret = simple_util_init_priv(priv, li);
if (ret < 0)
- return ret;
+ goto end;
if (np && of_device_is_available(np)) {
@@ -795,8 +807,8 @@ static int simple_probe(struct platform_device *pdev)
return 0;
err:
simple_util_clean_reference(card);
-
- return ret;
+end:
+ return dev_err_probe(dev, ret, "parse error\n");
}
static const struct of_device_id simple_of_match[] = {
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index eca6ec0428bb..585543f872fc 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -51,6 +51,7 @@ struct avs_dsp_ops {
int (* const load_basefw)(struct avs_dev *, struct firmware *);
int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32);
+ int (* const config_basefw)(struct avs_dev *);
int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long,
u32 *);
int (* const log_buffer_offset)(struct avs_dev *, u32);
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 0266edeafc19..2d706edcbf92 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -312,6 +312,18 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
{},
};
+static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
+ {
+ .id = "PCM3168A",
+ .drv_name = "avs_pcm3168a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
+ },
+ .tplg_filename = "pcm3168a-tplg.bin",
+ },
+ {}
+};
+
static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
{
.drv_name = "avs_i2s_test",
@@ -378,10 +390,11 @@ static const struct avs_acpi_boards i2s_boards[] = {
AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines),
AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines),
AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_ADL_N, avs_mbl_i2s_machines),
AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines),
AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines),
- AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines),
- {},
+ AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines),
+ {}
};
static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
index 00b0f6c176d6..ba4bee42124c 100644
--- a/sound/soc/intel/avs/boards/Kconfig
+++ b/sound/soc/intel/avs/boards/Kconfig
@@ -87,6 +87,16 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
+config SND_SOC_INTEL_AVS_MACH_PCM3168A
+ tristate "pcm3168a I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_PCM3168A_I2C
+ help
+ This adds support for AVS with PCM3168A I2C codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
config SND_SOC_INTEL_AVS_MACH_PROBE
tristate "Probing (data) board"
depends on DEBUG_FS
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
index 4fbd936ffb3e..a95256b94dc8 100644
--- a/sound/soc/intel/avs/boards/Makefile
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -9,6 +9,7 @@ snd-soc-avs-max98927-y := max98927.o
snd-soc-avs-max98357a-y := max98357a.o
snd-soc-avs-max98373-y := max98373.o
snd-soc-avs-nau8825-y := nau8825.o
+snd-soc-avs-pcm3168a-y := pcm3168a.o
snd-soc-avs-probe-y := probe.o
snd-soc-avs-rt274-y := rt274.o
snd-soc-avs-rt286-y := rt286.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c
new file mode 100644
index 000000000000..5d0e7a5bdc74
--- /dev/null
+++ b/sound/soc/intel/avs/boards/pcm3168a.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2024-2025 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),
+ SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL),
+ SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL),
+ SND_SOC_DAPM_LINE("CPB Line Out", NULL),
+ SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL),
+ SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL),
+ SND_SOC_DAPM_LINE("CPB Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ { "CPB Stereo HP 1", NULL, "AOUT1L" },
+ { "CPB Stereo HP 1", NULL, "AOUT1R" },
+ { "CPB Stereo HP 2", NULL, "AOUT2L" },
+ { "CPB Stereo HP 2", NULL, "AOUT2R" },
+ { "CPB Stereo HP 3", NULL, "AOUT3L" },
+ { "CPB Stereo HP 3", NULL, "AOUT3R" },
+ { "CPB Line Out", NULL, "AOUT4L" },
+ { "CPB Line Out", NULL, "AOUT4R" },
+
+ { "AIN1L", NULL, "CPB Stereo Mic 1" },
+ { "AIN1R", NULL, "CPB Stereo Mic 1" },
+ { "AIN2L", NULL, "CPB Stereo Mic 2" },
+ { "AIN2R", NULL, "CPB Stereo Mic 2" },
+ { "AIN3L", NULL, "CPB Line In" },
+ { "AIN3R", NULL, "CPB Line In" },
+};
+
+static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* Set SSP to 24 bit. */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+SND_SOC_DAILINK_DEF(pcm3168a_dac,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac")));
+SND_SOC_DAILINK_DEF(pcm3168a_adc,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc")));
+SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
+SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin")));
+
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ const int num_dl = 2;
+
+ dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl[0].num_cpus = 1;
+ dl[0].num_codecs = 1;
+ dl[0].platforms = platform;
+ dl[0].num_platforms = 1;
+ dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+ dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup;
+ dl[0].nonatomic = 1;
+ dl[0].no_pcm = 1;
+ memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+ dl[0].name = "SSP0-Codec-dac";
+ dl[0].cpus = cpu_ssp0;
+ dl[0].codecs = pcm3168a_dac;
+ dl[1].name = "SSP2-Codec-adc";
+ dl[1].cpus = cpu_ssp2;
+ dl[1].codecs = pcm3168a_adc;
+
+ *links = dl;
+ *num_links = num_dl;
+ return 0;
+}
+
+static int avs_pcm3168a_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ card->name = "avs_pcm3168a";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct platform_device_id avs_pcm3168a_driver_ids[] = {
+ {
+ .name = "avs_pcm3168a",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids);
+
+static struct platform_driver avs_pcm3168a_driver = {
+ .probe = avs_pcm3168a_probe,
+ .driver = {
+ .name = "avs_pcm3168a",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_pcm3168a_driver_ids,
+};
+
+module_platform_driver(avs_pcm3168a_driver);
+
+MODULE_DESCRIPTION("Intel pcm3168a machine driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index 7b47e52c2b39..b9de691e9b9b 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -6,6 +6,7 @@
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
+#include <linux/string_choices.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
#include "registers.h"
@@ -39,7 +40,7 @@ int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
AVS_ADSPCS_TIMEOUT_US);
if (ret)
dev_err(adev->dev, "core_mask %d power %s failed: %d\n",
- core_mask, power ? "on" : "off", ret);
+ core_mask, str_on_off(power), ret);
return ret;
}
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index 9ff7818395cd..0b29941feb0e 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -603,7 +603,7 @@ release_fw:
return ret;
}
-int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
+static int avs_load_firmware(struct avs_dev *adev, bool purge)
{
struct avs_soc_component *acomp;
int ret, i;
@@ -657,28 +657,33 @@ reenable_gating:
return 0;
}
-int avs_dsp_first_boot_firmware(struct avs_dev *adev)
+static int avs_config_basefw(struct avs_dev *adev)
{
- int ret, i;
+ int ret;
- if (avs_platattr_test(adev, CLDMA)) {
- ret = hda_cldma_init(&code_loader, &adev->base.core,
- adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
- if (ret < 0) {
- dev_err(adev->dev, "cldma init failed: %d\n", ret);
+ if (adev->spec->dsp_ops->config_basefw) {
+ ret = avs_dsp_op(adev, config_basefw);
+ if (ret)
return ret;
- }
}
- ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
- if (ret < 0)
- return ret;
+ return 0;
+}
- ret = avs_dsp_boot_firmware(adev, true);
- if (ret < 0) {
- dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
+{
+ int ret;
+
+ ret = avs_load_firmware(adev, purge);
+ if (ret)
return ret;
- }
+
+ return avs_config_basefw(adev);
+}
+
+static int avs_dsp_alloc_resources(struct avs_dev *adev)
+{
+ int ret, i;
ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg);
if (ret)
@@ -705,6 +710,31 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev)
strscpy(adev->lib_names[0], "BASEFW", AVS_LIB_NAME_SIZE);
ida_init(&adev->ppl_ida);
-
return 0;
}
+
+int avs_dsp_first_boot_firmware(struct avs_dev *adev)
+{
+ int ret;
+
+ if (avs_platattr_test(adev, CLDMA)) {
+ ret = hda_cldma_init(&code_loader, &adev->base.core,
+ adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
+ if (ret < 0) {
+ dev_err(adev->dev, "cldma init failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_dsp_boot_firmware(adev, true);
+ if (ret < 0) {
+ dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+ return ret;
+ }
+
+ return avs_dsp_alloc_resources(adev);
+}
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 30b666f8909b..242a175381c2 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -510,6 +510,44 @@ err:
return ret;
}
+int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...)
+{
+ struct avs_tlv *tlv;
+ void *payload;
+ size_t offset;
+ va_list args;
+ int ret, i;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ va_start(args, num_tlvs);
+ for (offset = i = 0; i < num_tlvs && offset < AVS_MAILBOX_SIZE - sizeof(*tlv); i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+ tlv->type = va_arg(args, u32);
+ tlv->length = va_arg(args, u32);
+
+ offset += sizeof(*tlv) + tlv->length;
+ if (offset > AVS_MAILBOX_SIZE)
+ break;
+
+ memcpy(tlv->value, va_arg(args, u8*), tlv->length);
+ }
+
+ if (i == num_tlvs)
+ ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_FIRMWARE_CONFIG, payload, offset);
+ else
+ ret = -ERANGE;
+
+ va_end(args);
+ kfree(payload);
+ if (ret)
+ dev_err(adev->dev, "set fw cfg failed: %d\n", ret);
+ return ret;
+}
+
int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg)
{
struct avs_tlv *tlv;
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 0378633c7f96..f44fcfc81de7 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -451,6 +451,8 @@ enum avs_fw_cfg_params {
AVS_FW_CFG_RESERVED,
AVS_FW_CFG_POWER_GATING_POLICY,
AVS_FW_CFG_ASSERT_MODE,
+ AVS_FW_CFG_RESERVED2,
+ AVS_FW_CFG_BUS_HARDWARE_ID,
};
struct avs_fw_cfg {
@@ -475,7 +477,14 @@ struct avs_fw_cfg {
u32 power_gating_policy;
};
+struct avs_bus_hwid {
+ u32 device;
+ u32 subsystem;
+ u8 revision;
+};
+
int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg);
+int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...);
enum avs_hw_cfg_params {
AVS_HW_CFG_AVS_VER,
@@ -643,6 +652,9 @@ int avs_ipc_set_system_time(struct avs_dev *adev);
#define AVS_INTELWOV_MOD_UUID \
GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7)
+#define AVS_WOVHOSTM_MOD_UUID \
+ GUID_INIT(0xF9ED62B7, 0x092E, 0x4A90, 0x8F, 0x4D, 0x82, 0xDA, 0xA8, 0xB3, 0x8F, 0x3B)
+
/* channel map */
enum avs_channel_index {
AVS_CHANNEL_LEFT = 0,
@@ -872,6 +884,16 @@ struct avs_wov_cfg {
} __packed;
static_assert(sizeof(struct avs_wov_cfg) == 44);
+struct avs_whm_cfg {
+ struct avs_modcfg_base base;
+ /* Audio format for output pin 0 */
+ struct avs_audio_format ref_fmt;
+ struct avs_audio_format out_fmt;
+ u32 wake_tick_period;
+ struct avs_copier_gtw_cfg gtw_cfg;
+} __packed;
+static_assert(sizeof(struct avs_whm_cfg) == 108);
+
/* Module runtime parameters */
enum avs_copier_runtime_param {
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index f31d5e2caa7b..dfb85bd2b665 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -115,150 +115,188 @@ avs_path_find_variant(struct avs_dev *adev,
return NULL;
}
-__maybe_unused
-static bool avs_dma_type_is_host(u32 dma_type)
+static void avs_init_node_id(union avs_connector_node_id *node_id,
+ struct avs_tplg_modcfg_ext *te, u32 dma_id)
{
- return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
- dma_type == AVS_DMA_HDA_HOST_INPUT;
-}
+ node_id->val = 0;
+ node_id->dma_type = te->copier.dma_type;
-__maybe_unused
-static bool avs_dma_type_is_link(u32 dma_type)
-{
- return !avs_dma_type_is_host(dma_type);
-}
+ switch (node_id->dma_type) {
+ case AVS_DMA_DMIC_LINK_INPUT:
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ case AVS_DMA_I2S_LINK_INPUT:
+ /* Gateway's virtual index is statically assigned in the topology. */
+ node_id->vindex = te->copier.vindex.val;
+ break;
-__maybe_unused
-static bool avs_dma_type_is_output(u32 dma_type)
-{
- return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
- dma_type == AVS_DMA_HDA_LINK_OUTPUT ||
- dma_type == AVS_DMA_I2S_LINK_OUTPUT;
-}
+ case AVS_DMA_HDA_HOST_OUTPUT:
+ case AVS_DMA_HDA_HOST_INPUT:
+ /* Gateway's virtual index is dynamically assigned with DMA ID */
+ node_id->vindex = dma_id;
+ break;
-__maybe_unused
-static bool avs_dma_type_is_input(u32 dma_type)
-{
- return !avs_dma_type_is_output(dma_type);
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ node_id->vindex = te->copier.vindex.val | dma_id;
+ break;
+
+ default:
+ *node_id = INVALID_NODE_ID;
+ break;
+ }
}
-static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
-{
- struct avs_tplg_module *t = mod->template;
- struct avs_copier_cfg *cfg;
- struct acpi_nhlt_format_config *ep_blob;
- struct acpi_nhlt_endpoint *ep;
- union avs_connector_node_id node_id = {0};
- size_t cfg_size, data_size;
- void *data = NULL;
- u32 dma_type;
- int ret;
+/* Every BLOB contains at least gateway attributes. */
+static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4};
- data_size = sizeof(cfg->gtw_cfg.config);
- dma_type = t->cfg_ext->copier.dma_type;
- node_id.dma_type = dma_type;
+static struct acpi_nhlt_config *
+avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t)
+{
+ struct acpi_nhlt_format_config *fmtcfg;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_audio_format *fmt;
+ int link_type, dev_type;
+ int bus_id, dir;
- switch (dma_type) {
- struct avs_audio_format *fmt;
- int direction;
+ te = t->cfg_ext;
+ switch (te->copier.dma_type) {
case AVS_DMA_I2S_LINK_OUTPUT:
- case AVS_DMA_I2S_LINK_INPUT:
- if (avs_dma_type_is_input(dma_type))
- direction = SNDRV_PCM_STREAM_CAPTURE;
- else
- direction = SNDRV_PCM_STREAM_PLAYBACK;
-
- if (t->cfg_ext->copier.blob_fmt)
- fmt = t->cfg_ext->copier.blob_fmt;
- else if (direction == SNDRV_PCM_STREAM_CAPTURE)
- fmt = t->in_fmt;
- else
- fmt = t->cfg_ext->copier.out_fmt;
-
- ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP,
- ACPI_NHLT_DEVICETYPE_CODEC, direction,
- t->cfg_ext->copier.vindex.i2s.instance);
- ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq,
- fmt->valid_bit_depth, fmt->bit_depth);
- if (!ep_blob) {
- dev_err(adev->dev, "no I2S ep_blob found\n");
- return -ENOENT;
- }
-
- data = ep_blob->config.capabilities;
- data_size = ep_blob->config.capabilities_size;
- /* I2S gateway's vindex is statically assigned in topology */
- node_id.vindex = t->cfg_ext->copier.vindex.val;
+ link_type = ACPI_NHLT_LINKTYPE_SSP;
+ dev_type = ACPI_NHLT_DEVICETYPE_CODEC;
+ bus_id = te->copier.vindex.i2s.instance;
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ fmt = te->copier.out_fmt;
+ break;
+ case AVS_DMA_I2S_LINK_INPUT:
+ link_type = ACPI_NHLT_LINKTYPE_SSP;
+ dev_type = ACPI_NHLT_DEVICETYPE_CODEC;
+ bus_id = te->copier.vindex.i2s.instance;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
break;
case AVS_DMA_DMIC_LINK_INPUT:
- direction = SNDRV_PCM_STREAM_CAPTURE;
-
- if (t->cfg_ext->copier.blob_fmt)
- fmt = t->cfg_ext->copier.blob_fmt;
- else
- fmt = t->in_fmt;
-
- ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, direction, 0);
- ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq,
- fmt->valid_bit_depth, fmt->bit_depth);
- if (!ep_blob) {
- dev_err(adev->dev, "no DMIC ep_blob found\n");
- return -ENOENT;
- }
-
- data = ep_blob->config.capabilities;
- data_size = ep_blob->config.capabilities_size;
- /* DMIC gateway's vindex is statically assigned in topology */
- node_id.vindex = t->cfg_ext->copier.vindex.val;
-
+ link_type = ACPI_NHLT_LINKTYPE_PDM;
+ dev_type = -1; /* ignored */
+ bus_id = 0;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
break;
- case AVS_DMA_HDA_HOST_OUTPUT:
- case AVS_DMA_HDA_HOST_INPUT:
- /* HOST gateway's vindex is dynamically assigned with DMA id */
- node_id.vindex = mod->owner->owner->dma_id;
- break;
+ default:
+ return default_blob;
+ }
- case AVS_DMA_HDA_LINK_OUTPUT:
- case AVS_DMA_HDA_LINK_INPUT:
- node_id.vindex = t->cfg_ext->copier.vindex.val |
- mod->owner->owner->dma_id;
- break;
+ /* Override format selection if necessary. */
+ if (te->copier.blob_fmt)
+ fmt = te->copier.blob_fmt;
- case INVALID_OBJECT_ID:
- default:
- node_id = INVALID_NODE_ID;
- break;
+ fmtcfg = acpi_nhlt_find_fmtcfg(link_type, dev_type, dir, bus_id,
+ fmt->num_channels, fmt->sampling_freq, fmt->valid_bit_depth,
+ fmt->bit_depth);
+ if (!fmtcfg) {
+ dev_warn(adev->dev, "Endpoint format configuration not found.\n");
+ return ERR_PTR(-ENOENT);
}
- cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config) + data_size;
- if (cfg_size > AVS_MAILBOX_SIZE)
- return -EINVAL;
+ if (fmtcfg->config.capabilities_size < default_blob->capabilities_size)
+ return ERR_PTR(-ETOOSMALL);
+ /* The firmware expects the payload to be DWORD-aligned. */
+ if (fmtcfg->config.capabilities_size % sizeof(u32))
+ return ERR_PTR(-EINVAL);
+
+ return &fmtcfg->config;
+}
+
+static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw,
+ struct avs_tplg_module *t, size_t *cfg_size)
+{
+ struct acpi_nhlt_config *blob;
+ size_t gtw_size;
+
+ blob = avs_nhlt_config_or_default(adev, t);
+ if (IS_ERR(blob))
+ return PTR_ERR(blob);
+ gtw_size = blob->capabilities_size;
+ if (*cfg_size + gtw_size > AVS_MAILBOX_SIZE)
+ return -E2BIG;
+
+ gtw->config_length = gtw_size / sizeof(u32);
+ memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size);
+ *cfg_size += gtw_size;
+
+ return 0;
+}
+
+static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_copier_cfg *cfg;
+ size_t cfg_size;
+ u32 dma_id;
+ int ret;
+
+ te = t->cfg_ext;
cfg = adev->modcfg_buf;
- memset(cfg, 0, cfg_size);
+ dma_id = mod->owner->owner->dma_id;
+ cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config);
+
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size);
+ if (ret)
+ return ret;
+
cfg->base.cpc = t->cfg_base->cpc;
cfg->base.ibs = t->cfg_base->ibs;
cfg->base.obs = t->cfg_base->obs;
cfg->base.is_pages = t->cfg_base->is_pages;
cfg->base.audio_fmt = *t->in_fmt;
- cfg->out_fmt = *t->cfg_ext->copier.out_fmt;
- cfg->feature_mask = t->cfg_ext->copier.feature_mask;
- cfg->gtw_cfg.node_id = node_id;
- cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size;
- /* config_length in DWORDs */
- cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4);
- if (data)
- memcpy(&cfg->gtw_cfg.config.blob, data, data_size);
+ cfg->out_fmt = *te->copier.out_fmt;
+ cfg->feature_mask = te->copier.feature_mask;
+ avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id);
+ cfg->gtw_cfg.dma_buffer_size = te->copier.dma_buffer_size;
+ mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
+ return ret;
+}
+
+static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_whm_cfg *cfg;
+ size_t cfg_size;
+ u32 dma_id;
+ int ret;
+
+ te = t->cfg_ext;
+ cfg = adev->modcfg_buf;
+ dma_id = mod->owner->owner->dma_id;
+ cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config);
+
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size);
+ if (ret)
+ return ret;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->ref_fmt = *te->whm.ref_fmt;
+ cfg->out_fmt = *te->whm.out_fmt;
+ cfg->wake_tick_period = te->whm.wake_tick_period;
+ avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id);
+ cfg->gtw_cfg.dma_buffer_size = te->whm.dma_buffer_size;
mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
- ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
- t->core_id, t->domain, cfg, cfg_size,
- &mod->instance_id);
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
return ret;
}
@@ -533,6 +571,7 @@ static struct avs_module_create avs_module_create[] = {
{ &AVS_ASRC_MOD_UUID, avs_asrc_create },
{ &AVS_INTELWOV_MOD_UUID, avs_wov_create },
{ &AVS_PROBE_MOD_UUID, avs_probe_create },
+ { &AVS_WOVHOSTM_MOD_UUID, avs_whm_create },
};
static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod)
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
index a9019ff5e3af..56905f2b9eb2 100644
--- a/sound/soc/intel/avs/tgl.c
+++ b/sound/soc/intel/avs/tgl.c
@@ -6,7 +6,11 @@
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
+#include <linux/pci.h>
#include "avs.h"
+#include "messages.h"
+
+#define CPUID_TSC_LEAF 0x15
static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
{
@@ -35,6 +39,34 @@ static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stal
return avs_dsp_core_stall(adev, core_mask, stall);
}
+static int avs_tgl_config_basefw(struct avs_dev *adev)
+{
+ struct pci_dev *pci = adev->base.pci;
+ struct avs_bus_hwid hwid;
+ int ret;
+#ifdef CONFIG_X86
+ unsigned int ecx;
+
+#include <asm/cpuid.h>
+ ecx = cpuid_ecx(CPUID_TSC_LEAF);
+ if (ecx) {
+ ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_XTAL_FREQ_HZ, sizeof(ecx), &ecx);
+ if (ret)
+ return AVS_IPC_RET(ret);
+ }
+#endif
+
+ hwid.device = pci->device;
+ hwid.subsystem = pci->subsystem_vendor | (pci->subsystem_device << 16);
+ hwid.revision = pci->revision;
+
+ ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_BUS_HARDWARE_ID, sizeof(hwid), &hwid);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
const struct avs_dsp_ops avs_tgl_dsp_ops = {
.power = avs_tgl_dsp_core_power,
.reset = avs_tgl_dsp_core_reset,
@@ -44,6 +76,7 @@ const struct avs_dsp_ops avs_tgl_dsp_ops = {
.load_basefw = avs_icl_load_basefw,
.load_lib = avs_hda_load_library,
.transfer_mods = avs_hda_transfer_modules,
+ .config_basefw = avs_tgl_config_basefw,
.log_buffer_offset = avs_icl_log_buffer_offset,
.log_buffer_status = avs_apl_log_buffer_status,
.coredump = avs_apl_coredump,
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index d612f20ed989..471b00b9a149 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -815,6 +815,48 @@ static const struct avs_tplg_token_parser modcfg_ext_parsers[] = {
.offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins),
.parse = avs_parse_short_token,
},
+ {
+ .token = AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.wake_tick_period),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_VINDEX_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.vindex),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_DMA_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_buffer_size),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
};
static const struct avs_tplg_token_parser pin_format_parsers[] = {
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index 7892e3797f63..23d5ccd19959 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -74,10 +74,17 @@ struct avs_tplg_modcfg_ext {
union avs_virtual_index vindex;
u32 dma_type;
u32 dma_buffer_size;
- u32 config_length;
- /* config_data part of priv data */
} copier;
struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ u32 wake_tick_period;
+ union avs_virtual_index vindex;
+ u32 dma_type;
+ u32 dma_buffer_size;
+ struct avs_audio_format *blob_fmt; /* optional override */
+ } whm;
+ struct {
u32 out_channel_config;
u32 coefficients_select;
s32 coefficients[AVS_CHANNELS_MAX];
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
index 770e2194a283..9e611e3667ad 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -330,7 +330,7 @@ static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = {
static const struct snd_soc_acpi_adr_device rt1318_1_single_adr[] = {
{
- .adr = 0x000130025D131801,
+ .adr = 0x000130025D131801ull,
.num_endpoints = 1,
.endpoints = &single_endpoint,
.name_prefix = "rt1318-1"
diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
index 9eb4a43e3e7a..dd7993b76dee 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
@@ -251,7 +251,7 @@ 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[] = {
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt713_vb_l2_rt1320_l13[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt713_vb_2_adr),
@@ -270,7 +270,7 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_vb_l2_rt1320_l13[] = {
{}
};
-static const struct snd_soc_acpi_link_adr lnl_sdw_rt712_vb_l2_rt1320_l1[] = {
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l2_rt1320_l1[] = {
{
.mask = BIT(2),
.num_adr = ARRAY_SIZE(rt712_vb_2_group1_adr),
@@ -337,17 +337,17 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
},
{
.link_mask = BIT(1) | BIT(2),
- .links = lnl_sdw_rt712_vb_l2_rt1320_l1,
+ .links = ptl_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"
+ .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg"
},
{
.link_mask = BIT(1) | BIT(2) | BIT(3),
- .links = lnl_sdw_rt713_vb_l2_rt1320_l13,
+ .links = ptl_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"
+ .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg"
},
{},
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
index 6f8c06413665..b77aafb0bfb6 100644
--- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c
@@ -658,25 +658,25 @@ static const struct snd_soc_acpi_endpoint cs35l56_7_fb_endpoints[] = {
static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_1_4_fb_adr[] = {
{
- .adr = 0x00003301fa355601,
+ .adr = 0x00003301fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
.endpoints = cs35l56_l_fb_endpoints,
.name_prefix = "AMP1"
},
{
- .adr = 0x00003201fa355601,
+ .adr = 0x00003201fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_2_fb_endpoints),
.endpoints = cs35l56_2_fb_endpoints,
.name_prefix = "AMP2"
},
{
- .adr = 0x00003101fa355601,
+ .adr = 0x00003101fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_4_fb_endpoints),
.endpoints = cs35l56_4_fb_endpoints,
.name_prefix = "AMP3"
},
{
- .adr = 0x00003001fa355601,
+ .adr = 0x00003001fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_6_fb_endpoints),
.endpoints = cs35l56_6_fb_endpoints,
.name_prefix = "AMP4"
@@ -685,25 +685,25 @@ static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_1_4_fb_adr[] = {
static const struct snd_soc_acpi_adr_device cs35l56_sdw_eight_5_8_fb_adr[] = {
{
- .adr = 0x00013701fa355601,
+ .adr = 0x00013701fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
.endpoints = cs35l56_r_fb_endpoints,
.name_prefix = "AMP8"
},
{
- .adr = 0x00013601fa355601,
+ .adr = 0x00013601fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_3_fb_endpoints),
.endpoints = cs35l56_3_fb_endpoints,
.name_prefix = "AMP7"
},
{
- .adr = 0x00013501fa355601,
+ .adr = 0x00013501fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_5_fb_endpoints),
.endpoints = cs35l56_5_fb_endpoints,
.name_prefix = "AMP6"
},
{
- .adr = 0x00013401fa355601,
+ .adr = 0x00013401fa355601ull,
.num_endpoints = ARRAY_SIZE(cs35l56_7_fb_endpoints),
.endpoints = cs35l56_7_fb_endpoints,
.name_prefix = "AMP5"
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
index 3044d9ab3d4d..3809068f5620 100644
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -500,26 +500,6 @@ static int mtk_memif_set_rate_fs(struct mtk_base_afe *afe,
return 0;
}
-int mtk_memif_set_rate(struct mtk_base_afe *afe,
- int id, unsigned int rate)
-{
- int fs = 0;
-
- if (!afe->get_dai_fs) {
- dev_err(afe->dev, "%s(), error, afe->get_dai_fs == NULL\n",
- __func__);
- return -EINVAL;
- }
-
- fs = afe->get_dai_fs(afe, id, rate);
-
- if (fs < 0)
- return -EINVAL;
-
- return mtk_memif_set_rate_fs(afe, id, fs);
-}
-EXPORT_SYMBOL_GPL(mtk_memif_set_rate);
-
int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream,
int id, unsigned int rate)
{
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.h b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
index 8cec90671827..b6d0f2b27e86 100644
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.h
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -42,8 +42,6 @@ int mtk_memif_set_addr(struct mtk_base_afe *afe, int id,
size_t dma_bytes);
int mtk_memif_set_channel(struct mtk_base_afe *afe,
int id, unsigned int channel);
-int mtk_memif_set_rate(struct mtk_base_afe *afe,
- int id, unsigned int rate);
int mtk_memif_set_rate_substream(struct snd_pcm_substream *substream,
int id, unsigned int rate);
int mtk_memif_set_format(struct mtk_base_afe *afe,
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
index 70ec101890d3..daaca36a2d08 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
@@ -329,61 +329,6 @@ void mt8186_afe_disable_clock(struct mtk_base_afe *afe)
clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
}
-int mt8186_afe_suspend_clock(struct mtk_base_afe *afe)
-{
- struct mt8186_afe_private *afe_priv = afe->platform_priv;
- int ret;
-
- /* set audio int bus to 26M */
- ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
- if (ret) {
- dev_info(afe->dev, "%s clk_prepare_enable %s fail %d\n",
- __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
- goto clk_mux_audio_intbus_err;
- }
- ret = mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M);
- if (ret)
- goto clk_mux_audio_intbus_parent_err;
-
- clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
-
- return 0;
-
-clk_mux_audio_intbus_parent_err:
- mt8186_set_audio_int_bus_parent(afe, CLK_TOP_MAINPLL_D2_D4);
-clk_mux_audio_intbus_err:
- clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
- return ret;
-}
-
-int mt8186_afe_resume_clock(struct mtk_base_afe *afe)
-{
- struct mt8186_afe_private *afe_priv = afe->platform_priv;
- int ret;
-
- /* set audio int bus to normal working clock */
- ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
- if (ret) {
- dev_info(afe->dev, "%s clk_prepare_enable %s fail %d\n",
- __func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
- goto clk_mux_audio_intbus_err;
- }
- ret = mt8186_set_audio_int_bus_parent(afe,
- CLK_TOP_MAINPLL_D2_D4);
- if (ret)
- goto clk_mux_audio_intbus_parent_err;
-
- clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
-
- return 0;
-
-clk_mux_audio_intbus_parent_err:
- mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M);
-clk_mux_audio_intbus_err:
- clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
- return ret;
-}
-
int mt8186_apll1_enable(struct mtk_base_afe *afe)
{
struct mt8186_afe_private *afe_priv = afe->platform_priv;
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h
index a9d59e506d9a..e524833ce780 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.h
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.h
@@ -85,8 +85,6 @@ int mt8186_afe_enable_cgs(struct mtk_base_afe *afe);
void mt8186_afe_disable_cgs(struct mtk_base_afe *afe);
int mt8186_afe_enable_clock(struct mtk_base_afe *afe);
void mt8186_afe_disable_clock(struct mtk_base_afe *afe);
-int mt8186_afe_suspend_clock(struct mtk_base_afe *afe);
-int mt8186_afe_resume_clock(struct mtk_base_afe *afe);
int mt8186_apll1_enable(struct mtk_base_afe *afe);
void mt8186_apll1_disable(struct mtk_base_afe *afe);
diff --git a/sound/soc/qcom/sdw.c b/sound/soc/qcom/sdw.c
index f2eda2ff46c0..1d01b9329e08 100644
--- a/sound/soc/qcom/sdw.c
+++ b/sound/soc/qcom/sdw.c
@@ -23,9 +23,11 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ u32 rx_ch[SDW_MAX_PORTS], tx_ch[SDW_MAX_PORTS];
struct sdw_stream_runtime *sruntime;
struct snd_soc_dai *codec_dai;
- int ret, i;
+ u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+ int ret, i, j;
sruntime = sdw_alloc_stream(cpu_dai->name);
if (!sruntime)
@@ -35,9 +37,35 @@ int qcom_snd_sdw_startup(struct snd_pcm_substream *substream)
ret = snd_soc_dai_set_stream(codec_dai, sruntime,
substream->stream);
if (ret < 0 && ret != -ENOTSUPP) {
- dev_err(rtd->dev, "Failed to set sdw stream on %s\n",
- codec_dai->name);
+ dev_err(rtd->dev, "Failed to set sdw stream on %s\n", codec_dai->name);
goto err_set_stream;
+ } else if (ret == -ENOTSUPP) {
+ /* Ignore unsupported */
+ continue;
+ }
+
+ ret = snd_soc_dai_get_channel_map(codec_dai, &tx_ch_cnt, tx_ch,
+ &rx_ch_cnt, rx_ch);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "Failed to get codec chan map %s\n", codec_dai->name);
+ goto err_set_stream;
+ } else if (ret == -ENOTSUPP) {
+ /* Ignore unsupported */
+ continue;
+ }
+ }
+
+ switch (cpu_dai->id) {
+ case RX_CODEC_DMA_RX_0:
+ case TX_CODEC_DMA_TX_3:
+ if (tx_ch_cnt || rx_ch_cnt) {
+ for_each_rtd_codec_dais(rtd, j, codec_dai) {
+ ret = snd_soc_dai_set_channel_map(codec_dai,
+ tx_ch_cnt, tx_ch,
+ rx_ch_cnt, rx_ch);
+ if (ret != 0 && ret != -ENOTSUPP)
+ goto err_set_stream;
+ }
}
}
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c
index 7f5fcaecee4b..78ab88843f86 100644
--- a/sound/soc/rockchip/rockchip_i2s_tdm.c
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -451,11 +451,11 @@ static int rockchip_i2s_tdm_set_fmt(struct snd_soc_dai *cpu_dai,
break;
case SND_SOC_DAIFMT_DSP_A:
val = I2S_TXCR_TFS_TDM_PCM;
- tdm_val = TDM_SHIFT_CTRL(0);
+ tdm_val = TDM_SHIFT_CTRL(2);
break;
case SND_SOC_DAIFMT_DSP_B:
val = I2S_TXCR_TFS_TDM_PCM;
- tdm_val = TDM_SHIFT_CTRL(2);
+ tdm_val = TDM_SHIFT_CTRL(4);
break;
default:
ret = -EINVAL;
diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c
index b6399b773986..0244cdcdd109 100644
--- a/sound/soc/sdca/sdca_device.c
+++ b/sound/soc/sdca/sdca_device.c
@@ -48,8 +48,7 @@ static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave)
return false;
for (i = 0; i < slave->sdca_data.num_functions; i++) {
- if (slave->sdca_data.sdca_func[i].type ==
- SDCA_FUNCTION_TYPE_SMART_MIC)
+ if (slave->sdca_data.function[i].type == SDCA_FUNCTION_TYPE_SMART_MIC)
return true;
}
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 38071bc838b9..091d55abe109 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -9,7 +9,9 @@
#define dev_fmt(fmt) "%s: " fmt, __func__
#include <linux/acpi.h>
+#include <linux/byteorder/generic.h>
#include <linux/device.h>
+#include <linux/dev_printk.h>
#include <linux/module.h>
#include <linux/property.h>
#include <linux/soundwire/sdw.h>
@@ -17,11 +19,16 @@
#include <sound/sdca.h>
#include <sound/sdca_function.h>
+/*
+ * Should be long enough to encompass all the MIPI DisCo properties.
+ */
+#define SDCA_PROPERTY_LENGTH 64
+
static int patch_sdca_function_type(u32 interface_revision, u32 *function_type)
{
/*
* Unfortunately early SDCA specifications used different indices for Functions,
- * for backwards compatibility we have to reorder the values found
+ * for backwards compatibility we have to reorder the values found.
*/
if (interface_revision < 0x0801) {
switch (*function_type) {
@@ -85,7 +92,7 @@ static int find_sdca_function(struct acpi_device *adev, void *data)
struct fwnode_handle *control5; /* used to identify function type */
const char *function_name;
u32 function_type;
- int func_index;
+ int function_index;
u64 addr;
int ret;
@@ -145,27 +152,1413 @@ static int find_sdca_function(struct acpi_device *adev, void *data)
function_name, function_type, addr);
/* store results */
- func_index = sdca_data->num_functions;
- sdca_data->sdca_func[func_index].adr = addr;
- sdca_data->sdca_func[func_index].type = function_type;
- sdca_data->sdca_func[func_index].name = function_name;
+ function_index = sdca_data->num_functions;
+ sdca_data->function[function_index].adr = addr;
+ sdca_data->function[function_index].type = function_type;
+ sdca_data->function[function_index].name = function_name;
+ sdca_data->function[function_index].node = function_node;
sdca_data->num_functions++;
return 0;
}
+/**
+ * sdca_lookup_functions - Parse sdca_device_desc for each Function
+ * @slave: SoundWire slave device to be processed.
+ *
+ * Iterate through the available SDCA Functions and fill in a short
+ * descriptor (struct sdca_function_desc) for each function, this
+ * information is stored along with the SoundWire slave device and
+ * used for adding drivers and quirks before the devices have fully
+ * probed.
+ */
void sdca_lookup_functions(struct sdw_slave *slave)
{
struct device *dev = &slave->dev;
struct acpi_device *adev = to_acpi_device_node(dev->fwnode);
if (!adev) {
- dev_info(dev, "No matching ACPI device found, ignoring peripheral\n");
+ dev_info(dev, "no matching ACPI device found, ignoring peripheral\n");
return;
}
+
acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
}
EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
+static int find_sdca_init_table(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ struct sdca_init_write *init_write;
+ int write_size = sizeof(init_write->addr) + sizeof(init_write->val);
+ u8 *init_list, *init_iter;
+ int num_init_writes;
+
+ num_init_writes = fwnode_property_count_u8(function_node,
+ "mipi-sdca-function-initialization-table");
+ if (!num_init_writes || num_init_writes == -EINVAL) {
+ return 0;
+ } else if (num_init_writes < 0) {
+ dev_err(dev, "%pfwP: failed to read initialization table: %d\n",
+ function_node, num_init_writes);
+ return num_init_writes;
+ } else if (num_init_writes % write_size != 0) {
+ dev_err(dev, "%pfwP: init table size invalid\n", function_node);
+ return -EINVAL;
+ } else if (num_init_writes > SDCA_MAX_INIT_COUNT) {
+ dev_err(dev, "%pfwP: maximum init table size exceeded\n", function_node);
+ return -EINVAL;
+ }
+
+ init_write = devm_kcalloc(dev, num_init_writes / write_size,
+ sizeof(*init_write), GFP_KERNEL);
+ if (!init_write)
+ return -ENOMEM;
+
+ init_list = kcalloc(num_init_writes, sizeof(*init_list), GFP_KERNEL);
+ if (!init_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u8_array(function_node,
+ "mipi-sdca-function-initialization-table",
+ init_list, num_init_writes);
+
+ function->num_init_table = num_init_writes;
+ function->init_table = init_write;
+
+ for (init_iter = init_list; init_iter < init_list + num_init_writes;) {
+ u32 *addr = (u32 *)init_iter;
+
+ init_write->addr = le32_to_cpu(*addr);
+ init_iter += sizeof(init_write->addr);
+
+ init_write->val = *init_iter;
+ init_iter += sizeof(init_write->val);
+ }
+
+ kfree(init_list);
+
+ return 0;
+}
+
+static const char *find_sdca_control_label(const struct sdca_entity *entity,
+ const struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(IT, MIC_BIAS):
+ return SDCA_CTL_MIC_BIAS_NAME;
+ case SDCA_CTL_TYPE_S(IT, USAGE):
+ case SDCA_CTL_TYPE_S(OT, USAGE):
+ return SDCA_CTL_USAGE_NAME;
+ case SDCA_CTL_TYPE_S(IT, LATENCY):
+ case SDCA_CTL_TYPE_S(OT, LATENCY):
+ case SDCA_CTL_TYPE_S(MU, LATENCY):
+ case SDCA_CTL_TYPE_S(SU, LATENCY):
+ case SDCA_CTL_TYPE_S(FU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, LATENCY):
+ case SDCA_CTL_TYPE_S(CRU, LATENCY):
+ case SDCA_CTL_TYPE_S(UDMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SAPU, LATENCY):
+ case SDCA_CTL_TYPE_S(PPU, LATENCY):
+ return SDCA_CTL_LATENCY_NAME;
+ case SDCA_CTL_TYPE_S(IT, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(CRU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(UDMPU, CLUSTERINDEX):
+ case SDCA_CTL_TYPE_S(MFPU, CLUSTERINDEX):
+ return SDCA_CTL_CLUSTERINDEX_NAME;
+ case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR):
+ return SDCA_CTL_DATAPORT_SELECTOR_NAME;
+ case SDCA_CTL_TYPE_S(IT, MATCHING_GUID):
+ case SDCA_CTL_TYPE_S(OT, MATCHING_GUID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, MATCHING_GUID):
+ return SDCA_CTL_MATCHING_GUID_NAME;
+ case SDCA_CTL_TYPE_S(IT, KEEP_ALIVE):
+ case SDCA_CTL_TYPE_S(OT, KEEP_ALIVE):
+ return SDCA_CTL_KEEP_ALIVE_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_STREAM):
+ case SDCA_CTL_TYPE_S(OT, NDAI_STREAM):
+ return SDCA_CTL_NDAI_STREAM_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_CATEGORY):
+ case SDCA_CTL_TYPE_S(OT, NDAI_CATEGORY):
+ return SDCA_CTL_NDAI_CATEGORY_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_CODINGTYPE):
+ case SDCA_CTL_TYPE_S(OT, NDAI_CODINGTYPE):
+ return SDCA_CTL_NDAI_CODINGTYPE_NAME;
+ case SDCA_CTL_TYPE_S(IT, NDAI_PACKETTYPE):
+ case SDCA_CTL_TYPE_S(OT, NDAI_PACKETTYPE):
+ return SDCA_CTL_NDAI_PACKETTYPE_NAME;
+ case SDCA_CTL_TYPE_S(MU, MIXER):
+ return SDCA_CTL_MIXER_NAME;
+ case SDCA_CTL_TYPE_S(SU, SELECTOR):
+ return SDCA_CTL_SELECTOR_NAME;
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ return SDCA_CTL_MUTE_NAME;
+ case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME):
+ return SDCA_CTL_CHANNEL_VOLUME_NAME;
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ return SDCA_CTL_AGC_NAME;
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ return SDCA_CTL_BASS_BOOST_NAME;
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ return SDCA_CTL_LOUDNESS_NAME;
+ case SDCA_CTL_TYPE_S(FU, GAIN):
+ return SDCA_CTL_GAIN_NAME;
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ return SDCA_CTL_BYPASS_NAME;
+ case SDCA_CTL_TYPE_S(XU, XU_ID):
+ return SDCA_CTL_XU_ID_NAME;
+ case SDCA_CTL_TYPE_S(XU, XU_VERSION):
+ return SDCA_CTL_XU_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_CURRENTOWNER):
+ return SDCA_CTL_FDL_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET):
+ return SDCA_CTL_FDL_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH):
+ return SDCA_CTL_FDL_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_STATUS):
+ return SDCA_CTL_FDL_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_SET_INDEX):
+ return SDCA_CTL_FDL_SET_INDEX_NAME;
+ case SDCA_CTL_TYPE_S(XU, FDL_HOST_REQUEST):
+ return SDCA_CTL_FDL_HOST_REQUEST_NAME;
+ case SDCA_CTL_TYPE_S(CS, CLOCK_VALID):
+ return SDCA_CTL_CLOCK_VALID_NAME;
+ case SDCA_CTL_TYPE_S(CS, SAMPLERATEINDEX):
+ return SDCA_CTL_SAMPLERATEINDEX_NAME;
+ case SDCA_CTL_TYPE_S(CX, CLOCK_SELECT):
+ return SDCA_CTL_CLOCK_SELECT_NAME;
+ case SDCA_CTL_TYPE_S(PDE, REQUESTED_PS):
+ return SDCA_CTL_REQUESTED_PS_NAME;
+ case SDCA_CTL_TYPE_S(PDE, ACTUAL_PS):
+ return SDCA_CTL_ACTUAL_PS_NAME;
+ case SDCA_CTL_TYPE_S(GE, SELECTED_MODE):
+ return SDCA_CTL_SELECTED_MODE_NAME;
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ return SDCA_CTL_DETECTED_MODE_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVATE):
+ return SDCA_CTL_PRIVATE_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_POLICY):
+ return SDCA_CTL_PRIVACY_POLICY_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_LOCKSTATE):
+ return SDCA_CTL_PRIVACY_LOCKSTATE_NAME;
+ case SDCA_CTL_TYPE_S(SPE, PRIVACY_OWNER):
+ return SDCA_CTL_PRIVACY_OWNER_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_CURRENTOWNER):
+ return SDCA_CTL_AUTHTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET):
+ return SDCA_CTL_AUTHTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH):
+ return SDCA_CTL_AUTHTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_CURRENTOWNER):
+ return SDCA_CTL_AUTHRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET):
+ return SDCA_CTL_AUTHRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH):
+ return SDCA_CTL_AUTHRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR):
+ return SDCA_CTL_ACOUSTIC_ENERGY_LEVEL_MONITOR_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN):
+ return SDCA_CTL_ULTRASOUND_LOOP_GAIN_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_0):
+ return SDCA_CTL_OPAQUESET_0_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_1):
+ return SDCA_CTL_OPAQUESET_1_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_2):
+ return SDCA_CTL_OPAQUESET_2_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_3):
+ return SDCA_CTL_OPAQUESET_3_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_4):
+ return SDCA_CTL_OPAQUESET_4_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_5):
+ return SDCA_CTL_OPAQUESET_5_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_6):
+ return SDCA_CTL_OPAQUESET_6_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_7):
+ return SDCA_CTL_OPAQUESET_7_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_8):
+ return SDCA_CTL_OPAQUESET_8_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_9):
+ return SDCA_CTL_OPAQUESET_9_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_10):
+ return SDCA_CTL_OPAQUESET_10_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_11):
+ return SDCA_CTL_OPAQUESET_11_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_12):
+ return SDCA_CTL_OPAQUESET_12_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_13):
+ return SDCA_CTL_OPAQUESET_13_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_14):
+ return SDCA_CTL_OPAQUESET_14_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_15):
+ return SDCA_CTL_OPAQUESET_15_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_16):
+ return SDCA_CTL_OPAQUESET_16_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_17):
+ return SDCA_CTL_OPAQUESET_17_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_18):
+ return SDCA_CTL_OPAQUESET_18_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_19):
+ return SDCA_CTL_OPAQUESET_19_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_20):
+ return SDCA_CTL_OPAQUESET_20_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_21):
+ return SDCA_CTL_OPAQUESET_21_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_22):
+ return SDCA_CTL_OPAQUESET_22_NAME;
+ case SDCA_CTL_TYPE_S(UDMPU, OPAQUESET_23):
+ return SDCA_CTL_OPAQUESET_23_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_READY):
+ return SDCA_CTL_ALGORITHM_READY_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_ENABLE):
+ return SDCA_CTL_ALGORITHM_ENABLE_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ALGORITHM_PREPARE):
+ return SDCA_CTL_ALGORITHM_PREPARE_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, CENTER_FREQUENCY_INDEX):
+ return SDCA_CTL_CENTER_FREQUENCY_INDEX_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL):
+ return SDCA_CTL_ULTRASOUND_LEVEL_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_NUMBER):
+ return SDCA_CTL_AE_NUMBER_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_CURRENTOWNER):
+ return SDCA_CTL_AE_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET):
+ return SDCA_CTL_AE_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH):
+ return SDCA_CTL_AE_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_ENABLE):
+ return SDCA_CTL_TRIGGER_ENABLE_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_STATUS):
+ return SDCA_CTL_TRIGGER_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_MODE):
+ return SDCA_CTL_HIST_BUFFER_MODE_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_BUFFER_PREAMBLE):
+ return SDCA_CTL_HIST_BUFFER_PREAMBLE_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_ERROR):
+ return SDCA_CTL_HIST_ERROR_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_EXTENSION):
+ return SDCA_CTL_TRIGGER_EXTENSION_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, TRIGGER_READY):
+ return SDCA_CTL_TRIGGER_READY_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_CURRENTOWNER):
+ return SDCA_CTL_HIST_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET):
+ return SDCA_CTL_HIST_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH):
+ return SDCA_CTL_HIST_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_CURRENTOWNER):
+ return SDCA_CTL_DTODTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH):
+ return SDCA_CTL_DTODTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_CURRENTOWNER):
+ return SDCA_CTL_DTODRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH):
+ return SDCA_CTL_DTODRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, PROTECTION_MODE):
+ return SDCA_CTL_PROTECTION_MODE_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, PROTECTION_STATUS):
+ return SDCA_CTL_PROTECTION_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, OPAQUESETREQ_INDEX):
+ return SDCA_CTL_OPAQUESETREQ_INDEX_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_CURRENTOWNER):
+ return SDCA_CTL_DTODTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH):
+ return SDCA_CTL_DTODTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_CURRENTOWNER):
+ return SDCA_CTL_DTODRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET):
+ return SDCA_CTL_DTODRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH):
+ return SDCA_CTL_DTODRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(PPU, POSTURENUMBER):
+ return SDCA_CTL_POSTURENUMBER_NAME;
+ case SDCA_CTL_TYPE_S(PPU, POSTUREEXTENSION):
+ return SDCA_CTL_POSTUREEXTENSION_NAME;
+ case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE):
+ return SDCA_CTL_HORIZONTALBALANCE_NAME;
+ case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE):
+ return SDCA_CTL_VERTICALBALANCE_NAME;
+ case SDCA_CTL_TYPE_S(TG, TONE_DIVIDER):
+ return SDCA_CTL_TONE_DIVIDER_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_CURRENTOWNER):
+ return SDCA_CTL_HIDTX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET):
+ return SDCA_CTL_HIDTX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH):
+ return SDCA_CTL_HIDTX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_CURRENTOWNER):
+ return SDCA_CTL_HIDRX_CURRENTOWNER_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET):
+ return SDCA_CTL_HIDRX_MESSAGEOFFSET_NAME;
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH):
+ return SDCA_CTL_HIDRX_MESSAGELENGTH_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, COMMIT_GROUP_MASK):
+ return SDCA_CTL_COMMIT_GROUP_MASK_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_SDCA_VERSION):
+ return SDCA_CTL_FUNCTION_SDCA_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_TYPE):
+ return SDCA_CTL_FUNCTION_TYPE_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID):
+ return SDCA_CTL_FUNCTION_MANUFACTURER_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID):
+ return SDCA_CTL_FUNCTION_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_VERSION):
+ return SDCA_CTL_FUNCTION_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID):
+ return SDCA_CTL_FUNCTION_EXTENSION_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_VERSION):
+ return SDCA_CTL_FUNCTION_EXTENSION_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_STATUS):
+ return SDCA_CTL_FUNCTION_STATUS_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ACTION):
+ return SDCA_CTL_FUNCTION_ACTION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID):
+ return SDCA_CTL_DEVICE_MANUFACTURER_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID):
+ return SDCA_CTL_DEVICE_PART_ID_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_VERSION):
+ return SDCA_CTL_DEVICE_VERSION_NAME;
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_SDCA_VERSION):
+ return SDCA_CTL_DEVICE_SDCA_VERSION_NAME;
+ default:
+ return NULL;
+ }
+}
+
+static unsigned int find_sdca_control_bits(const struct sdca_entity *entity,
+ const struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(IT, LATENCY):
+ case SDCA_CTL_TYPE_S(OT, LATENCY):
+ case SDCA_CTL_TYPE_S(MU, LATENCY):
+ case SDCA_CTL_TYPE_S(SU, LATENCY):
+ case SDCA_CTL_TYPE_S(FU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, LATENCY):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(XU, FDL_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SPE, AUTHRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(CRU, LATENCY):
+ case SDCA_CTL_TYPE_S(UDMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, LATENCY):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(MFPU, AE_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, HIST_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SMPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, LATENCY):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(SAPU, DTODRX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(PPU, LATENCY):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDTX_MESSAGELENGTH):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGEOFFSET):
+ case SDCA_CTL_TYPE_S(HIDE, HIDRX_MESSAGELENGTH):
+ return 32;
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_MANUFACTURER_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, FUNCTION_EXTENSION_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_MANUFACTURER_ID):
+ case SDCA_CTL_TYPE_S(ENTITY_0, DEVICE_PART_ID):
+ case SDCA_CTL_TYPE_S(IT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(OT, DATAPORT_SELECTOR):
+ case SDCA_CTL_TYPE_S(MU, MIXER):
+ case SDCA_CTL_TYPE_S(FU, CHANNEL_VOLUME):
+ case SDCA_CTL_TYPE_S(FU, GAIN):
+ case SDCA_CTL_TYPE_S(XU, XU_ID):
+ case SDCA_CTL_TYPE_S(UDMPU, ACOUSTIC_ENERGY_LEVEL_MONITOR):
+ case SDCA_CTL_TYPE_S(UDMPU, ULTRASOUND_LOOP_GAIN):
+ case SDCA_CTL_TYPE_S(MFPU, ULTRASOUND_LEVEL):
+ case SDCA_CTL_TYPE_S(PPU, HORIZONTALBALANCE):
+ case SDCA_CTL_TYPE_S(PPU, VERTICALBALANCE):
+ return 16;
+ case SDCA_CTL_TYPE_S(FU, MUTE):
+ case SDCA_CTL_TYPE_S(FU, AGC):
+ case SDCA_CTL_TYPE_S(FU, BASS_BOOST):
+ case SDCA_CTL_TYPE_S(FU, LOUDNESS):
+ case SDCA_CTL_TYPE_S(XU, BYPASS):
+ case SDCA_CTL_TYPE_S(MFPU, BYPASS):
+ return 1;
+ default:
+ return 8;
+ }
+}
+
+static int find_sdca_control_range(struct device *dev,
+ struct fwnode_handle *control_node,
+ struct sdca_control_range *range)
+{
+ u8 *range_list;
+ int num_range;
+ u16 *limits;
+ int i;
+
+ num_range = fwnode_property_count_u8(control_node, "mipi-sdca-control-range");
+ if (!num_range || num_range == -EINVAL)
+ return 0;
+ else if (num_range < 0)
+ return num_range;
+
+ range_list = devm_kcalloc(dev, num_range, sizeof(*range_list), GFP_KERNEL);
+ if (!range_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u8_array(control_node, "mipi-sdca-control-range",
+ range_list, num_range);
+
+ limits = (u16 *)range_list;
+
+ range->cols = le16_to_cpu(limits[0]);
+ range->rows = le16_to_cpu(limits[1]);
+ range->data = (u32 *)&limits[2];
+
+ num_range = (num_range - (2 * sizeof(*limits))) / sizeof(*range->data);
+ if (num_range != range->cols * range->rows)
+ return -EINVAL;
+
+ for (i = 0; i < num_range; i++)
+ range->data[i] = le32_to_cpu(range->data[i]);
+
+ return 0;
+}
+
+/*
+ * TODO: Add support for -cn- properties, allowing different channels to have
+ * different defaults etc.
+ */
+static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity,
+ struct fwnode_handle *control_node,
+ struct sdca_control *control)
+{
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-access-mode", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: access mode missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->mode = tmp;
+
+ ret = fwnode_property_read_u32(control_node, "mipi-sdca-control-access-layer", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: access layer missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->layers = tmp;
+
+ switch (control->mode) {
+ case SDCA_ACCESS_MODE_DC:
+ ret = fwnode_property_read_u32(control_node,
+ "mipi-sdca-control-dc-value",
+ &tmp);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: dc value missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ control->value = tmp;
+ control->has_fixed = true;
+ break;
+ case SDCA_ACCESS_MODE_RW:
+ case SDCA_ACCESS_MODE_DUAL:
+ ret = fwnode_property_read_u32(control_node,
+ "mipi-sdca-control-default-value",
+ &tmp);
+ if (!ret) {
+ control->value = tmp;
+ control->has_default = true;
+ }
+
+ ret = fwnode_property_read_u32(control_node,
+ "mipi-sdca-control-fixed-value",
+ &tmp);
+ if (!ret) {
+ if (control->has_default && control->value != tmp) {
+ dev_err(dev,
+ "%s: control %#x: default and fixed value don't match\n",
+ entity->label, control->sel);
+ return -EINVAL;
+ }
+
+ control->value = tmp;
+ control->has_fixed = true;
+ }
+
+ control->deferrable = fwnode_property_read_bool(control_node,
+ "mipi-sdca-control-deferrable");
+ break;
+ default:
+ break;
+ }
+
+ ret = find_sdca_control_range(dev, control_node, &control->range);
+ if (ret) {
+ dev_err(dev, "%s: control %#x: range missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ ret = fwnode_property_read_u64(control_node, "mipi-sdca-control-cn-list",
+ &control->cn_list);
+ if (ret == -EINVAL) {
+ /* Spec allows not specifying cn-list if only the first number is used */
+ control->cn_list = 0x1;
+ } else if (ret || !control->cn_list) {
+ dev_err(dev, "%s: control %#x: cn list missing: %d\n",
+ entity->label, control->sel, ret);
+ return ret;
+ }
+
+ ret = fwnode_property_read_u32(control_node,
+ "mipi-sdca-control-interrupt-position",
+ &tmp);
+ if (!ret)
+ control->interrupt_position = tmp;
+
+ control->label = find_sdca_control_label(entity, control);
+ if (!control->label) {
+ dev_err(dev, "%s: control %#x: name not found\n",
+ entity->label, control->sel);
+ return -EINVAL;
+ }
+
+ control->nbits = find_sdca_control_bits(entity, control);
+
+ dev_info(dev, "%s: %s: control %#x mode %#x layers %#x cn %#llx int %d value %#x %s\n",
+ entity->label, control->label, control->sel,
+ control->mode, control->layers, control->cn_list,
+ control->interrupt_position, control->value,
+ control->deferrable ? "deferrable" : "");
+
+ return 0;
+}
+
+static int find_sdca_entity_controls(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_control *controls;
+ int num_controls;
+ u64 control_list;
+ int control_sel;
+ int i, ret;
+
+ ret = fwnode_property_read_u64(entity_node, "mipi-sdca-control-list", &control_list);
+ if (ret == -EINVAL) {
+ /* Allow missing control lists, assume no controls. */
+ dev_warn(dev, "%s: missing control list\n", entity->label);
+ return 0;
+ } else if (ret) {
+ dev_err(dev, "%s: failed to read control list: %d\n", entity->label, ret);
+ return ret;
+ } else if (!control_list) {
+ return 0;
+ }
+
+ num_controls = hweight64(control_list);
+ controls = devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_set_bit(control_sel, (unsigned long *)&control_list,
+ BITS_PER_TYPE(control_list)) {
+ struct fwnode_handle *control_node;
+ char control_property[SDCA_PROPERTY_LENGTH];
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(control_property, sizeof(control_property),
+ "mipi-sdca-control-0x%X-subproperties", control_sel);
+
+ control_node = fwnode_get_named_child_node(entity_node, control_property);
+ if (!control_node) {
+ dev_err(dev, "%s: control node %s not found\n",
+ entity->label, control_property);
+ return -EINVAL;
+ }
+
+ controls[i].sel = control_sel;
+
+ ret = find_sdca_entity_control(dev, entity, control_node, &controls[i]);
+ fwnode_handle_put(control_node);
+ if (ret)
+ return ret;
+
+ i++;
+ }
+
+ entity->num_controls = num_controls;
+ entity->controls = controls;
+
+ return 0;
+}
+
+static bool find_sdca_iot_dataport(struct sdca_entity_iot *terminal)
+{
+ switch (terminal->type) {
+ case SDCA_TERM_TYPE_GENERIC:
+ case SDCA_TERM_TYPE_ULTRASOUND:
+ case SDCA_TERM_TYPE_CAPTURE_DIRECT_PCM_MIC:
+ case SDCA_TERM_TYPE_RAW_PDM_MIC:
+ case SDCA_TERM_TYPE_SPEECH:
+ case SDCA_TERM_TYPE_VOICE:
+ case SDCA_TERM_TYPE_SECONDARY_PCM_MIC:
+ case SDCA_TERM_TYPE_ACOUSTIC_CONTEXT_AWARENESS:
+ case SDCA_TERM_TYPE_DTOD_STREAM:
+ case SDCA_TERM_TYPE_REFERENCE_STREAM:
+ case SDCA_TERM_TYPE_SENSE_CAPTURE:
+ case SDCA_TERM_TYPE_STREAMING_MIC:
+ case SDCA_TERM_TYPE_OPTIMIZATION_STREAM:
+ case SDCA_TERM_TYPE_PDM_RENDER_STREAM:
+ case SDCA_TERM_TYPE_COMPANION_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int find_sdca_entity_iot(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_iot *terminal = &entity->iot;
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-terminal-type", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: terminal type missing: %d\n", entity->label, ret);
+ return ret;
+ }
+
+ terminal->type = tmp;
+ terminal->is_dataport = find_sdca_iot_dataport(terminal);
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-terminal-reference-number", &tmp);
+ if (!ret)
+ terminal->reference = tmp;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-terminal-connector-type", &tmp);
+ if (!ret)
+ terminal->connector = tmp;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-terminal-transducer-count", &tmp);
+ if (!ret)
+ terminal->num_transducer = tmp;
+
+ dev_info(dev, "%s: terminal type %#x ref %#x conn %#x count %d\n",
+ entity->label, terminal->type, terminal->reference,
+ terminal->connector, terminal->num_transducer);
+
+ return 0;
+}
+
+static int find_sdca_entity_cs(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_cs *clock = &entity->cs;
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-cs-type", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: clock type missing: %d\n", entity->label, ret);
+ return ret;
+ }
+
+ clock->type = tmp;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-clock-valid-max-delay", &tmp);
+ if (!ret)
+ clock->max_delay = tmp;
+
+ dev_info(dev, "%s: clock type %#x delay %d\n", entity->label,
+ clock->type, clock->max_delay);
+
+ return 0;
+}
+
+static int find_sdca_entity_pde(struct device *dev,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ static const int mult_delay = 3;
+ struct sdca_entity_pde *power = &entity->pde;
+ struct sdca_pde_delay *delays;
+ int num_delays;
+ u32 *delay_list;
+ int i, j;
+
+ num_delays = fwnode_property_count_u32(entity_node,
+ "mipi-sdca-powerdomain-transition-max-delay");
+ if (num_delays <= 0) {
+ dev_err(dev, "%s: max delay list missing: %d\n",
+ entity->label, num_delays);
+ return -EINVAL;
+ } else if (num_delays % mult_delay != 0) {
+ dev_err(dev, "%s: delays not multiple of %d\n",
+ entity->label, mult_delay);
+ return -EINVAL;
+ } else if (num_delays > SDCA_MAX_DELAY_COUNT) {
+ dev_err(dev, "%s: maximum number of transition delays exceeded\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ /* There are 3 values per delay */
+ delays = devm_kcalloc(dev, num_delays / mult_delay,
+ sizeof(*delays), GFP_KERNEL);
+ if (!delays)
+ return -ENOMEM;
+
+ delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL);
+ if (!delay_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-powerdomain-transition-max-delay",
+ delay_list, num_delays);
+
+ num_delays /= mult_delay;
+
+ for (i = 0, j = 0; i < num_delays; i++) {
+ delays[i].from_ps = delay_list[j++];
+ delays[i].to_ps = delay_list[j++];
+ delays[i].us = delay_list[j++];
+
+ dev_info(dev, "%s: from %#x to %#x delay %dus\n", entity->label,
+ delays[i].from_ps, delays[i].to_ps, delays[i].us);
+ }
+
+ power->num_max_delay = num_delays;
+ power->max_delay = delays;
+
+ kfree(delay_list);
+
+ return 0;
+}
+
+static int find_sdca_entity(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_string(entity_node, "mipi-sdca-entity-label",
+ &entity->label);
+ if (ret) {
+ dev_err(dev, "%pfwP: entity %#x: label missing: %d\n",
+ function_node, entity->id, ret);
+ return ret;
+ }
+
+ ret = fwnode_property_read_u32(entity_node, "mipi-sdca-entity-type", &tmp);
+ if (ret) {
+ dev_err(dev, "%s: type missing: %d\n", entity->label, ret);
+ return ret;
+ }
+
+ entity->type = tmp;
+
+ dev_info(dev, "%s: entity %#x type %#x\n",
+ entity->label, entity->id, entity->type);
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ ret = find_sdca_entity_iot(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_CS:
+ ret = find_sdca_entity_cs(dev, entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ ret = find_sdca_entity_pde(dev, entity_node, entity);
+ break;
+ default:
+ break;
+ }
+ if (ret)
+ return ret;
+
+ ret = find_sdca_entity_controls(dev, entity_node, entity);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int find_sdca_entities(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ struct sdca_entity *entities;
+ u32 *entity_list;
+ int num_entities;
+ int i, ret;
+
+ num_entities = fwnode_property_count_u32(function_node,
+ "mipi-sdca-entity-id-list");
+ if (num_entities <= 0) {
+ dev_err(dev, "%pfwP: entity id list missing: %d\n",
+ function_node, num_entities);
+ return -EINVAL;
+ } else if (num_entities > SDCA_MAX_ENTITY_COUNT) {
+ dev_err(dev, "%pfwP: maximum number of entities exceeded\n",
+ function_node);
+ return -EINVAL;
+ }
+
+ /* Add 1 to make space for Entity 0 */
+ entities = devm_kcalloc(dev, num_entities + 1, sizeof(*entities), GFP_KERNEL);
+ if (!entities)
+ return -ENOMEM;
+
+ entity_list = kcalloc(num_entities, sizeof(*entity_list), GFP_KERNEL);
+ if (!entity_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(function_node, "mipi-sdca-entity-id-list",
+ entity_list, num_entities);
+
+ for (i = 0; i < num_entities; i++)
+ entities[i].id = entity_list[i];
+
+ kfree(entity_list);
+
+ /* now read subproperties */
+ for (i = 0; i < num_entities; i++) {
+ char entity_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *entity_node;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(entity_property, sizeof(entity_property),
+ "mipi-sdca-entity-id-0x%X-subproperties", entities[i].id);
+
+ entity_node = fwnode_get_named_child_node(function_node, entity_property);
+ if (!entity_node) {
+ dev_err(dev, "%pfwP: entity node %s not found\n",
+ function_node, entity_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_entity(dev, function_node, entity_node, &entities[i]);
+ fwnode_handle_put(entity_node);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Add Entity 0 at end of the array, makes it easy to skip during
+ * all the Entity searches involved in creating connections.
+ */
+ entities[num_entities].label = "entity0";
+
+ ret = find_sdca_entity_controls(dev, function_node, &entities[num_entities]);
+ if (ret)
+ return ret;
+
+ function->num_entities = num_entities + 1;
+ function->entities = entities;
+
+ return 0;
+}
+
+static struct sdca_entity *find_sdca_entity_by_label(struct sdca_function_data *function,
+ const char *entity_label)
+{
+ int i;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ if (!strcmp(entity->label, entity_label))
+ return entity;
+ }
+
+ return NULL;
+}
+
+static struct sdca_entity *find_sdca_entity_by_id(struct sdca_function_data *function,
+ const int id)
+{
+ int i;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ if (entity->id == id)
+ return entity;
+ }
+
+ return NULL;
+}
+
+static int find_sdca_entity_connection_iot(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_iot *terminal = &entity->iot;
+ struct fwnode_handle *clock_node;
+ struct sdca_entity *clock_entity;
+ const char *clock_label;
+ int ret;
+
+ clock_node = fwnode_get_named_child_node(entity_node,
+ "mipi-sdca-terminal-clock-connection");
+ if (!clock_node)
+ return 0;
+
+ ret = fwnode_property_read_string(clock_node, "mipi-sdca-entity-label",
+ &clock_label);
+ if (ret) {
+ dev_err(dev, "%s: clock label missing: %d\n", entity->label, ret);
+ fwnode_handle_put(clock_node);
+ return ret;
+ }
+
+ clock_entity = find_sdca_entity_by_label(function, clock_label);
+ if (!clock_entity) {
+ dev_err(dev, "%s: failed to find clock with label %s\n",
+ entity->label, clock_label);
+ fwnode_handle_put(clock_node);
+ return -EINVAL;
+ }
+
+ terminal->clock = clock_entity;
+
+ dev_info(dev, "%s -> %s\n", clock_entity->label, entity->label);
+
+ fwnode_handle_put(clock_node);
+ return 0;
+}
+
+static int find_sdca_entity_connection_pde(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity_pde *power = &entity->pde;
+ struct sdca_entity **managed;
+ u32 *managed_list;
+ int num_managed;
+ int i;
+
+ num_managed = fwnode_property_count_u32(entity_node,
+ "mipi-sdca-powerdomain-managed-list");
+ if (!num_managed) {
+ return 0;
+ } else if (num_managed < 0) {
+ dev_err(dev, "%s: managed list missing: %d\n", entity->label, num_managed);
+ return num_managed;
+ } else if (num_managed > SDCA_MAX_ENTITY_COUNT) {
+ dev_err(dev, "%s: maximum number of managed entities exceeded\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ managed = devm_kcalloc(dev, num_managed, sizeof(*managed), GFP_KERNEL);
+ if (!managed)
+ return -ENOMEM;
+
+ managed_list = kcalloc(num_managed, sizeof(*managed_list), GFP_KERNEL);
+ if (!managed_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-powerdomain-managed-list",
+ managed_list, num_managed);
+
+ for (i = 0; i < num_managed; i++) {
+ managed[i] = find_sdca_entity_by_id(function, managed_list[i]);
+ if (!managed[i]) {
+ dev_err(dev, "%s: failed to find entity with id %#x\n",
+ entity->label, managed_list[i]);
+ kfree(managed_list);
+ return -EINVAL;
+ }
+
+ dev_info(dev, "%s -> %s\n", managed[i]->label, entity->label);
+ }
+
+ kfree(managed_list);
+
+ power->num_managed = num_managed;
+ power->managed = managed;
+
+ return 0;
+}
+
+static int find_sdca_entity_connection(struct device *dev,
+ struct sdca_function_data *function,
+ struct fwnode_handle *entity_node,
+ struct sdca_entity *entity)
+{
+ struct sdca_entity **pins;
+ int num_pins, pin;
+ u64 pin_list;
+ int i, ret;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ ret = find_sdca_entity_connection_iot(dev, function,
+ entity_node, entity);
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ ret = find_sdca_entity_connection_pde(dev, function,
+ entity_node, entity);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret)
+ return ret;
+
+ ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list);
+ if (ret == -EINVAL) {
+ /* Allow missing pin lists, assume no pins. */
+ dev_warn(dev, "%s: missing pin list\n", entity->label);
+ return 0;
+ } else if (ret) {
+ dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret);
+ return ret;
+ } else if (pin_list & BIT(0)) {
+ /*
+ * Each bit set in the pin-list refers to an entity_id in this
+ * Function. Entity 0 is an illegal connection since it is used
+ * for Function-level configurations.
+ */
+ dev_err(dev, "%s: pin 0 used as input\n", entity->label);
+ return -EINVAL;
+ } else if (!pin_list) {
+ return 0;
+ }
+
+ num_pins = hweight64(pin_list);
+ pins = devm_kcalloc(dev, num_pins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_set_bit(pin, (unsigned long *)&pin_list, BITS_PER_TYPE(pin_list)) {
+ char pin_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *connected_node;
+ struct sdca_entity *connected_entity;
+ const char *connected_label;
+
+ snprintf(pin_property, sizeof(pin_property), "mipi-sdca-input-pin-%d", pin);
+
+ connected_node = fwnode_get_named_child_node(entity_node, pin_property);
+ if (!connected_node) {
+ dev_err(dev, "%s: pin node %s not found\n",
+ entity->label, pin_property);
+ return -EINVAL;
+ }
+
+ ret = fwnode_property_read_string(connected_node, "mipi-sdca-entity-label",
+ &connected_label);
+ if (ret) {
+ dev_err(dev, "%s: pin %d label missing: %d\n",
+ entity->label, pin, ret);
+ fwnode_handle_put(connected_node);
+ return ret;
+ }
+
+ connected_entity = find_sdca_entity_by_label(function, connected_label);
+ if (!connected_entity) {
+ dev_err(dev, "%s: failed to find entity with label %s\n",
+ entity->label, connected_label);
+ fwnode_handle_put(connected_node);
+ return -EINVAL;
+ }
+
+ pins[i] = connected_entity;
+
+ dev_info(dev, "%s -> %s\n", connected_entity->label, entity->label);
+
+ i++;
+ fwnode_handle_put(connected_node);
+ }
+
+ entity->num_sources = num_pins;
+ entity->sources = pins;
+
+ return 0;
+}
+
+static int find_sdca_connections(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ int i;
+
+ /* Entity 0 cannot have connections */
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+ char entity_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *entity_node;
+ int ret;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(entity_property, sizeof(entity_property),
+ "mipi-sdca-entity-id-0x%X-subproperties",
+ entity->id);
+
+ entity_node = fwnode_get_named_child_node(function_node, entity_property);
+ if (!entity_node) {
+ dev_err(dev, "%pfwP: entity node %s not found\n",
+ function_node, entity_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_entity_connection(dev, function, entity_node, entity);
+ fwnode_handle_put(entity_node);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int find_sdca_cluster_channel(struct device *dev,
+ struct sdca_cluster *cluster,
+ struct fwnode_handle *channel_node,
+ struct sdca_channel *channel)
+{
+ u32 tmp;
+ int ret;
+
+ ret = fwnode_property_read_u32(channel_node, "mipi-sdca-cluster-channel-id", &tmp);
+ if (ret) {
+ dev_err(dev, "cluster %#x: missing channel id: %d\n",
+ cluster->id, ret);
+ return ret;
+ }
+
+ channel->id = tmp;
+
+ ret = fwnode_property_read_u32(channel_node,
+ "mipi-sdca-cluster-channel-purpose",
+ &tmp);
+ if (ret) {
+ dev_err(dev, "cluster %#x: channel %#x: missing purpose: %d\n",
+ cluster->id, channel->id, ret);
+ return ret;
+ }
+
+ channel->purpose = tmp;
+
+ ret = fwnode_property_read_u32(channel_node,
+ "mipi-sdca-cluster-channel-relationship",
+ &tmp);
+ if (ret) {
+ dev_err(dev, "cluster %#x: channel %#x: missing relationship: %d\n",
+ cluster->id, channel->id, ret);
+ return ret;
+ }
+
+ channel->relationship = tmp;
+
+ dev_info(dev, "cluster %#x: channel id %#x purpose %#x relationship %#x\n",
+ cluster->id, channel->id, channel->purpose, channel->relationship);
+
+ return 0;
+}
+
+static int find_sdca_cluster_channels(struct device *dev,
+ struct fwnode_handle *cluster_node,
+ struct sdca_cluster *cluster)
+{
+ struct sdca_channel *channels;
+ u32 num_channels;
+ int i, ret;
+
+ ret = fwnode_property_read_u32(cluster_node, "mipi-sdca-channel-count",
+ &num_channels);
+ if (ret < 0) {
+ dev_err(dev, "cluster %#x: failed to read channel list: %d\n",
+ cluster->id, ret);
+ return ret;
+ } else if (num_channels > SDCA_MAX_CHANNEL_COUNT) {
+ dev_err(dev, "cluster %#x: maximum number of channels exceeded\n",
+ cluster->id);
+ return -EINVAL;
+ }
+
+ channels = devm_kcalloc(dev, num_channels, sizeof(*channels), GFP_KERNEL);
+ if (!channels)
+ return -ENOMEM;
+
+ for (i = 0; i < num_channels; i++) {
+ char channel_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *channel_node;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(channel_property, sizeof(channel_property),
+ "mipi-sdca-channel-%d-subproperties", i + 1);
+
+ channel_node = fwnode_get_named_child_node(cluster_node, channel_property);
+ if (!channel_node) {
+ dev_err(dev, "cluster %#x: channel node %s not found\n",
+ cluster->id, channel_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_cluster_channel(dev, cluster, channel_node, &channels[i]);
+ fwnode_handle_put(channel_node);
+ if (ret)
+ return ret;
+ }
+
+ cluster->num_channels = num_channels;
+ cluster->channels = channels;
+
+ return 0;
+}
+
+static int find_sdca_clusters(struct device *dev,
+ struct fwnode_handle *function_node,
+ struct sdca_function_data *function)
+{
+ struct sdca_cluster *clusters;
+ int num_clusters;
+ u32 *cluster_list;
+ int i, ret;
+
+ num_clusters = fwnode_property_count_u32(function_node, "mipi-sdca-cluster-id-list");
+ if (!num_clusters || num_clusters == -EINVAL) {
+ return 0;
+ } else if (num_clusters < 0) {
+ dev_err(dev, "%pfwP: failed to read cluster id list: %d\n",
+ function_node, num_clusters);
+ return num_clusters;
+ } else if (num_clusters > SDCA_MAX_CLUSTER_COUNT) {
+ dev_err(dev, "%pfwP: maximum number of clusters exceeded\n", function_node);
+ return -EINVAL;
+ }
+
+ clusters = devm_kcalloc(dev, num_clusters, sizeof(*clusters), GFP_KERNEL);
+ if (!clusters)
+ return -ENOMEM;
+
+ cluster_list = kcalloc(num_clusters, sizeof(*cluster_list), GFP_KERNEL);
+ if (!cluster_list)
+ return -ENOMEM;
+
+ fwnode_property_read_u32_array(function_node, "mipi-sdca-cluster-id-list",
+ cluster_list, num_clusters);
+
+ for (i = 0; i < num_clusters; i++)
+ clusters[i].id = cluster_list[i];
+
+ kfree(cluster_list);
+
+ /* now read subproperties */
+ for (i = 0; i < num_clusters; i++) {
+ char cluster_property[SDCA_PROPERTY_LENGTH];
+ struct fwnode_handle *cluster_node;
+
+ /* DisCo uses upper-case for hex numbers */
+ snprintf(cluster_property, sizeof(cluster_property),
+ "mipi-sdca-cluster-id-0x%X-subproperties", clusters[i].id);
+
+ cluster_node = fwnode_get_named_child_node(function_node, cluster_property);
+ if (!cluster_node) {
+ dev_err(dev, "%pfwP: cluster node %s not found\n",
+ function_node, cluster_property);
+ return -EINVAL;
+ }
+
+ ret = find_sdca_cluster_channels(dev, cluster_node, &clusters[i]);
+ fwnode_handle_put(cluster_node);
+ if (ret)
+ return ret;
+ }
+
+ function->num_clusters = num_clusters;
+ function->clusters = clusters;
+
+ return 0;
+}
+
+/**
+ * sdca_parse_function - parse ACPI DisCo for a Function
+ * @dev: Pointer to device against which function data will be allocated.
+ * @function_desc: Pointer to the Function short descriptor.
+ * @function: Pointer to the Function information, to be populated.
+ *
+ * Return: Returns 0 for success.
+ */
+int sdca_parse_function(struct device *dev,
+ struct sdca_function_desc *function_desc,
+ struct sdca_function_data *function)
+{
+ u32 tmp;
+ int ret;
+
+ function->desc = function_desc;
+
+ ret = fwnode_property_read_u32(function_desc->node,
+ "mipi-sdca-function-busy-max-delay", &tmp);
+ if (!ret)
+ function->busy_max_delay = tmp;
+
+ dev_info(dev, "%pfwP: name %s delay %dus\n", function->desc->node,
+ function->desc->name, function->busy_max_delay);
+
+ ret = find_sdca_init_table(dev, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ ret = find_sdca_entities(dev, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ ret = find_sdca_connections(dev, function_desc->node, function);
+ if (ret)
+ return ret;
+
+ ret = find_sdca_clusters(dev, function_desc->node, function);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA");
+
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SDCA library");
diff --git a/sound/soc/soc-card.c b/sound/soc/soc-card.c
index e6eb71b3010a..235427d69061 100644
--- a/sound/soc/soc-card.c
+++ b/sound/soc/soc-card.c
@@ -15,18 +15,8 @@
static inline int _soc_card_ret(struct snd_soc_card *card,
const char *func, int ret)
{
- switch (ret) {
- case -EPROBE_DEFER:
- case -ENOTSUPP:
- case 0:
- break;
- default:
- dev_err(card->dev,
- "ASoC: error at %s on %s: %d\n",
- func, card->name, ret);
- }
-
- return ret;
+ return snd_soc_ret(card->dev, ret,
+ "at %s() on %s\n", func, card->name);
}
struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index b67ef78f405c..25f5e543ae8d 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -13,32 +13,20 @@
#include <sound/soc.h>
#include <linux/bitops.h>
-#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret, -1)
-#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret(dai, __func__, ret, reg)
-static inline int _soc_component_ret(struct snd_soc_component *component,
- const char *func, int ret, int reg)
-{
- /* Positive/Zero values are not errors */
- if (ret >= 0)
- return ret;
-
- /* Negative values might be errors */
- switch (ret) {
- case -EPROBE_DEFER:
- case -ENOTSUPP:
- break;
- default:
- if (reg == -1)
- dev_err(component->dev,
- "ASoC: error at %s on %s: %d\n",
- func, component->name, ret);
- else
- dev_err(component->dev,
- "ASoC: error at %s on %s for register: [0x%08x] %d\n",
- func, component->name, reg, ret);
- }
+#define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret)
+static inline int _soc_component_ret(struct snd_soc_component *component, const char *func, int ret)
+{
+ return snd_soc_ret(component->dev, ret,
+ "at %s() on %s\n", func, component->name);
+}
- return ret;
+#define soc_component_ret_reg_rw(dai, ret, reg) _soc_component_ret_reg_rw(dai, __func__, ret, reg)
+static inline int _soc_component_ret_reg_rw(struct snd_soc_component *component,
+ const char *func, int ret, int reg)
+{
+ return snd_soc_ret(component->dev, ret,
+ "at %s() on %s for register: [0x%08x]\n",
+ func, component->name, reg);
}
static inline int soc_component_field_shift(struct snd_soc_component *component,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 3c6d8aef4130..26b34b688508 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3046,7 +3046,7 @@ int snd_soc_of_parse_pin_switches(struct snd_soc_card *card, const char *prop)
unsigned int i, nb_controls;
int ret;
- if (!of_property_read_bool(dev->of_node, prop))
+ if (!of_property_present(dev->of_node, prop))
return 0;
strings = devm_kcalloc(dev, nb_controls_max,
@@ -3120,23 +3120,17 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np,
if (rx_mask)
snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
- if (of_property_read_bool(np, "dai-tdm-slot-num")) {
- ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
- if (ret)
- return ret;
-
- if (slots)
- *slots = val;
- }
-
- if (of_property_read_bool(np, "dai-tdm-slot-width")) {
- ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
- if (ret)
- return ret;
+ ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
+ if (ret && ret != -EINVAL)
+ return ret;
+ if (!ret && slots)
+ *slots = val;
- if (slot_width)
- *slot_width = val;
- }
+ ret = of_property_read_u32(np, "dai-tdm-slot-width", &val);
+ if (ret && ret != -EINVAL)
+ return ret;
+ if (!ret && slot_width)
+ *slot_width = val;
return 0;
}
@@ -3403,12 +3397,12 @@ unsigned int snd_soc_daifmt_parse_clock_provider_raw(struct device_node *np,
* check "[prefix]frame-master"
*/
snprintf(prop, sizeof(prop), "%sbitclock-master", prefix);
- bit = of_property_read_bool(np, prop);
+ bit = of_property_present(np, prop);
if (bit && bitclkmaster)
*bitclkmaster = of_parse_phandle(np, prop, 0);
snprintf(prop, sizeof(prop), "%sframe-master", prefix);
- frame = of_property_read_bool(np, prop);
+ frame = of_property_present(np, prop);
if (frame && framemaster)
*framemaster = of_parse_phandle(np, prop, 0);
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index ca0308f6d41c..7c4c9127e5f3 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -14,22 +14,8 @@
static inline int _soc_dai_ret(const struct snd_soc_dai *dai,
const char *func, int ret)
{
- /* Positive, Zero values are not errors */
- if (ret >= 0)
- return ret;
-
- /* Negative values might be errors */
- switch (ret) {
- case -EPROBE_DEFER:
- case -ENOTSUPP:
- break;
- default:
- dev_err(dai->dev,
- "ASoC: error at %s on %s: %d\n",
- func, dai->name, ret);
- }
-
- return ret;
+ return snd_soc_ret(dai->dev, ret,
+ "at %s() on %s\n", func, dai->name);
}
/*
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b5116b700d73..420fe7dea31e 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -317,7 +317,6 @@ void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
snd_soc_dapm_mutex_unlock(card);
}
-EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
@@ -2783,7 +2782,6 @@ int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_update_dai);
int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char *s)
{
@@ -4865,7 +4863,6 @@ void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
/* see for_each_card_dapms */
list_add(&dapm->list, &card->dapm_list);
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_init);
static void soc_dapm_shutdown_dapm(struct snd_soc_dapm_context *dapm)
{
diff --git a/sound/soc/soc-link.c b/sound/soc/soc-link.c
index 7f1f1bc717e2..02fd68f2e702 100644
--- a/sound/soc/soc-link.c
+++ b/sound/soc/soc-link.c
@@ -12,22 +12,8 @@
static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd,
const char *func, int ret)
{
- /* Positive, Zero values are not errors */
- if (ret >= 0)
- return ret;
-
- /* Negative values might be errors */
- switch (ret) {
- case -EPROBE_DEFER:
- case -ENOTSUPP:
- break;
- default:
- dev_err(rtd->dev,
- "ASoC: error at %s on %s: %d\n",
- func, rtd->dai_link->name, ret);
- }
-
- return ret;
+ return snd_soc_ret(rtd->dev, ret,
+ "at %s() on %s\n", func, rtd->dai_link->name);
}
/*
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 19928f098d8d..c6601ef16f84 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -24,7 +24,6 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/soc-dpcm.h>
#include <sound/initval.h>
/**
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 88b3ad5a2552..ebe99d369ca9 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -30,22 +30,8 @@
static inline int _soc_pcm_ret(struct snd_soc_pcm_runtime *rtd,
const char *func, int ret)
{
- /* Positive, Zero values are not errors */
- if (ret >= 0)
- return ret;
-
- /* Negative values might be errors */
- switch (ret) {
- case -EPROBE_DEFER:
- case -ENOTSUPP:
- break;
- default:
- dev_err(rtd->dev,
- "ASoC: error at %s on %s: %d\n",
- func, rtd->dai_link->name, ret);
- }
-
- return ret;
+ return snd_soc_ret(rtd->dev, ret,
+ "at %s() on %s\n", func, rtd->dai_link->name);
}
/* is the current PCM operation for this FE ? */
@@ -252,11 +238,9 @@ static ssize_t dpcm_state_read_file(struct file *file, char __user *user_buf,
int stream;
char *buf;
- if (fe->dai_link->num_cpus > 1) {
- dev_err(fe->dev,
+ if (fe->dai_link->num_cpus > 1)
+ return snd_soc_ret(fe->dev, -EINVAL,
"%s doesn't support Multi CPU yet\n", __func__);
- return -EINVAL;
- }
buf = kmalloc(out_count, GFP_KERNEL);
if (!buf)
@@ -474,12 +458,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
ret = snd_pcm_hw_constraint_single(substream->runtime, \
SNDRV_PCM_HW_PARAM_##NAME,\
soc_dai->symmetric_##name); \
- if (ret < 0) { \
- dev_err(soc_dai->dev, \
- "ASoC: Unable to apply %s constraint: %d\n",\
- #name, ret); \
- return ret; \
- } \
+ if (ret < 0) \
+ return snd_soc_ret(soc_dai->dev, ret, \
+ "Unable to apply %s constraint\n", #name); \
}
__soc_pcm_apply_symmetry(rate, RATE);
@@ -510,12 +491,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
for_each_rtd_cpu_dais(rtd, i, cpu_dai) \
if (!snd_soc_dai_is_dummy(cpu_dai) && \
cpu_dai->symmetric_##xxx && \
- cpu_dai->symmetric_##xxx != d.symmetric_##xxx) { \
- dev_err(rtd->dev, "ASoC: unmatched %s symmetry: %s:%d - %s:%d\n", \
- #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \
- d.name, d.symmetric_##xxx); \
- return -EINVAL; \
- }
+ cpu_dai->symmetric_##xxx != d.symmetric_##xxx) \
+ return snd_soc_ret(rtd->dev, -EINVAL, \
+ "unmatched %s symmetry: %s:%d - %s:%d\n", \
+ #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \
+ d.name, d.symmetric_##xxx);
/* reject unmatched parameters when applying symmetry */
__soc_pcm_params_symmetry(rate);
@@ -860,9 +840,8 @@ static int soc_hw_sanity_check(struct snd_pcm_substream *substream)
return 0;
config_err:
- dev_err(dev, "ASoC: %s <-> %s No matching %s\n",
- name_codec, name_cpu, err_msg);
- return -EINVAL;
+ return snd_soc_ret(dev, -EINVAL,
+ "%s <-> %s No matching %s\n", name_codec, name_cpu, err_msg);
}
/*
@@ -1333,11 +1312,11 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
fe_substream = snd_soc_dpcm_get_substream(fe, stream);
be_substream = snd_soc_dpcm_get_substream(be, stream);
- if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic) {
- dev_err(be->dev, "%s: FE is atomic but BE is nonatomic, invalid configuration\n",
- __func__);
- return -EINVAL;
- }
+ if (!fe_substream->pcm->nonatomic && be_substream->pcm->nonatomic)
+ return snd_soc_ret(be->dev, -EINVAL,
+ "%s: %s is atomic but %s is nonatomic, invalid configuration\n",
+ __func__, fe->dai_link->name, be->dai_link->name);
+
if (fe_substream->pcm->nonatomic && !be_substream->pcm->nonatomic) {
dev_dbg(be->dev, "FE is nonatomic but BE is not, forcing BE as nonatomic\n");
be_substream->pcm->nonatomic = 1;
@@ -1507,11 +1486,9 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(fe, 0);
int paths;
- if (fe->dai_link->num_cpus > 1) {
- dev_err(fe->dev,
+ if (fe->dai_link->num_cpus > 1)
+ return snd_soc_ret(fe->dev, -EINVAL,
"%s doesn't support Multi CPU yet\n", __func__);
- return -EINVAL;
- }
/* get number of valid DAI paths and their widgets */
paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
@@ -2402,23 +2379,23 @@ static int dpcm_dai_trigger_fe_be(struct snd_pcm_substream *substream,
ret = soc_pcm_trigger(substream, cmd);
if (ret < 0)
- return ret;
+ goto end;
ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
- return ret;
+ goto end;
}
/* call trigger on the frontend after the backend. */
ret = dpcm_be_dai_trigger(fe, substream->stream, cmd);
if (ret < 0)
- return ret;
+ goto end;
dev_dbg(fe->dev, "ASoC: post trigger FE %s cmd %d\n",
fe->dai_link->name, cmd);
ret = soc_pcm_trigger(substream, cmd);
-
- return ret;
+end:
+ return snd_soc_ret(fe->dev, ret, "trigger FE cmd: %d failed\n", cmd);
}
static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
@@ -2474,11 +2451,8 @@ static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
goto out;
}
- if (ret < 0) {
- dev_err(fe->dev, "ASoC: trigger FE cmd: %d failed: %d\n",
- cmd, ret);
+ if (ret < 0)
goto out;
- }
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -2707,11 +2681,9 @@ static int soc_dpcm_fe_runtime_update(struct snd_soc_pcm_runtime *fe, int new)
if (!fe->dai_link->dynamic)
return 0;
- if (fe->dai_link->num_cpus > 1) {
- dev_err(fe->dev,
+ if (fe->dai_link->num_cpus > 1)
+ return snd_soc_ret(fe->dev, -EINVAL,
"%s doesn't support Multi CPU yet\n", __func__);
- return -EINVAL;
- }
/* only check active links */
if (!snd_soc_dai_active(snd_soc_rtd_to_cpu(fe, 0)))
@@ -2782,7 +2754,8 @@ int snd_soc_dpcm_runtime_update(struct snd_soc_card *card)
out:
snd_soc_dpcm_mutex_unlock(card);
- return ret;
+
+ return snd_soc_ret(card->dev, ret, "%s() failed\n", __func__);
}
EXPORT_SYMBOL_GPL(snd_soc_dpcm_runtime_update);
@@ -2856,10 +2829,9 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
int has_capture = 0;
int i;
- if (dai_link->dynamic && dai_link->num_cpus > 1) {
- dev_err(rtd->dev, "DPCM doesn't support Multi CPU for Front-Ends yet\n");
- return -EINVAL;
- }
+ if (dai_link->dynamic && dai_link->num_cpus > 1)
+ return snd_soc_ret(rtd->dev, -EINVAL,
+ "DPCM doesn't support Multi CPU for Front-Ends yet\n");
/* Adapt stream for codec2codec links */
cpu_capture = snd_soc_get_stream_cpu(dai_link, SNDRV_PCM_STREAM_CAPTURE);
@@ -2901,12 +2873,9 @@ static int soc_get_playback_capture(struct snd_soc_pcm_runtime *rtd,
if (dai_link->capture_only)
has_playback = 0;
- if (!has_playback && !has_capture) {
- dev_err(rtd->dev, "substream %s has no playback, no capture\n",
- dai_link->stream_name);
-
- return -EINVAL;
- }
+ if (!has_playback && !has_capture)
+ return snd_soc_ret(rtd->dev, -EINVAL,
+ "substream %s has no playback, no capture\n", dai_link->stream_name);
*playback = has_playback;
*capture = has_capture;
@@ -2946,11 +2915,10 @@ static int soc_create_pcm(struct snd_pcm **pcm,
ret = snd_pcm_new(rtd->card->snd_card, new_name, rtd->id, playback,
capture, pcm);
}
- if (ret < 0) {
- dev_err(rtd->card->dev, "ASoC: can't create pcm %s for dailink %s: %d\n",
- new_name, rtd->dai_link->name, ret);
- return ret;
- }
+ if (ret < 0)
+ return snd_soc_ret(rtd->dev, ret,
+ "can't create pcm %s for dailink %s\n", new_name, rtd->dai_link->name);
+
dev_dbg(rtd->card->dev, "ASoC: registered pcm #%d %s\n", rtd->id, new_name);
return 0;
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index aa93e77ac937..318b141c00b3 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -15,6 +15,33 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
+int snd_soc_ret(const struct device *dev, int ret, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ /* Positive, Zero values are not errors */
+ if (ret >= 0)
+ return ret;
+
+ /* Negative values might be errors */
+ switch (ret) {
+ case -EPROBE_DEFER:
+ case -ENOTSUPP:
+ case -EOPNOTSUPP:
+ break;
+ default:
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ dev_err(dev, "ASoC error (%d): %pV", ret, &vaf);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_ret);
+
int snd_soc_calc_frame_size(int sample_size, int channels, int tdm_slots)
{
return sample_size * channels * tdm_slots;
diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig
index 4751b04d5e6f..327e2df94a58 100644
--- a/sound/soc/sof/imx/Kconfig
+++ b/sound/soc/sof/imx/Kconfig
@@ -32,22 +32,13 @@ config SND_SOC_SOF_IMX8
Say Y if you have such a device.
If unsure select "N".
-config SND_SOC_SOF_IMX8M
- tristate "SOF support for i.MX8M"
+config SND_SOC_SOF_IMX9
+ tristate "SOF support for i.MX9"
depends on IMX_DSP
select SND_SOC_SOF_IMX_COMMON
help
- This adds support for Sound Open Firmware for NXP i.MX8M platforms.
- Say Y if you have such a device.
- If unsure select "N".
-
-config SND_SOC_SOF_IMX8ULP
- tristate "SOF support for i.MX8ULP"
- depends on IMX_DSP
- select SND_SOC_SOF_IMX_COMMON
- help
- This adds support for Sound Open Firmware for NXP i.MX8ULP platforms.
- Say Y if you have such a device.
+ This adds support for Sound Open Firmware for NXP i.MX9 platforms.
+ Say Y if you need such a device.
If unsure select "N".
endif ## SND_SOC_SOF_IMX_TOPLEVEL
diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile
index be0bf0736dfa..74b5ecad8fe8 100644
--- a/sound/soc/sof/imx/Makefile
+++ b/sound/soc/sof/imx/Makefile
@@ -1,11 +1,9 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
snd-sof-imx8-y := imx8.o
-snd-sof-imx8m-y := imx8m.o
-snd-sof-imx8ulp-y := imx8ulp.o
+snd-sof-imx9-y := imx9.o
snd-sof-imx-common-y := imx-common.o
obj-$(CONFIG_SND_SOC_SOF_IMX8) += snd-sof-imx8.o
-obj-$(CONFIG_SND_SOC_SOF_IMX8M) += snd-sof-imx8m.o
-obj-$(CONFIG_SND_SOC_SOF_IMX8ULP) += snd-sof-imx8ulp.o
+obj-$(CONFIG_SND_SOC_SOF_IMX9) += snd-sof-imx9.o
obj-$(CONFIG_SND_SOC_SOF_IMX_COMMON) += imx-common.o
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index fce6d9cf6a6b..c3594815e60e 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -1,11 +1,16 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
-// Copyright 2020 NXP
+// Copyright 2020-2025 NXP
//
// Common helpers for the audio DSP on i.MX8
+#include <linux/firmware/imx/dsp.h>
#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/pm_domain.h>
#include <sound/sof/xtensa.h>
+
#include "../ops.h"
#include "imx-common.h"
@@ -74,5 +79,429 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags)
}
EXPORT_SYMBOL(imx8_dump);
+static void imx_handle_reply(struct imx_dsp_ipc *ipc)
+{
+ struct snd_sof_dev *sdev;
+ unsigned long flags;
+
+ sdev = imx_dsp_get_data(ipc);
+
+ spin_lock_irqsave(&sdev->ipc_lock, flags);
+ snd_sof_ipc_process_reply(sdev, 0);
+ spin_unlock_irqrestore(&sdev->ipc_lock, flags);
+}
+
+static void imx_handle_request(struct imx_dsp_ipc *ipc)
+{
+ struct snd_sof_dev *sdev;
+ u32 panic_code;
+
+ sdev = imx_dsp_get_data(ipc);
+
+ if (get_chip_info(sdev)->ipc_info.has_panic_code) {
+ sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4,
+ &panic_code,
+ sizeof(panic_code));
+
+ if ((panic_code & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) {
+ snd_sof_dsp_panic(sdev, panic_code, true);
+ return;
+ }
+ }
+
+ snd_sof_ipc_msgs_rx(sdev);
+}
+
+static struct imx_dsp_ops imx_ipc_ops = {
+ .handle_reply = imx_handle_reply,
+ .handle_request = imx_handle_request,
+};
+
+static int imx_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
+{
+ struct imx_common_data *common = sdev->pdata->hw_pdata;
+
+ sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size);
+ imx_dsp_ring_doorbell(common->ipc_handle, 0x0);
+
+ return 0;
+}
+
+static int imx_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+{
+ switch (type) {
+ case SOF_FW_BLK_TYPE_IRAM:
+ case SOF_FW_BLK_TYPE_SRAM:
+ return type;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int imx_get_mailbox_offset(struct snd_sof_dev *sdev)
+{
+ return get_chip_info(sdev)->ipc_info.boot_mbox_offset;
+}
+
+static int imx_get_window_offset(struct snd_sof_dev *sdev, u32 id)
+{
+ return get_chip_info(sdev)->ipc_info.window_offset;
+}
+
+static int imx_set_power_state(struct snd_sof_dev *sdev,
+ const struct sof_dsp_power_state *target)
+{
+ sdev->dsp_power_state = *target;
+
+ return 0;
+}
+
+static int imx_common_resume(struct snd_sof_dev *sdev)
+{
+ struct imx_common_data *common;
+ int ret, i;
+
+ common = sdev->pdata->hw_pdata;
+
+ ret = clk_bulk_prepare_enable(common->clk_num, common->clks);
+ if (ret)
+ dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_request_channel(common->ipc_handle, i);
+
+ /* done. If need be, core will be started by SOF core immediately after */
+ return 0;
+}
+
+static int imx_common_suspend(struct snd_sof_dev *sdev)
+{
+ struct imx_common_data *common;
+ int i, ret;
+
+ common = sdev->pdata->hw_pdata;
+
+ ret = imx_chip_core_shutdown(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to shutdown core: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < DSP_MU_CHAN_NUM; i++)
+ imx_dsp_free_channel(common->ipc_handle, i);
+
+ clk_bulk_disable_unprepare(common->clk_num, common->clks);
+
+ return 0;
+}
+
+static int imx_runtime_resume(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+ int ret;
+
+ ret = imx_common_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to runtime common resume: %d\n", ret);
+ return ret;
+ }
+
+ return snd_sof_dsp_set_power_state(sdev, &target_state);
+}
+
+static int imx_resume(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D0,
+ };
+ int ret;
+
+ ret = imx_common_resume(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to common resume: %d\n", ret);
+ return ret;
+ }
+
+ if (pm_runtime_suspended(sdev->dev)) {
+ pm_runtime_disable(sdev->dev);
+ pm_runtime_set_active(sdev->dev);
+ pm_runtime_mark_last_busy(sdev->dev);
+ pm_runtime_enable(sdev->dev);
+ pm_runtime_idle(sdev->dev);
+ }
+
+ return snd_sof_dsp_set_power_state(sdev, &target_state);
+}
+
+static int imx_runtime_suspend(struct snd_sof_dev *sdev)
+{
+ const struct sof_dsp_power_state target_state = {
+ .state = SOF_DSP_PM_D3,
+ };
+ int ret;
+
+ ret = imx_common_suspend(sdev);
+ if (ret < 0)
+ dev_err(sdev->dev, "failed to runtime common suspend: %d\n", ret);
+
+ return snd_sof_dsp_set_power_state(sdev, &target_state);
+}
+
+static int imx_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
+{
+ const struct sof_dsp_power_state target_power_state = {
+ .state = target_state,
+ };
+ int ret;
+
+ if (!pm_runtime_suspended(sdev->dev)) {
+ ret = imx_common_suspend(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "failed to common suspend: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return snd_sof_dsp_set_power_state(sdev, &target_power_state);
+}
+
+static int imx_region_name_to_blk_type(const char *region_name)
+{
+ if (!strcmp(region_name, "iram"))
+ return SOF_FW_BLK_TYPE_IRAM;
+ else if (!strcmp(region_name, "dram"))
+ return SOF_FW_BLK_TYPE_DRAM;
+ else if (!strcmp(region_name, "sram"))
+ return SOF_FW_BLK_TYPE_SRAM;
+ else
+ return -EINVAL;
+}
+
+static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev)
+{
+ const struct imx_chip_info *chip_info;
+ struct reserved_mem *reserved;
+ struct platform_device *pdev;
+ struct device_node *res_np;
+ phys_addr_t base, size;
+ struct resource *res;
+ int i, blk_type, ret;
+
+ pdev = to_platform_device(sdev->dev);
+ chip_info = get_chip_info(sdev);
+
+ for (i = 0; chip_info->memory[i].name; i++) {
+ blk_type = imx_region_name_to_blk_type(chip_info->memory[i].name);
+ if (blk_type < 0)
+ return dev_err_probe(sdev->dev, blk_type,
+ "no blk type for region %s\n",
+ chip_info->memory[i].name);
+
+ if (!chip_info->memory[i].reserved) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ chip_info->memory[i].name);
+ if (!res)
+ return dev_err_probe(sdev->dev, -ENODEV,
+ "failed to fetch %s resource\n",
+ chip_info->memory[i].name);
+
+ base = res->start;
+ size = resource_size(res);
+ } else {
+ ret = of_property_match_string(pdev->dev.of_node,
+ "memory-region-names",
+ chip_info->memory[i].name);
+ if (ret < 0)
+ return dev_err_probe(sdev->dev, ret,
+ "no valid index for %s\n",
+ chip_info->memory[i].name);
+
+ res_np = of_parse_phandle(pdev->dev.of_node,
+ "memory-region",
+ ret);
+ if (!res_np)
+ return dev_err_probe(sdev->dev, -ENODEV,
+ "failed to parse phandle %s\n",
+ chip_info->memory[i].name);
+
+ reserved = of_reserved_mem_lookup(res_np);
+ of_node_put(res_np);
+ if (!reserved)
+ return dev_err_probe(sdev->dev, -ENODEV,
+ "failed to get %s reserved\n",
+ chip_info->memory[i].name);
+
+ base = reserved->base;
+ size = reserved->size;
+ }
+
+ sdev->bar[blk_type] = devm_ioremap(sdev->dev, base, size);
+ if (IS_ERR(sdev->bar[blk_type]))
+ return dev_err_probe(sdev->dev,
+ PTR_ERR(sdev->bar[blk_type]),
+ "failed to ioremap %s region\n",
+ chip_info->memory[i].name);
+ }
+
+ return 0;
+}
+
+static void imx_unregister_action(void *data)
+{
+ struct imx_common_data *common;
+ struct snd_sof_dev *sdev;
+
+ sdev = data;
+ common = sdev->pdata->hw_pdata;
+
+ if (get_chip_info(sdev)->has_dma_reserved)
+ of_reserved_mem_device_release(sdev->dev);
+
+ platform_device_unregister(common->ipc_dev);
+}
+
+static int imx_probe(struct snd_sof_dev *sdev)
+{
+ struct dev_pm_domain_attach_data domain_data = {
+ .pd_names = NULL, /* no filtering */
+ .pd_flags = PD_FLAG_DEV_LINK_ON,
+ };
+ struct imx_common_data *common;
+ struct platform_device *pdev;
+ int ret;
+
+ pdev = to_platform_device(sdev->dev);
+
+ common = devm_kzalloc(sdev->dev, sizeof(*common), GFP_KERNEL);
+ if (!common)
+ return dev_err_probe(sdev->dev, -ENOMEM,
+ "failed to allocate common data\n");
+ sdev->pdata->hw_pdata = common;
+
+ common->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
+ PLATFORM_DEVID_NONE,
+ pdev, sizeof(*pdev));
+ if (IS_ERR(common->ipc_dev))
+ return dev_err_probe(sdev->dev, PTR_ERR(common->ipc_dev),
+ "failed to create IPC device\n");
+
+ if (get_chip_info(sdev)->has_dma_reserved) {
+ ret = of_reserved_mem_device_init_by_name(sdev->dev,
+ pdev->dev.of_node,
+ "dma");
+ if (ret) {
+ platform_device_unregister(common->ipc_dev);
+
+ return dev_err_probe(sdev->dev, ret,
+ "failed to bind DMA region\n");
+ }
+ }
+
+ /* let the devres API take care of the cleanup */
+ ret = devm_add_action_or_reset(sdev->dev,
+ imx_unregister_action,
+ sdev);
+ if (ret)
+ return dev_err_probe(sdev->dev, ret, "failed to add devm action\n");
+
+ common->ipc_handle = dev_get_drvdata(&common->ipc_dev->dev);
+ if (!common->ipc_handle)
+ return dev_err_probe(sdev->dev, -EPROBE_DEFER,
+ "failed to fetch IPC handle\n");
+
+ ret = imx_parse_ioremap_memory(sdev);
+ if (ret < 0)
+ return dev_err_probe(sdev->dev, ret,
+ "failed to parse/ioremap memory regions\n");
+
+ if (!sdev->dev->pm_domain) {
+ ret = devm_pm_domain_attach_list(sdev->dev,
+ &domain_data, &common->pd_list);
+ if (ret < 0)
+ return dev_err_probe(sdev->dev, ret, "failed to attach PDs\n");
+ }
+
+ ret = devm_clk_bulk_get_all(sdev->dev, &common->clks);
+ if (ret < 0)
+ return dev_err_probe(sdev->dev, common->clk_num,
+ "failed to fetch clocks\n");
+ common->clk_num = ret;
+
+ ret = clk_bulk_prepare_enable(common->clk_num, common->clks);
+ if (ret < 0)
+ return dev_err_probe(sdev->dev, ret, "failed to enable clocks\n");
+
+ common->ipc_handle->ops = &imx_ipc_ops;
+ imx_dsp_set_data(common->ipc_handle, sdev);
+
+ sdev->num_cores = 1;
+ sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+ sdev->dsp_box.offset = get_chip_info(sdev)->ipc_info.boot_mbox_offset;
+
+ return imx_chip_probe(sdev);
+}
+
+static void imx_remove(struct snd_sof_dev *sdev)
+{
+ struct imx_common_data *common;
+ int ret;
+
+ common = sdev->pdata->hw_pdata;
+
+ if (!pm_runtime_suspended(sdev->dev)) {
+ ret = imx_chip_core_shutdown(sdev);
+ if (ret < 0)
+ dev_err(sdev->dev, "failed to shutdown core: %d\n", ret);
+
+ clk_bulk_disable_unprepare(common->clk_num, common->clks);
+ }
+}
+
+const struct snd_sof_dsp_ops sof_imx_ops = {
+ .probe = imx_probe,
+ .remove = imx_remove,
+
+ .run = imx_chip_core_kick,
+ .reset = imx_chip_core_reset,
+
+ .block_read = sof_block_read,
+ .block_write = sof_block_write,
+
+ .mailbox_read = sof_mailbox_read,
+ .mailbox_write = sof_mailbox_write,
+
+ .send_msg = imx_send_msg,
+ .get_mailbox_offset = imx_get_mailbox_offset,
+ .get_window_offset = imx_get_window_offset,
+
+ .ipc_msg_data = sof_ipc_msg_data,
+ .set_stream_data_offset = sof_set_stream_data_offset,
+
+ .get_bar_index = imx_get_bar_index,
+ .load_firmware = snd_sof_load_firmware_memcpy,
+
+ .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+
+ .pcm_open = sof_stream_pcm_open,
+ .pcm_close = sof_stream_pcm_close,
+
+ .runtime_suspend = imx_runtime_suspend,
+ .runtime_resume = imx_runtime_resume,
+ .suspend = imx_suspend,
+ .resume = imx_resume,
+
+ .set_power_state = imx_set_power_state,
+
+ .hw_info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+};
+EXPORT_SYMBOL(sof_imx_ops);
+
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SOF helpers for IMX platforms");
diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h
index 13d7f3ef675e..9bd711dbb5d0 100644
--- a/sound/soc/sof/imx/imx-common.h
+++ b/sound/soc/sof/imx/imx-common.h
@@ -4,10 +4,159 @@
#define __IMX_COMMON_H__
#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <sound/sof/xtensa.h>
+
+#include "../sof-of-dev.h"
+#include "../ops.h"
#define EXCEPT_MAX_HDR_SIZE 0x400
#define IMX8_STACK_DUMP_SIZE 32
+/* chip_info refers to the data stored in struct sof_dev_desc's chip_info */
+#define get_chip_info(sdev)\
+ ((const struct imx_chip_info *)((sdev)->pdata->desc->chip_info))
+
+/* chip_pdata refers to the data stored in struct imx_common_data's chip_pdata */
+#define get_chip_pdata(sdev)\
+ (((struct imx_common_data *)((sdev)->pdata->hw_pdata))->chip_pdata)
+
+/* can be used if:
+ * 1) The only supported IPC version is IPC3.
+ * 2) The default paths/FW name match values below.
+ *
+ * otherwise, just explicitly declare the structure
+ */
+#define IMX_SOF_DEV_DESC(mach_name, of_machs, \
+ mach_chip_info, mach_ops, mach_ops_init) \
+static struct sof_dev_desc sof_of_##mach_name##_desc = { \
+ .of_machines = of_machs, \
+ .chip_info = mach_chip_info, \
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_3), \
+ .ipc_default = SOF_IPC_TYPE_3, \
+ .default_fw_path = { \
+ [SOF_IPC_TYPE_3] = "imx/sof", \
+ }, \
+ .default_tplg_path = { \
+ [SOF_IPC_TYPE_3] = "imx/sof-tplg", \
+ }, \
+ .default_fw_filename = { \
+ [SOF_IPC_TYPE_3] = "sof-" #mach_name ".ri", \
+ }, \
+ .ops = mach_ops, \
+ .ops_init = mach_ops_init, \
+}
+
+/* to be used alongside IMX_SOF_DEV_DESC() */
+#define IMX_SOF_DEV_DESC_NAME(mach_name) sof_of_##mach_name##_desc
+
+/* dai driver entry w/ playback and capture caps. If one direction is missing
+ * then set the channels to 0.
+ */
+#define IMX_SOF_DAI_DRV_ENTRY(dai_name, pb_cmin, pb_cmax, cap_cmin, cap_cmax) \
+{ \
+ .name = dai_name, \
+ .playback = { \
+ .channels_min = pb_cmin, \
+ .channels_max = pb_cmax, \
+ }, \
+ .capture = { \
+ .channels_min = cap_cmin, \
+ .channels_max = cap_cmax, \
+ }, \
+}
+
+/* use if playback and capture have the same min/max channel count */
+#define IMX_SOF_DAI_DRV_ENTRY_BIDIR(dai_name, cmin, cmax)\
+ IMX_SOF_DAI_DRV_ENTRY(dai_name, cmin, cmax, cmin, cmax)
+
+struct imx_ipc_info {
+ /* true if core is able to write a panic code to the debug box */
+ bool has_panic_code;
+ /* offset to mailbox in which firmware initially writes FW_READY */
+ int boot_mbox_offset;
+ /* offset to region at which the mailboxes start */
+ int window_offset;
+};
+
+struct imx_chip_ops {
+ /* called after clocks and PDs are enabled */
+ int (*probe)(struct snd_sof_dev *sdev);
+ /* used directly by the SOF core */
+ int (*core_kick)(struct snd_sof_dev *sdev);
+ /* called during suspend()/remove() before clocks are disabled */
+ int (*core_shutdown)(struct snd_sof_dev *sdev);
+ /* used directly by the SOF core */
+ int (*core_reset)(struct snd_sof_dev *sdev);
+};
+
+struct imx_memory_info {
+ const char *name;
+ bool reserved;
+};
+
+struct imx_chip_info {
+ struct imx_ipc_info ipc_info;
+ /* does the chip have a reserved memory region for DMA? */
+ bool has_dma_reserved;
+ struct imx_memory_info *memory;
+ struct snd_soc_dai_driver *drv;
+ int num_drv;
+ /* optional */
+ const struct imx_chip_ops *ops;
+};
+
+struct imx_common_data {
+ struct platform_device *ipc_dev;
+ struct imx_dsp_ipc *ipc_handle;
+ /* core may have no clocks */
+ struct clk_bulk_data *clks;
+ int clk_num;
+ /* core may have no PDs */
+ struct dev_pm_domain_list *pd_list;
+ void *chip_pdata;
+};
+
+static inline int imx_chip_core_kick(struct snd_sof_dev *sdev)
+{
+ const struct imx_chip_ops *ops = get_chip_info(sdev)->ops;
+
+ if (ops && ops->core_kick)
+ return ops->core_kick(sdev);
+
+ return 0;
+}
+
+static inline int imx_chip_core_shutdown(struct snd_sof_dev *sdev)
+{
+ const struct imx_chip_ops *ops = get_chip_info(sdev)->ops;
+
+ if (ops && ops->core_shutdown)
+ return ops->core_shutdown(sdev);
+
+ return 0;
+}
+
+static inline int imx_chip_core_reset(struct snd_sof_dev *sdev)
+{
+ const struct imx_chip_ops *ops = get_chip_info(sdev)->ops;
+
+ if (ops && ops->core_reset)
+ return ops->core_reset(sdev);
+
+ return 0;
+}
+
+static inline int imx_chip_probe(struct snd_sof_dev *sdev)
+{
+ const struct imx_chip_ops *ops = get_chip_info(sdev)->ops;
+
+ if (ops && ops->probe)
+ return ops->probe(sdev);
+
+ return 0;
+}
+
void imx8_get_registers(struct snd_sof_dev *sdev,
struct sof_ipc_dsp_oops_xtensa *xoops,
struct sof_ipc_panic_info *panic_info,
@@ -15,4 +164,6 @@ void imx8_get_registers(struct snd_sof_dev *sdev,
void imx8_dump(struct snd_sof_dev *sdev, u32 flags);
+extern const struct snd_sof_dsp_ops sof_imx_ops;
+
#endif
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index 1e7bf00d7c46..4be4c569b583 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -1,140 +1,72 @@
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
//
-// Copyright 2019 NXP
+// Copyright 2019-2025 NXP
//
// Author: Daniel Baluta <daniel.baluta@nxp.com>
//
// Hardware interface for audio DSP on i.MX8
-#include <linux/firmware.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/pm_domain.h>
-
-#include <linux/module.h>
-#include <sound/sof.h>
-#include <sound/sof/xtensa.h>
-#include <linux/firmware/imx/ipc.h>
-#include <linux/firmware/imx/dsp.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+#include <linux/arm-smccc.h>
#include <linux/firmware/imx/svc/misc.h>
-#include <dt-bindings/firmware/imx/rsrc.h>
-#include "../ops.h"
-#include "../sof-of-dev.h"
-#include "imx-common.h"
+#include <linux/mfd/syscon.h>
-/* DSP memories */
-#define IRAM_OFFSET 0x10000
-#define IRAM_SIZE (2 * 1024)
-#define DRAM0_OFFSET 0x0
-#define DRAM0_SIZE (32 * 1024)
-#define DRAM1_OFFSET 0x8000
-#define DRAM1_SIZE (32 * 1024)
-#define SYSRAM_OFFSET 0x18000
-#define SYSRAM_SIZE (256 * 1024)
-#define SYSROM_OFFSET 0x58000
-#define SYSROM_SIZE (192 * 1024)
+#include "imx-common.h"
+/* imx8/imx8x macros */
#define RESET_VECTOR_VADDR 0x596f8000
-#define MBOX_OFFSET 0x800000
-#define MBOX_SIZE 0x1000
-
-struct imx8_priv {
- struct device *dev;
- struct snd_sof_dev *sdev;
-
- /* DSP IPC handler */
- struct imx_dsp_ipc *dsp_ipc;
- struct platform_device *ipc_dev;
-
- /* System Controller IPC handler */
- struct imx_sc_ipc *sc_ipc;
-
- /* Power domain handling */
- int num_domains;
- struct device **pd_dev;
- struct device_link **link;
-
- struct clk_bulk_data *clks;
- int clk_num;
-};
-
-static int imx8_get_mailbox_offset(struct snd_sof_dev *sdev)
-{
- return MBOX_OFFSET;
-}
-
-static int imx8_get_window_offset(struct snd_sof_dev *sdev, u32 id)
-{
- return MBOX_OFFSET;
-}
-
-static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc)
-{
- struct imx8_priv *priv = imx_dsp_get_data(ipc);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
- snd_sof_ipc_process_reply(priv->sdev, 0);
- spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
-}
-
-static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc)
-{
- struct imx8_priv *priv = imx_dsp_get_data(ipc);
- u32 p; /* panic code */
-
- /* Read the message from the debug box. */
- sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
-
- /* Check to see if the message is a panic code (0x0dead***) */
- if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
- snd_sof_dsp_panic(priv->sdev, p, true);
- else
- snd_sof_ipc_msgs_rx(priv->sdev);
-}
-
-static struct imx_dsp_ops dsp_ops = {
- .handle_reply = imx8_dsp_handle_reply,
- .handle_request = imx8_dsp_handle_request,
+/* imx8m macros */
+#define IMX8M_DAP_DEBUG 0x28800000
+#define IMX8M_DAP_DEBUG_SIZE (64 * 1024)
+#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
+#define IMX8M_PWRCTL_CORERESET BIT(16)
+
+#define AudioDSP_REG0 0x100
+#define AudioDSP_REG1 0x104
+#define AudioDSP_REG2 0x108
+#define AudioDSP_REG3 0x10c
+
+#define AudioDSP_REG2_RUNSTALL BIT(5)
+
+/* imx8ulp macros */
+#define FSL_SIP_HIFI_XRDC 0xc200000e
+#define SYSCTRL0 0x8
+#define EXECUTE_BIT BIT(13)
+#define RESET_BIT BIT(16)
+#define HIFI4_CLK_BIT BIT(17)
+#define PB_CLK_BIT BIT(18)
+#define PLAT_CLK_BIT BIT(19)
+#define DEBUG_LOGIC_BIT BIT(25)
+
+struct imx8m_chip_data {
+ void __iomem *dap;
+ struct regmap *regmap;
};
-static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
-{
- struct imx8_priv *priv = sdev->pdata->hw_pdata;
-
- sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
- msg->msg_size);
- imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
-
- return 0;
-}
-
/*
* DSP control.
*/
static int imx8x_run(struct snd_sof_dev *sdev)
{
- struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
int ret;
- ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
+ ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
IMX_SC_C_OFS_SEL, 1);
if (ret < 0) {
dev_err(sdev->dev, "Error system address offset source select\n");
return ret;
}
- ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
+ ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
IMX_SC_C_OFS_AUDIO, 0x80);
if (ret < 0) {
dev_err(sdev->dev, "Error system address offset of AUDIO\n");
return ret;
}
- ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
+ ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
IMX_SC_C_OFS_PERIPH, 0x5A);
if (ret < 0) {
dev_err(sdev->dev, "Error system address offset of PERIPH %d\n",
@@ -142,14 +74,14 @@ static int imx8x_run(struct snd_sof_dev *sdev)
return ret;
}
- ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
+ ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
IMX_SC_C_OFS_IRQ, 0x51);
if (ret < 0) {
dev_err(sdev->dev, "Error system address offset of IRQ\n");
return ret;
}
- imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
+ imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true,
RESET_VECTOR_VADDR);
return 0;
@@ -157,17 +89,16 @@ static int imx8x_run(struct snd_sof_dev *sdev)
static int imx8_run(struct snd_sof_dev *sdev)
{
- struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata;
int ret;
- ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP,
+ ret = imx_sc_misc_set_control(get_chip_pdata(sdev), IMX_SC_R_DSP,
IMX_SC_C_OFS_SEL, 0);
if (ret < 0) {
dev_err(sdev->dev, "Error system address offset source select\n");
return ret;
}
- imx_sc_pm_cpu_start(dsp_priv->sc_ipc, IMX_SC_R_DSP, true,
+ imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true,
RESET_VECTOR_VADDR);
return 0;
@@ -175,428 +106,273 @@ static int imx8_run(struct snd_sof_dev *sdev)
static int imx8_probe(struct snd_sof_dev *sdev)
{
- struct platform_device *pdev = to_platform_device(sdev->dev);
- struct device_node *np = pdev->dev.of_node;
- struct device_node *res_node;
- struct resource *mmio;
- struct imx8_priv *priv;
- struct resource res;
- u32 base, size;
- int ret = 0;
- int i;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- sdev->num_cores = 1;
- sdev->pdata->hw_pdata = priv;
- priv->dev = sdev->dev;
- priv->sdev = sdev;
-
- /* power up device associated power domains */
- priv->num_domains = of_count_phandle_with_args(np, "power-domains",
- "#power-domain-cells");
- if (priv->num_domains < 0) {
- dev_err(sdev->dev, "no power-domains property in %pOF\n", np);
- return priv->num_domains;
- }
-
- priv->pd_dev = devm_kmalloc_array(&pdev->dev, priv->num_domains,
- sizeof(*priv->pd_dev), GFP_KERNEL);
- if (!priv->pd_dev)
- return -ENOMEM;
-
- priv->link = devm_kmalloc_array(&pdev->dev, priv->num_domains,
- sizeof(*priv->link), GFP_KERNEL);
- if (!priv->link)
- return -ENOMEM;
-
- for (i = 0; i < priv->num_domains; i++) {
- priv->pd_dev[i] = dev_pm_domain_attach_by_id(&pdev->dev, i);
- if (IS_ERR(priv->pd_dev[i])) {
- ret = PTR_ERR(priv->pd_dev[i]);
- goto exit_unroll_pm;
- }
- priv->link[i] = device_link_add(&pdev->dev, priv->pd_dev[i],
- DL_FLAG_STATELESS |
- DL_FLAG_PM_RUNTIME |
- DL_FLAG_RPM_ACTIVE);
- if (!priv->link[i]) {
- ret = -ENOMEM;
- dev_pm_domain_detach(priv->pd_dev[i], false);
- goto exit_unroll_pm;
- }
- }
-
- ret = imx_scu_get_handle(&priv->sc_ipc);
- if (ret) {
- dev_err(sdev->dev, "Cannot obtain SCU handle (err = %d)\n",
- ret);
- goto exit_unroll_pm;
- }
+ struct imx_sc_ipc *sc_ipc_handle;
+ struct imx_common_data *common;
+ int ret;
- priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
- PLATFORM_DEVID_NONE,
- pdev, sizeof(*pdev));
- if (IS_ERR(priv->ipc_dev)) {
- ret = PTR_ERR(priv->ipc_dev);
- goto exit_unroll_pm;
- }
+ common = sdev->pdata->hw_pdata;
- priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
- if (!priv->dsp_ipc) {
- /* DSP IPC driver not probed yet, try later */
- ret = -EPROBE_DEFER;
- dev_err(sdev->dev, "Failed to get drvdata\n");
- goto exit_pdev_unregister;
- }
+ ret = imx_scu_get_handle(&sc_ipc_handle);
+ if (ret < 0)
+ return dev_err_probe(sdev->dev, ret,
+ "failed to fetch SC IPC handle\n");
- imx_dsp_set_data(priv->dsp_ipc, priv);
- priv->dsp_ipc->ops = &dsp_ops;
-
- /* DSP base */
- mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mmio) {
- base = mmio->start;
- size = resource_size(mmio);
- } else {
- dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
- ret = -EINVAL;
- goto exit_pdev_unregister;
- }
+ common->chip_pdata = sc_ipc_handle;
- sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
- dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
- base, size);
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
- sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
+ return 0;
+}
- res_node = of_parse_phandle(np, "memory-region", 0);
- if (!res_node) {
- dev_err(&pdev->dev, "failed to get memory region node\n");
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
+static int imx8m_reset(struct snd_sof_dev *sdev)
+{
+ struct imx8m_chip_data *chip;
+ u32 pwrctl;
- ret = of_address_to_resource(res_node, 0, &res);
- of_node_put(res_node);
- if (ret) {
- dev_err(&pdev->dev, "failed to get reserved region address\n");
- goto exit_pdev_unregister;
- }
+ chip = get_chip_pdata(sdev);
- sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
- resource_size(&res));
- if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
- dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
- base, size);
- ret = -ENOMEM;
- goto exit_pdev_unregister;
- }
- sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
+ /* put DSP into reset and stall */
+ pwrctl = readl(chip->dap + IMX8M_DAP_PWRCTL);
+ pwrctl |= IMX8M_PWRCTL_CORERESET;
+ writel(pwrctl, chip->dap + IMX8M_DAP_PWRCTL);
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = MBOX_OFFSET;
+ /* keep reset asserted for 10 cycles */
+ usleep_range(1, 2);
- ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret);
- goto exit_pdev_unregister;
- }
- priv->clk_num = ret;
+ regmap_update_bits(chip->regmap, AudioDSP_REG2,
+ AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL);
- ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
- goto exit_pdev_unregister;
- }
+ /* take the DSP out of reset and keep stalled for FW loading */
+ pwrctl = readl(chip->dap + IMX8M_DAP_PWRCTL);
+ pwrctl &= ~IMX8M_PWRCTL_CORERESET;
+ writel(pwrctl, chip->dap + IMX8M_DAP_PWRCTL);
return 0;
-
-exit_pdev_unregister:
- platform_device_unregister(priv->ipc_dev);
-exit_unroll_pm:
- while (--i >= 0) {
- device_link_del(priv->link[i]);
- dev_pm_domain_detach(priv->pd_dev[i], false);
- }
-
- return ret;
}
-static void imx8_remove(struct snd_sof_dev *sdev)
+static int imx8m_run(struct snd_sof_dev *sdev)
{
- struct imx8_priv *priv = sdev->pdata->hw_pdata;
- int i;
+ struct imx8m_chip_data *chip = get_chip_pdata(sdev);
- clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
- platform_device_unregister(priv->ipc_dev);
+ regmap_update_bits(chip->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0);
- for (i = 0; i < priv->num_domains; i++) {
- device_link_del(priv->link[i]);
- dev_pm_domain_detach(priv->pd_dev[i], false);
- }
+ return 0;
}
-/* on i.MX8 there is 1 to 1 match between type and BAR idx */
-static int imx8_get_bar_index(struct snd_sof_dev *sdev, u32 type)
+static int imx8m_probe(struct snd_sof_dev *sdev)
{
- /* Only IRAM and SRAM bars are valid */
- switch (type) {
- case SOF_FW_BLK_TYPE_IRAM:
- case SOF_FW_BLK_TYPE_SRAM:
- return type;
- default:
- return -EINVAL;
- }
-}
+ struct imx_common_data *common;
+ struct imx8m_chip_data *chip;
-static void imx8_suspend(struct snd_sof_dev *sdev)
-{
- int i;
- struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
+ common = sdev->pdata->hw_pdata;
- for (i = 0; i < DSP_MU_CHAN_NUM; i++)
- imx_dsp_free_channel(priv->dsp_ipc, i);
+ chip = devm_kzalloc(sdev->dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return dev_err_probe(sdev->dev, -ENOMEM,
+ "failed to allocate chip data\n");
- clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
-}
+ chip->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
+ if (!chip->dap)
+ return dev_err_probe(sdev->dev, -ENODEV,
+ "failed to ioremap DAP\n");
-static int imx8_resume(struct snd_sof_dev *sdev)
-{
- struct imx8_priv *priv = (struct imx8_priv *)sdev->pdata->hw_pdata;
- int ret;
- int i;
+ chip->regmap = syscon_regmap_lookup_by_phandle(sdev->dev->of_node, "fsl,dsp-ctrl");
+ if (IS_ERR(chip->regmap))
+ return dev_err_probe(sdev->dev, PTR_ERR(chip->regmap),
+ "failed to fetch dsp ctrl regmap\n");
- ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
- return ret;
- }
-
- for (i = 0; i < DSP_MU_CHAN_NUM; i++)
- imx_dsp_request_channel(priv->dsp_ipc, i);
+ common->chip_pdata = chip;
return 0;
}
-static int imx8_dsp_runtime_resume(struct snd_sof_dev *sdev)
+static int imx8ulp_run(struct snd_sof_dev *sdev)
{
- int ret;
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D0,
- };
+ struct regmap *regmap = get_chip_pdata(sdev);
- ret = imx8_resume(sdev);
- if (ret < 0)
- return ret;
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
+ /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
+ regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, 0);
-static int imx8_dsp_runtime_suspend(struct snd_sof_dev *sdev)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D3,
- };
+ /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/
+ regmap_update_bits(regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
- imx8_suspend(sdev);
+ /* Stall HIFI4 DSP Execution: 1 stall, 0 run */
+ regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, 0);
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
+ return 0;
}
-static int imx8_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
+static int imx8ulp_reset(struct snd_sof_dev *sdev)
{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = target_state,
- };
+ struct arm_smccc_res smc_res;
+ struct regmap *regmap;
- if (!pm_runtime_suspended(sdev->dev))
- imx8_suspend(sdev);
+ regmap = get_chip_pdata(sdev);
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
+ /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
+ regmap_update_bits(regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
-static int imx8_dsp_resume(struct snd_sof_dev *sdev)
-{
- int ret;
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D0,
- };
+ /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
+ regmap_update_bits(regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
- ret = imx8_resume(sdev);
- if (ret < 0)
- return ret;
+ /* HiFi4 Clock Enable: 1 enabled, 0 disabled */
+ regmap_update_bits(regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
- if (pm_runtime_suspended(sdev->dev)) {
- pm_runtime_disable(sdev->dev);
- pm_runtime_set_active(sdev->dev);
- pm_runtime_mark_last_busy(sdev->dev);
- pm_runtime_enable(sdev->dev);
- pm_runtime_idle(sdev->dev);
- }
+ regmap_update_bits(regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
+ usleep_range(1, 2);
-static struct snd_soc_dai_driver imx8_dai[] = {
-{
- .name = "esai0",
- .playback = {
- .channels_min = 1,
- .channels_max = 8,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-{
- .name = "sai1",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-};
+ /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
+ regmap_update_bits(regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
+ usleep_range(1, 2);
-static int imx8_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state)
-{
- sdev->dsp_power_state = *target_state;
+ arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_res);
- return 0;
+ return smc_res.a0;
}
-/* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8_ops = {
- /* probe and remove */
- .probe = imx8_probe,
- .remove = imx8_remove,
- /* DSP core boot */
- .run = imx8_run,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
+static int imx8ulp_probe(struct snd_sof_dev *sdev)
+{
+ struct imx_common_data *common;
+ struct regmap *regmap;
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
+ common = sdev->pdata->hw_pdata;
- /* ipc */
- .send_msg = imx8_send_msg,
- .get_mailbox_offset = imx8_get_mailbox_offset,
- .get_window_offset = imx8_get_window_offset,
+ regmap = syscon_regmap_lookup_by_phandle(sdev->dev->of_node, "fsl,dsp-ctrl");
+ if (IS_ERR(regmap))
+ return dev_err_probe(sdev->dev, PTR_ERR(regmap),
+ "failed to fetch dsp ctrl regmap\n");
- .ipc_msg_data = sof_ipc_msg_data,
- .set_stream_data_offset = sof_set_stream_data_offset,
+ common->chip_pdata = regmap;
- .get_bar_index = imx8_get_bar_index,
+ return 0;
+}
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_memcpy,
+static struct snd_soc_dai_driver imx8_dai[] = {
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("esai0", 1, 8),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32),
+};
- /* Debug information */
- .dbg_dump = imx8_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+static struct snd_soc_dai_driver imx8m_dai[] = {
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai1", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai2", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai7", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY("micfil", 0, 0, 1, 8),
+};
- /* stream callbacks */
- .pcm_open = sof_stream_pcm_open,
- .pcm_close = sof_stream_pcm_close,
+static struct snd_soc_dai_driver imx8ulp_dai[] = {
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai5", 1, 32),
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai6", 1, 32),
+};
- /* Firmware ops */
- .dsp_arch_ops = &sof_xtensa_arch_ops,
+static struct snd_sof_dsp_ops sof_imx8_ops;
- /* DAI drivers */
- .drv = imx8_dai,
- .num_drv = ARRAY_SIZE(imx8_dai),
+static int imx8_ops_init(struct snd_sof_dev *sdev)
+{
+ /* first copy from template */
+ memcpy(&sof_imx8_ops, &sof_imx_ops, sizeof(sof_imx_ops));
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
+ /* then set common imx8 ops */
+ sof_imx8_ops.dbg_dump = imx8_dump;
+ sof_imx8_ops.dsp_arch_ops = &sof_xtensa_arch_ops;
+ sof_imx8_ops.debugfs_add_region_item =
+ snd_sof_debugfs_add_region_item_iomem;
- /* PM */
- .runtime_suspend = imx8_dsp_runtime_suspend,
- .runtime_resume = imx8_dsp_runtime_resume,
+ /* ... and finally set DAI driver */
+ sof_imx8_ops.drv = get_chip_info(sdev)->drv;
+ sof_imx8_ops.num_drv = get_chip_info(sdev)->num_drv;
- .suspend = imx8_dsp_suspend,
- .resume = imx8_dsp_resume,
+ return 0;
+}
- .set_power_state = imx8_dsp_set_power_state,
+static const struct imx_chip_ops imx8_chip_ops = {
+ .probe = imx8_probe,
+ .core_kick = imx8_run,
};
-/* i.MX8X ops */
-static const struct snd_sof_dsp_ops sof_imx8x_ops = {
- /* probe and remove */
- .probe = imx8_probe,
- .remove = imx8_remove,
- /* DSP core boot */
- .run = imx8x_run,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
-
- /* ipc */
- .send_msg = imx8_send_msg,
- .get_mailbox_offset = imx8_get_mailbox_offset,
- .get_window_offset = imx8_get_window_offset,
-
- .ipc_msg_data = sof_ipc_msg_data,
- .set_stream_data_offset = sof_set_stream_data_offset,
+static const struct imx_chip_ops imx8x_chip_ops = {
+ .probe = imx8_probe,
+ .core_kick = imx8x_run,
+};
- .get_bar_index = imx8_get_bar_index,
+static const struct imx_chip_ops imx8m_chip_ops = {
+ .probe = imx8m_probe,
+ .core_kick = imx8m_run,
+ .core_reset = imx8m_reset,
+};
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_memcpy,
+static const struct imx_chip_ops imx8ulp_chip_ops = {
+ .probe = imx8ulp_probe,
+ .core_kick = imx8ulp_run,
+ .core_reset = imx8ulp_reset,
+};
- /* Debug information */
- .dbg_dump = imx8_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
+static struct imx_memory_info imx8_memory_regions[] = {
+ { .name = "iram", .reserved = false },
+ { .name = "sram", .reserved = true },
+ { }
+};
- /* stream callbacks */
- .pcm_open = sof_stream_pcm_open,
- .pcm_close = sof_stream_pcm_close,
+static struct imx_memory_info imx8m_memory_regions[] = {
+ { .name = "iram", .reserved = false },
+ { .name = "sram", .reserved = true },
+ { }
+};
- /* Firmware ops */
- .dsp_arch_ops = &sof_xtensa_arch_ops,
+static struct imx_memory_info imx8ulp_memory_regions[] = {
+ { .name = "iram", .reserved = false },
+ { .name = "sram", .reserved = true },
+ { }
+};
- /* DAI drivers */
+static const struct imx_chip_info imx8_chip_info = {
+ .ipc_info = {
+ .has_panic_code = true,
+ .boot_mbox_offset = 0x800000,
+ .window_offset = 0x800000,
+ },
+ .memory = imx8_memory_regions,
.drv = imx8_dai,
.num_drv = ARRAY_SIZE(imx8_dai),
+ .ops = &imx8_chip_ops,
+};
- /* PM */
- .runtime_suspend = imx8_dsp_runtime_suspend,
- .runtime_resume = imx8_dsp_runtime_resume,
-
- .suspend = imx8_dsp_suspend,
- .resume = imx8_dsp_resume,
+static const struct imx_chip_info imx8x_chip_info = {
+ .ipc_info = {
+ .has_panic_code = true,
+ .boot_mbox_offset = 0x800000,
+ .window_offset = 0x800000,
+ },
+ .memory = imx8_memory_regions,
+ .drv = imx8_dai,
+ .num_drv = ARRAY_SIZE(imx8_dai),
+ .ops = &imx8x_chip_ops,
+};
- .set_power_state = imx8_dsp_set_power_state,
+static const struct imx_chip_info imx8m_chip_info = {
+ .ipc_info = {
+ .has_panic_code = true,
+ .boot_mbox_offset = 0x800000,
+ .window_offset = 0x800000,
+ },
+ .memory = imx8m_memory_regions,
+ .drv = imx8m_dai,
+ .num_drv = ARRAY_SIZE(imx8m_dai),
+ .ops = &imx8m_chip_ops,
+};
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP
+static const struct imx_chip_info imx8ulp_chip_info = {
+ .ipc_info = {
+ .has_panic_code = true,
+ .boot_mbox_offset = 0x800000,
+ .window_offset = 0x800000,
+ },
+ .has_dma_reserved = true,
+ .memory = imx8ulp_memory_regions,
+ .drv = imx8ulp_dai,
+ .num_drv = ARRAY_SIZE(imx8ulp_dai),
+ .ops = &imx8ulp_chip_ops,
};
static struct snd_sof_of_mach sof_imx8_machs[] = {
@@ -630,47 +406,46 @@ static struct snd_sof_of_mach sof_imx8_machs[] = {
.sof_tplg_filename = "sof-imx8-cs42888.tplg",
.drv_name = "asoc-audio-graph-card2",
},
-
- {}
-};
-
-static struct sof_dev_desc sof_of_imx8qxp_desc = {
- .of_machines = sof_imx8_machs,
- .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
- .ipc_default = SOF_IPC_TYPE_3,
- .default_fw_path = {
- [SOF_IPC_TYPE_3] = "imx/sof",
+ {
+ .compatible = "fsl,imx8mp-evk",
+ .sof_tplg_filename = "sof-imx8mp-wm8960.tplg",
+ .drv_name = "asoc-audio-graph-card2",
},
- .default_tplg_path = {
- [SOF_IPC_TYPE_3] = "imx/sof-tplg",
+ {
+ .compatible = "fsl,imx8mp-evk-revb4",
+ .sof_tplg_filename = "sof-imx8mp-wm8962.tplg",
+ .drv_name = "asoc-audio-graph-card2",
},
- .default_fw_filename = {
- [SOF_IPC_TYPE_3] = "sof-imx8x.ri",
+ {
+ .compatible = "fsl,imx8ulp-evk",
+ .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
+ .drv_name = "asoc-audio-graph-card2",
},
- .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
- .ops = &sof_imx8x_ops,
+ {}
};
-static struct sof_dev_desc sof_of_imx8qm_desc = {
- .of_machines = sof_imx8_machs,
- .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
- .ipc_default = SOF_IPC_TYPE_3,
- .default_fw_path = {
- [SOF_IPC_TYPE_3] = "imx/sof",
+IMX_SOF_DEV_DESC(imx8, sof_imx8_machs, &imx8_chip_info, &sof_imx8_ops, imx8_ops_init);
+IMX_SOF_DEV_DESC(imx8x, sof_imx8_machs, &imx8x_chip_info, &sof_imx8_ops, imx8_ops_init);
+IMX_SOF_DEV_DESC(imx8m, sof_imx8_machs, &imx8m_chip_info, &sof_imx8_ops, imx8_ops_init);
+IMX_SOF_DEV_DESC(imx8ulp, sof_imx8_machs, &imx8ulp_chip_info, &sof_imx8_ops, imx8_ops_init);
+
+static const struct of_device_id sof_of_imx8_ids[] = {
+ {
+ .compatible = "fsl,imx8qxp-dsp",
+ .data = &IMX_SOF_DEV_DESC_NAME(imx8x),
},
- .default_tplg_path = {
- [SOF_IPC_TYPE_3] = "imx/sof-tplg",
+ {
+ .compatible = "fsl,imx8qm-dsp",
+ .data = &IMX_SOF_DEV_DESC_NAME(imx8),
},
- .default_fw_filename = {
- [SOF_IPC_TYPE_3] = "sof-imx8.ri",
+ {
+ .compatible = "fsl,imx8mp-dsp",
+ .data = &IMX_SOF_DEV_DESC_NAME(imx8m),
+ },
+ {
+ .compatible = "fsl,imx8ulp-dsp",
+ .data = &IMX_SOF_DEV_DESC_NAME(imx8ulp),
},
- .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
- .ops = &sof_imx8_ops,
-};
-
-static const struct of_device_id sof_of_imx8_ids[] = {
- { .compatible = "fsl,imx8qxp-dsp", .data = &sof_of_imx8qxp_desc},
- { .compatible = "fsl,imx8qm-dsp", .data = &sof_of_imx8qm_desc},
{ }
};
MODULE_DEVICE_TABLE(of, sof_of_imx8_ids);
diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c
deleted file mode 100644
index 3cabdebac558..000000000000
--- a/sound/soc/sof/imx/imx8m.c
+++ /dev/null
@@ -1,567 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
-//
-// Copyright 2020 NXP
-//
-// Author: Daniel Baluta <daniel.baluta@nxp.com>
-//
-// Hardware interface for audio DSP on i.MX8M
-
-#include <linux/bits.h>
-#include <linux/firmware.h>
-#include <linux/mfd/syscon.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/regmap.h>
-
-#include <linux/module.h>
-#include <sound/sof.h>
-#include <sound/sof/xtensa.h>
-#include <linux/firmware/imx/dsp.h>
-
-#include "../ops.h"
-#include "../sof-of-dev.h"
-#include "imx-common.h"
-
-#define MBOX_OFFSET 0x800000
-#define MBOX_SIZE 0x1000
-
-/* DAP registers */
-#define IMX8M_DAP_DEBUG 0x28800000
-#define IMX8M_DAP_DEBUG_SIZE (64 * 1024)
-#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
-#define IMX8M_PWRCTL_CORERESET BIT(16)
-
-/* DSP audio mix registers */
-#define AudioDSP_REG0 0x100
-#define AudioDSP_REG1 0x104
-#define AudioDSP_REG2 0x108
-#define AudioDSP_REG3 0x10c
-
-#define AudioDSP_REG2_RUNSTALL BIT(5)
-
-struct imx8m_priv {
- struct device *dev;
- struct snd_sof_dev *sdev;
-
- /* DSP IPC handler */
- struct imx_dsp_ipc *dsp_ipc;
- struct platform_device *ipc_dev;
-
- struct clk_bulk_data *clks;
- int clk_num;
-
- void __iomem *dap;
- struct regmap *regmap;
-};
-
-static int imx8m_get_mailbox_offset(struct snd_sof_dev *sdev)
-{
- return MBOX_OFFSET;
-}
-
-static int imx8m_get_window_offset(struct snd_sof_dev *sdev, u32 id)
-{
- return MBOX_OFFSET;
-}
-
-static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc)
-{
- struct imx8m_priv *priv = imx_dsp_get_data(ipc);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
- snd_sof_ipc_process_reply(priv->sdev, 0);
- spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
-}
-
-static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc)
-{
- struct imx8m_priv *priv = imx_dsp_get_data(ipc);
- u32 p; /* Panic code */
-
- /* Read the message from the debug box. */
- sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
-
- /* Check to see if the message is a panic code (0x0dead***) */
- if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
- snd_sof_dsp_panic(priv->sdev, p, true);
- else
- snd_sof_ipc_msgs_rx(priv->sdev);
-}
-
-static struct imx_dsp_ops imx8m_dsp_ops = {
- .handle_reply = imx8m_dsp_handle_reply,
- .handle_request = imx8m_dsp_handle_request,
-};
-
-static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
-{
- struct imx8m_priv *priv = sdev->pdata->hw_pdata;
-
- sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
- msg->msg_size);
- imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
-
- return 0;
-}
-
-/*
- * DSP control.
- */
-static int imx8m_run(struct snd_sof_dev *sdev)
-{
- struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
-
- regmap_update_bits(priv->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0);
-
- return 0;
-}
-
-static int imx8m_reset(struct snd_sof_dev *sdev)
-{
- struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
- u32 pwrctl;
-
- /* put DSP into reset and stall */
- pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL);
- pwrctl |= IMX8M_PWRCTL_CORERESET;
- writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL);
-
- /* keep reset asserted for 10 cycles */
- usleep_range(1, 2);
-
- regmap_update_bits(priv->regmap, AudioDSP_REG2,
- AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL);
-
- /* take the DSP out of reset and keep stalled for FW loading */
- pwrctl = readl(priv->dap + IMX8M_DAP_PWRCTL);
- pwrctl &= ~IMX8M_PWRCTL_CORERESET;
- writel(pwrctl, priv->dap + IMX8M_DAP_PWRCTL);
-
- return 0;
-}
-
-static int imx8m_probe(struct snd_sof_dev *sdev)
-{
- struct platform_device *pdev = to_platform_device(sdev->dev);
- struct device_node *np = pdev->dev.of_node;
- struct device_node *res_node;
- struct resource *mmio;
- struct imx8m_priv *priv;
- struct resource res;
- u32 base, size;
- int ret = 0;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- sdev->num_cores = 1;
- sdev->pdata->hw_pdata = priv;
- priv->dev = sdev->dev;
- priv->sdev = sdev;
-
- priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
- PLATFORM_DEVID_NONE,
- pdev, sizeof(*pdev));
- if (IS_ERR(priv->ipc_dev))
- return PTR_ERR(priv->ipc_dev);
-
- priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
- if (!priv->dsp_ipc) {
- /* DSP IPC driver not probed yet, try later */
- ret = -EPROBE_DEFER;
- dev_err(sdev->dev, "Failed to get drvdata\n");
- goto exit_pdev_unregister;
- }
-
- imx_dsp_set_data(priv->dsp_ipc, priv);
- priv->dsp_ipc->ops = &imx8m_dsp_ops;
-
- /* DSP base */
- mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mmio) {
- base = mmio->start;
- size = resource_size(mmio);
- } else {
- dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
- ret = -EINVAL;
- goto exit_pdev_unregister;
- }
-
- priv->dap = devm_ioremap(sdev->dev, IMX8M_DAP_DEBUG, IMX8M_DAP_DEBUG_SIZE);
- if (!priv->dap) {
- dev_err(sdev->dev, "error: failed to map DAP debug memory area");
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
-
- sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
- dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
- base, size);
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
- sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
-
- res_node = of_parse_phandle(np, "memory-region", 0);
- if (!res_node) {
- dev_err(&pdev->dev, "failed to get memory region node\n");
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
-
- ret = of_address_to_resource(res_node, 0, &res);
- of_node_put(res_node);
- if (ret) {
- dev_err(&pdev->dev, "failed to get reserved region address\n");
- goto exit_pdev_unregister;
- }
-
- sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
- resource_size(&res));
- if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
- dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
- base, size);
- ret = -ENOMEM;
- goto exit_pdev_unregister;
- }
- sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
-
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = MBOX_OFFSET;
-
- priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl");
- if (IS_ERR(priv->regmap)) {
- dev_err(sdev->dev, "cannot find dsp-ctrl registers");
- ret = PTR_ERR(priv->regmap);
- goto exit_pdev_unregister;
- }
-
- ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret);
- goto exit_pdev_unregister;
- }
- priv->clk_num = ret;
-
- ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
- goto exit_pdev_unregister;
- }
-
- return 0;
-
-exit_pdev_unregister:
- platform_device_unregister(priv->ipc_dev);
- return ret;
-}
-
-static void imx8m_remove(struct snd_sof_dev *sdev)
-{
- struct imx8m_priv *priv = sdev->pdata->hw_pdata;
-
- clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
- platform_device_unregister(priv->ipc_dev);
-}
-
-/* on i.MX8 there is 1 to 1 match between type and BAR idx */
-static int imx8m_get_bar_index(struct snd_sof_dev *sdev, u32 type)
-{
- /* Only IRAM and SRAM bars are valid */
- switch (type) {
- case SOF_FW_BLK_TYPE_IRAM:
- case SOF_FW_BLK_TYPE_SRAM:
- return type;
- default:
- return -EINVAL;
- }
-}
-
-static struct snd_soc_dai_driver imx8m_dai[] = {
-{
- .name = "sai1",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-{
- .name = "sai2",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-{
- .name = "sai3",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-{
- .name = "sai5",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-{
- .name = "sai6",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-{
- .name = "sai7",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
-},
-{
- .name = "micfil",
- .capture = {
- .channels_min = 1,
- .channels_max = 8,
- },
-},
-};
-
-static int imx8m_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state)
-{
- sdev->dsp_power_state = *target_state;
-
- return 0;
-}
-
-static int imx8m_resume(struct snd_sof_dev *sdev)
-{
- struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
- int ret;
- int i;
-
- ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
- return ret;
- }
-
- for (i = 0; i < DSP_MU_CHAN_NUM; i++)
- imx_dsp_request_channel(priv->dsp_ipc, i);
-
- return 0;
-}
-
-static void imx8m_suspend(struct snd_sof_dev *sdev)
-{
- struct imx8m_priv *priv = (struct imx8m_priv *)sdev->pdata->hw_pdata;
- int i;
-
- for (i = 0; i < DSP_MU_CHAN_NUM; i++)
- imx_dsp_free_channel(priv->dsp_ipc, i);
-
- clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
-}
-
-static int imx8m_dsp_runtime_resume(struct snd_sof_dev *sdev)
-{
- int ret;
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D0,
- };
-
- ret = imx8m_resume(sdev);
- if (ret < 0)
- return ret;
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static int imx8m_dsp_runtime_suspend(struct snd_sof_dev *sdev)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D3,
- };
-
- imx8m_suspend(sdev);
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static int imx8m_dsp_resume(struct snd_sof_dev *sdev)
-{
- int ret;
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D0,
- };
-
- ret = imx8m_resume(sdev);
- if (ret < 0)
- return ret;
-
- if (pm_runtime_suspended(sdev->dev)) {
- pm_runtime_disable(sdev->dev);
- pm_runtime_set_active(sdev->dev);
- pm_runtime_mark_last_busy(sdev->dev);
- pm_runtime_enable(sdev->dev);
- pm_runtime_idle(sdev->dev);
- }
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static int imx8m_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = target_state,
- };
-
- if (!pm_runtime_suspended(sdev->dev))
- imx8m_suspend(sdev);
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-/* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8m_ops = {
- /* probe and remove */
- .probe = imx8m_probe,
- .remove = imx8m_remove,
- /* DSP core boot */
- .run = imx8m_run,
- .reset = imx8m_reset,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
-
- /* ipc */
- .send_msg = imx8m_send_msg,
- .get_mailbox_offset = imx8m_get_mailbox_offset,
- .get_window_offset = imx8m_get_window_offset,
-
- .ipc_msg_data = sof_ipc_msg_data,
- .set_stream_data_offset = sof_set_stream_data_offset,
-
- .get_bar_index = imx8m_get_bar_index,
-
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_memcpy,
-
- /* Debug information */
- .dbg_dump = imx8_dump,
- .debugfs_add_region_item = snd_sof_debugfs_add_region_item_iomem,
-
- /* stream callbacks */
- .pcm_open = sof_stream_pcm_open,
- .pcm_close = sof_stream_pcm_close,
- /* Firmware ops */
- .dsp_arch_ops = &sof_xtensa_arch_ops,
-
- /* DAI drivers */
- .drv = imx8m_dai,
- .num_drv = ARRAY_SIZE(imx8m_dai),
-
- .suspend = imx8m_dsp_suspend,
- .resume = imx8m_dsp_resume,
-
- .runtime_suspend = imx8m_dsp_runtime_suspend,
- .runtime_resume = imx8m_dsp_runtime_resume,
-
- .set_power_state = imx8m_dsp_set_power_state,
-
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-};
-
-static struct snd_sof_of_mach sof_imx8mp_machs[] = {
- {
- .compatible = "fsl,imx8mp-evk-revb4",
- .sof_tplg_filename = "sof-imx8mp-wm8962.tplg",
- .drv_name = "asoc-audio-graph-card2",
- },
- {
- .compatible = "fsl,imx8mp-evk",
- .sof_tplg_filename = "sof-imx8mp-wm8960.tplg",
- .drv_name = "asoc-audio-graph-card2",
- },
- {}
-};
-
-static struct sof_dev_desc sof_of_imx8mp_desc = {
- .of_machines = sof_imx8mp_machs,
- .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
- .ipc_default = SOF_IPC_TYPE_3,
- .default_fw_path = {
- [SOF_IPC_TYPE_3] = "imx/sof",
- },
- .default_tplg_path = {
- [SOF_IPC_TYPE_3] = "imx/sof-tplg",
- },
- .default_fw_filename = {
- [SOF_IPC_TYPE_3] = "sof-imx8m.ri",
- },
- .nocodec_tplg_filename = "sof-imx8-nocodec.tplg",
- .ops = &sof_imx8m_ops,
-};
-
-static const struct of_device_id sof_of_imx8m_ids[] = {
- { .compatible = "fsl,imx8mp-dsp", .data = &sof_of_imx8mp_desc},
- { }
-};
-MODULE_DEVICE_TABLE(of, sof_of_imx8m_ids);
-
-/* DT driver definition */
-static struct platform_driver snd_sof_of_imx8m_driver = {
- .probe = sof_of_probe,
- .remove = sof_of_remove,
- .driver = {
- .name = "sof-audio-of-imx8m",
- .pm = &sof_of_pm,
- .of_match_table = sof_of_imx8m_ids,
- },
-};
-module_platform_driver(snd_sof_of_imx8m_driver);
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("SOF support for IMX8M platforms");
-MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
diff --git a/sound/soc/sof/imx/imx8ulp.c b/sound/soc/sof/imx/imx8ulp.c
deleted file mode 100644
index 0704da27e69d..000000000000
--- a/sound/soc/sof/imx/imx8ulp.c
+++ /dev/null
@@ -1,520 +0,0 @@
-// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
-//
-// Copyright 2021-2022 NXP
-//
-// Author: Peng Zhang <peng.zhang_8@nxp.com>
-//
-// Hardware interface for audio DSP on i.MX8ULP
-
-#include <linux/arm-smccc.h>
-#include <linux/clk.h>
-#include <linux/firmware.h>
-#include <linux/firmware/imx/dsp.h>
-#include <linux/firmware/imx/ipc.h>
-#include <linux/firmware/imx/svc/misc.h>
-#include <linux/mfd/syscon.h>
-#include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/of_reserved_mem.h>
-
-#include <sound/sof.h>
-#include <sound/sof/xtensa.h>
-
-#include "../ops.h"
-#include "../sof-of-dev.h"
-#include "imx-common.h"
-
-#define FSL_SIP_HIFI_XRDC 0xc200000e
-
-/* SIM Domain register */
-#define SYSCTRL0 0x8
-#define EXECUTE_BIT BIT(13)
-#define RESET_BIT BIT(16)
-#define HIFI4_CLK_BIT BIT(17)
-#define PB_CLK_BIT BIT(18)
-#define PLAT_CLK_BIT BIT(19)
-#define DEBUG_LOGIC_BIT BIT(25)
-
-#define MBOX_OFFSET 0x800000
-#define MBOX_SIZE 0x1000
-
-struct imx8ulp_priv {
- struct device *dev;
- struct snd_sof_dev *sdev;
-
- /* DSP IPC handler */
- struct imx_dsp_ipc *dsp_ipc;
- struct platform_device *ipc_dev;
-
- struct regmap *regmap;
- struct clk_bulk_data *clks;
- int clk_num;
-};
-
-static void imx8ulp_sim_lpav_start(struct imx8ulp_priv *priv)
-{
- /* Controls the HiFi4 DSP Reset: 1 in reset, 0 out of reset */
- regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, 0);
-
- /* Reset HiFi4 DSP Debug logic: 1 debug reset, 0 out of reset*/
- regmap_update_bits(priv->regmap, SYSCTRL0, DEBUG_LOGIC_BIT, 0);
-
- /* Stall HIFI4 DSP Execution: 1 stall, 0 run */
- regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, 0);
-}
-
-static int imx8ulp_get_mailbox_offset(struct snd_sof_dev *sdev)
-{
- return MBOX_OFFSET;
-}
-
-static int imx8ulp_get_window_offset(struct snd_sof_dev *sdev, u32 id)
-{
- return MBOX_OFFSET;
-}
-
-static void imx8ulp_dsp_handle_reply(struct imx_dsp_ipc *ipc)
-{
- struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
- unsigned long flags;
-
- spin_lock_irqsave(&priv->sdev->ipc_lock, flags);
-
- snd_sof_ipc_process_reply(priv->sdev, 0);
-
- spin_unlock_irqrestore(&priv->sdev->ipc_lock, flags);
-}
-
-static void imx8ulp_dsp_handle_request(struct imx_dsp_ipc *ipc)
-{
- struct imx8ulp_priv *priv = imx_dsp_get_data(ipc);
- u32 p; /* panic code */
-
- /* Read the message from the debug box. */
- sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p));
-
- /* Check to see if the message is a panic code (0x0dead***) */
- if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC)
- snd_sof_dsp_panic(priv->sdev, p, true);
- else
- snd_sof_ipc_msgs_rx(priv->sdev);
-}
-
-static struct imx_dsp_ops dsp_ops = {
- .handle_reply = imx8ulp_dsp_handle_reply,
- .handle_request = imx8ulp_dsp_handle_request,
-};
-
-static int imx8ulp_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg)
-{
- struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
-
- sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data,
- msg->msg_size);
- imx_dsp_ring_doorbell(priv->dsp_ipc, 0);
-
- return 0;
-}
-
-static int imx8ulp_run(struct snd_sof_dev *sdev)
-{
- struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
-
- imx8ulp_sim_lpav_start(priv);
-
- return 0;
-}
-
-static int imx8ulp_reset(struct snd_sof_dev *sdev)
-{
- struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
- struct arm_smccc_res smc_resource;
-
- /* HiFi4 Platform Clock Enable: 1 enabled, 0 disabled */
- regmap_update_bits(priv->regmap, SYSCTRL0, PLAT_CLK_BIT, PLAT_CLK_BIT);
-
- /* HiFi4 PBCLK clock enable: 1 enabled, 0 disabled */
- regmap_update_bits(priv->regmap, SYSCTRL0, PB_CLK_BIT, PB_CLK_BIT);
-
- /* HiFi4 Clock Enable: 1 enabled, 0 disabled */
- regmap_update_bits(priv->regmap, SYSCTRL0, HIFI4_CLK_BIT, HIFI4_CLK_BIT);
-
- regmap_update_bits(priv->regmap, SYSCTRL0, RESET_BIT, RESET_BIT);
- usleep_range(1, 2);
-
- /* Stall HIFI4 DSP Execution: 1 stall, 0 not stall */
- regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
- usleep_range(1, 2);
-
- arm_smccc_smc(FSL_SIP_HIFI_XRDC, 0, 0, 0, 0, 0, 0, 0, &smc_resource);
-
- return 0;
-}
-
-static int imx8ulp_probe(struct snd_sof_dev *sdev)
-{
- struct platform_device *pdev = to_platform_device(sdev->dev);
- struct device_node *np = pdev->dev.of_node;
- struct device_node *res_node;
- struct resource *mmio;
- struct imx8ulp_priv *priv;
- struct resource res;
- u32 base, size;
- int ret = 0;
-
- priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
- return -ENOMEM;
-
- sdev->num_cores = 1;
- sdev->pdata->hw_pdata = priv;
- priv->dev = sdev->dev;
- priv->sdev = sdev;
-
- /* System integration module(SIM) control dsp configuration */
- priv->regmap = syscon_regmap_lookup_by_phandle(np, "fsl,dsp-ctrl");
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
- priv->ipc_dev = platform_device_register_data(sdev->dev, "imx-dsp",
- PLATFORM_DEVID_NONE,
- pdev, sizeof(*pdev));
- if (IS_ERR(priv->ipc_dev))
- return PTR_ERR(priv->ipc_dev);
-
- priv->dsp_ipc = dev_get_drvdata(&priv->ipc_dev->dev);
- if (!priv->dsp_ipc) {
- /* DSP IPC driver not probed yet, try later */
- ret = -EPROBE_DEFER;
- dev_err(sdev->dev, "Failed to get drvdata\n");
- goto exit_pdev_unregister;
- }
-
- imx_dsp_set_data(priv->dsp_ipc, priv);
- priv->dsp_ipc->ops = &dsp_ops;
-
- /* DSP base */
- mmio = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (mmio) {
- base = mmio->start;
- size = resource_size(mmio);
- } else {
- dev_err(sdev->dev, "error: failed to get DSP base at idx 0\n");
- ret = -EINVAL;
- goto exit_pdev_unregister;
- }
-
- sdev->bar[SOF_FW_BLK_TYPE_IRAM] = devm_ioremap(sdev->dev, base, size);
- if (!sdev->bar[SOF_FW_BLK_TYPE_IRAM]) {
- dev_err(sdev->dev, "failed to ioremap base 0x%x size 0x%x\n",
- base, size);
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
- sdev->mmio_bar = SOF_FW_BLK_TYPE_IRAM;
-
- res_node = of_parse_phandle(np, "memory-reserved", 0);
- if (!res_node) {
- dev_err(&pdev->dev, "failed to get memory region node\n");
- ret = -ENODEV;
- goto exit_pdev_unregister;
- }
-
- ret = of_address_to_resource(res_node, 0, &res);
- of_node_put(res_node);
- if (ret) {
- dev_err(&pdev->dev, "failed to get reserved region address\n");
- goto exit_pdev_unregister;
- }
-
- sdev->bar[SOF_FW_BLK_TYPE_SRAM] = devm_ioremap_wc(sdev->dev, res.start,
- resource_size(&res));
- if (!sdev->bar[SOF_FW_BLK_TYPE_SRAM]) {
- dev_err(sdev->dev, "failed to ioremap mem 0x%x size 0x%x\n",
- base, size);
- ret = -ENOMEM;
- goto exit_pdev_unregister;
- }
- sdev->mailbox_bar = SOF_FW_BLK_TYPE_SRAM;
-
- /* set default mailbox offset for FW ready message */
- sdev->dsp_box.offset = MBOX_OFFSET;
-
- ret = of_reserved_mem_device_init(sdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "failed to init reserved memory region %d\n", ret);
- goto exit_pdev_unregister;
- }
-
- ret = devm_clk_bulk_get_all(sdev->dev, &priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to fetch clocks: %d\n", ret);
- goto exit_pdev_unregister;
- }
- priv->clk_num = ret;
-
- ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
- goto exit_pdev_unregister;
- }
-
- return 0;
-
-exit_pdev_unregister:
- platform_device_unregister(priv->ipc_dev);
-
- return ret;
-}
-
-static void imx8ulp_remove(struct snd_sof_dev *sdev)
-{
- struct imx8ulp_priv *priv = sdev->pdata->hw_pdata;
-
- clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
- platform_device_unregister(priv->ipc_dev);
-}
-
-/* on i.MX8 there is 1 to 1 match between type and BAR idx */
-static int imx8ulp_get_bar_index(struct snd_sof_dev *sdev, u32 type)
-{
- return type;
-}
-
-static int imx8ulp_suspend(struct snd_sof_dev *sdev)
-{
- int i;
- struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
-
- /*Stall DSP, release in .run() */
- regmap_update_bits(priv->regmap, SYSCTRL0, EXECUTE_BIT, EXECUTE_BIT);
-
- for (i = 0; i < DSP_MU_CHAN_NUM; i++)
- imx_dsp_free_channel(priv->dsp_ipc, i);
-
- clk_bulk_disable_unprepare(priv->clk_num, priv->clks);
-
- return 0;
-}
-
-static int imx8ulp_resume(struct snd_sof_dev *sdev)
-{
- struct imx8ulp_priv *priv = (struct imx8ulp_priv *)sdev->pdata->hw_pdata;
- int i, ret;
-
- ret = clk_bulk_prepare_enable(priv->clk_num, priv->clks);
- if (ret < 0) {
- dev_err(sdev->dev, "failed to enable clocks: %d\n", ret);
- return ret;
- }
-
- for (i = 0; i < DSP_MU_CHAN_NUM; i++)
- imx_dsp_request_channel(priv->dsp_ipc, i);
-
- return 0;
-}
-
-static int imx8ulp_dsp_runtime_resume(struct snd_sof_dev *sdev)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D0,
- .substate = 0,
- };
-
- imx8ulp_resume(sdev);
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static int imx8ulp_dsp_runtime_suspend(struct snd_sof_dev *sdev)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D3,
- .substate = 0,
- };
-
- imx8ulp_suspend(sdev);
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static int imx8ulp_dsp_suspend(struct snd_sof_dev *sdev, unsigned int target_state)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = target_state,
- .substate = 0,
- };
-
- if (!pm_runtime_suspended(sdev->dev))
- imx8ulp_suspend(sdev);
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static int imx8ulp_dsp_resume(struct snd_sof_dev *sdev)
-{
- const struct sof_dsp_power_state target_dsp_state = {
- .state = SOF_DSP_PM_D0,
- .substate = 0,
- };
-
- imx8ulp_resume(sdev);
-
- if (pm_runtime_suspended(sdev->dev)) {
- pm_runtime_disable(sdev->dev);
- pm_runtime_set_active(sdev->dev);
- pm_runtime_mark_last_busy(sdev->dev);
- pm_runtime_enable(sdev->dev);
- pm_runtime_idle(sdev->dev);
- }
-
- return snd_sof_dsp_set_power_state(sdev, &target_dsp_state);
-}
-
-static struct snd_soc_dai_driver imx8ulp_dai[] = {
- {
- .name = "sai5",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
- },
- {
- .name = "sai6",
- .playback = {
- .channels_min = 1,
- .channels_max = 32,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 32,
- },
- },
-};
-
-static int imx8ulp_dsp_set_power_state(struct snd_sof_dev *sdev,
- const struct sof_dsp_power_state *target_state)
-{
- sdev->dsp_power_state = *target_state;
-
- return 0;
-}
-
-/* i.MX8 ops */
-static const struct snd_sof_dsp_ops sof_imx8ulp_ops = {
- /* probe and remove */
- .probe = imx8ulp_probe,
- .remove = imx8ulp_remove,
- /* DSP core boot */
- .run = imx8ulp_run,
- .reset = imx8ulp_reset,
-
- /* Block IO */
- .block_read = sof_block_read,
- .block_write = sof_block_write,
-
- /* Module IO */
- .read64 = sof_io_read64,
-
- /* Mailbox IO */
- .mailbox_read = sof_mailbox_read,
- .mailbox_write = sof_mailbox_write,
-
- /* ipc */
- .send_msg = imx8ulp_send_msg,
- .get_mailbox_offset = imx8ulp_get_mailbox_offset,
- .get_window_offset = imx8ulp_get_window_offset,
-
- .ipc_msg_data = sof_ipc_msg_data,
- .set_stream_data_offset = sof_set_stream_data_offset,
-
- /* stream callbacks */
- .pcm_open = sof_stream_pcm_open,
- .pcm_close = sof_stream_pcm_close,
-
- /* module loading */
- .get_bar_index = imx8ulp_get_bar_index,
- /* firmware loading */
- .load_firmware = snd_sof_load_firmware_memcpy,
-
- /* Debug information */
- .dbg_dump = imx8_dump,
-
- /* Firmware ops */
- .dsp_arch_ops = &sof_xtensa_arch_ops,
-
- /* DAI drivers */
- .drv = imx8ulp_dai,
- .num_drv = ARRAY_SIZE(imx8ulp_dai),
-
- /* ALSA HW info flags */
- .hw_info = SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_BATCH |
- SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
-
- /* PM */
- .runtime_suspend = imx8ulp_dsp_runtime_suspend,
- .runtime_resume = imx8ulp_dsp_runtime_resume,
-
- .suspend = imx8ulp_dsp_suspend,
- .resume = imx8ulp_dsp_resume,
-
- .set_power_state = imx8ulp_dsp_set_power_state,
-};
-
-static struct snd_sof_of_mach sof_imx8ulp_machs[] = {
- {
- .compatible = "fsl,imx8ulp-evk",
- .sof_tplg_filename = "sof-imx8ulp-btsco.tplg",
- .drv_name = "asoc-audio-graph-card2",
- },
- {}
-};
-
-static struct sof_dev_desc sof_of_imx8ulp_desc = {
- .of_machines = sof_imx8ulp_machs,
- .ipc_supported_mask = BIT(SOF_IPC_TYPE_3),
- .ipc_default = SOF_IPC_TYPE_3,
- .default_fw_path = {
- [SOF_IPC_TYPE_3] = "imx/sof",
- },
- .default_tplg_path = {
- [SOF_IPC_TYPE_3] = "imx/sof-tplg",
- },
- .default_fw_filename = {
- [SOF_IPC_TYPE_3] = "sof-imx8ulp.ri",
- },
- .nocodec_tplg_filename = "sof-imx8ulp-nocodec.tplg",
- .ops = &sof_imx8ulp_ops,
-};
-
-static const struct of_device_id sof_of_imx8ulp_ids[] = {
- { .compatible = "fsl,imx8ulp-dsp", .data = &sof_of_imx8ulp_desc},
- { }
-};
-MODULE_DEVICE_TABLE(of, sof_of_imx8ulp_ids);
-
-/* DT driver definition */
-static struct platform_driver snd_sof_of_imx8ulp_driver = {
- .probe = sof_of_probe,
- .remove = sof_of_remove,
- .driver = {
- .name = "sof-audio-of-imx8ulp",
- .pm = &sof_of_pm,
- .of_match_table = sof_of_imx8ulp_ids,
- },
-};
-module_platform_driver(snd_sof_of_imx8ulp_driver);
-
-MODULE_LICENSE("Dual BSD/GPL");
-MODULE_DESCRIPTION("SOF support for IMX8ULP platforms");
-MODULE_IMPORT_NS("SND_SOC_SOF_XTENSA");
diff --git a/sound/soc/sof/imx/imx9.c b/sound/soc/sof/imx/imx9.c
new file mode 100644
index 000000000000..598675d4350a
--- /dev/null
+++ b/sound/soc/sof/imx/imx9.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <linux/arm-smccc.h>
+
+#include "imx-common.h"
+
+#define IMX_SIP_SRC 0xC2000005
+#define IMX_SIP_SRC_M_RESET_ADDR_SET 0x03
+
+#define IMX95_CPU_VEC_FLAGS_BOOT BIT(29)
+
+#define IMX_SIP_LMM 0xC200000F
+#define IMX_SIP_LMM_BOOT 0x0
+#define IMX_SIP_LMM_SHUTDOWN 0x1
+
+#define IMX95_M7_LM_ID 0x1
+
+static struct snd_soc_dai_driver imx95_dai[] = {
+ IMX_SOF_DAI_DRV_ENTRY_BIDIR("sai3", 1, 32),
+};
+
+static struct snd_sof_dsp_ops sof_imx9_ops;
+
+static int imx95_ops_init(struct snd_sof_dev *sdev)
+{
+ /* first copy from template */
+ memcpy(&sof_imx9_ops, &sof_imx_ops, sizeof(sof_imx_ops));
+
+ /* ... and finally set DAI driver */
+ sof_imx9_ops.drv = get_chip_info(sdev)->drv;
+ sof_imx9_ops.num_drv = get_chip_info(sdev)->num_drv;
+
+ return 0;
+}
+
+static int imx95_chip_probe(struct snd_sof_dev *sdev)
+{
+ struct arm_smccc_res smc_res;
+ struct platform_device *pdev;
+ struct resource *res;
+
+ pdev = to_platform_device(sdev->dev);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sram");
+ if (!res)
+ return dev_err_probe(sdev->dev, -ENODEV,
+ "failed to fetch SRAM region\n");
+
+ /* set core boot reset address */
+ arm_smccc_smc(IMX_SIP_SRC, IMX_SIP_SRC_M_RESET_ADDR_SET, res->start,
+ IMX95_CPU_VEC_FLAGS_BOOT, 0, 0, 0, 0, &smc_res);
+
+ return smc_res.a0;
+}
+
+static int imx95_core_kick(struct snd_sof_dev *sdev)
+{
+ struct arm_smccc_res smc_res;
+
+ arm_smccc_smc(IMX_SIP_LMM, IMX_SIP_LMM_BOOT,
+ IMX95_M7_LM_ID, 0, 0, 0, 0, 0, &smc_res);
+
+ return smc_res.a0;
+}
+
+static int imx95_core_shutdown(struct snd_sof_dev *sdev)
+{
+ struct arm_smccc_res smc_res;
+
+ arm_smccc_smc(IMX_SIP_LMM, IMX_SIP_LMM_SHUTDOWN,
+ IMX95_M7_LM_ID, 0, 0, 0, 0, 0, &smc_res);
+
+ return smc_res.a0;
+}
+
+static const struct imx_chip_ops imx95_chip_ops = {
+ .probe = imx95_chip_probe,
+ .core_kick = imx95_core_kick,
+ .core_shutdown = imx95_core_shutdown,
+};
+
+static struct imx_memory_info imx95_memory_regions[] = {
+ { .name = "sram", .reserved = false },
+ { }
+};
+
+static const struct imx_chip_info imx95_chip_info = {
+ .ipc_info = {
+ .boot_mbox_offset = 0x6001000,
+ .window_offset = 0x6000000,
+ },
+ .has_dma_reserved = true,
+ .memory = imx95_memory_regions,
+ .drv = imx95_dai,
+ .num_drv = ARRAY_SIZE(imx95_dai),
+ .ops = &imx95_chip_ops,
+};
+
+static struct snd_sof_of_mach sof_imx9_machs[] = {
+ {
+ .compatible = "fsl,imx95-19x19-evk",
+ .sof_tplg_filename = "sof-imx95-wm8962.tplg",
+ .drv_name = "asoc-audio-graph-card2",
+ },
+ {
+ }
+};
+
+IMX_SOF_DEV_DESC(imx95, sof_imx9_machs, &imx95_chip_info, &sof_imx9_ops, imx95_ops_init);
+
+static const struct of_device_id sof_of_imx9_ids[] = {
+ {
+ .compatible = "fsl,imx95-cm7-sof",
+ .data = &IMX_SOF_DEV_DESC_NAME(imx95),
+ },
+ {
+ },
+};
+MODULE_DEVICE_TABLE(of, sof_of_imx9_ids);
+
+static struct platform_driver snd_sof_of_imx9_driver = {
+ .probe = sof_of_probe,
+ .remove = sof_of_remove,
+ .driver = {
+ .name = "sof-audio-of-imx9",
+ .pm = &sof_of_pm,
+ .of_match_table = sof_of_imx9_ids,
+ },
+};
+module_platform_driver(snd_sof_of_imx9_driver);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SOF driver for imx9 platforms");
+MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>");
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c
index 1c1b8f595367..90ef5d99f626 100644
--- a/sound/soc/sof/ipc3-pcm.c
+++ b/sound/soc/sof/ipc3-pcm.c
@@ -117,22 +117,23 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component,
if (platform_params->cont_update_posn)
pcm.params.cont_update_posn = 1;
- dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
+ spcm_dbg(spcm, substream->stream, "stream_tag %d\n",
+ pcm.params.stream_tag);
/* send hw_params IPC to the DSP */
ret = sof_ipc_tx_message(sdev->ipc, &pcm, sizeof(pcm),
&ipc_params_reply, sizeof(ipc_params_reply));
if (ret < 0) {
- dev_err(component->dev, "HW params ipc failed for stream %d\n",
- pcm.params.stream_tag);
+ spcm_err(spcm, substream->stream,
+ "STREAM_PCM_PARAMS ipc failed for stream_tag %d\n",
+ pcm.params.stream_tag);
return ret;
}
ret = snd_sof_set_stream_data_offset(sdev, &spcm->stream[substream->stream],
ipc_params_reply.posn_offset);
if (ret < 0) {
- dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n",
- __func__, spcm->pcm.pcm_id);
+ spcm_err(spcm, substream->stream, "invalid stream data offset\n");
return ret;
}
@@ -171,7 +172,7 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component,
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
break;
default:
- dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
+ spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd);
return -EINVAL;
}
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index e98b53b67d12..473d416bc910 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2386,28 +2386,16 @@ static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify)
static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev)
{
struct snd_sof_widget *swidget;
- struct snd_sof_pcm *spcm;
- int dir, ret;
+ int ret;
/*
* free all PCMs and their associated DAPM widgets if their connected DAPM widget
* list is not NULL. This should only be true for paused streams at this point.
* This is equivalent to the handling of FE DAI suspend trigger for running streams.
*/
- list_for_each_entry(spcm, &sdev->pcm_list, list) {
- for_each_pcm_streams(dir) {
- struct snd_pcm_substream *substream = spcm->stream[dir].substream;
-
- if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
- continue;
-
- if (spcm->stream[dir].list) {
- ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
- if (ret < 0)
- return ret;
- }
- }
- }
+ ret = sof_pcm_free_all_streams(sdev);
+ if (ret)
+ return ret;
/*
* free any left over DAI widgets. This is equivalent to the handling of suspend trigger
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index bcdb33d03682..6ad1ef0e53e8 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -169,21 +169,14 @@ static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev)
return payload_offset;
}
-static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev,
- unsigned long lib_id, const guid_t *uuid)
+static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id,
+ const char *lib_filename, bool optional)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_ipc4_fw_library *fw_lib;
- const char *fw_filename;
ssize_t payload_offset;
int ret, i, err;
- if (!sdev->pdata->fw_lib_prefix) {
- dev_err(sdev->dev,
- "Library loading is not supported due to not set library path\n");
- return -EINVAL;
- }
-
if (!ipc4_data->load_library) {
dev_err(sdev->dev, "Library loading is not supported on this platform\n");
return -EOPNOTSUPP;
@@ -193,21 +186,26 @@ static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev,
if (!fw_lib)
return -ENOMEM;
- fw_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin",
- sdev->pdata->fw_lib_prefix, uuid);
- if (!fw_filename) {
- ret = -ENOMEM;
- goto free_fw_lib;
- }
-
- ret = request_firmware(&fw_lib->sof_fw.fw, fw_filename, sdev->dev);
- if (ret < 0) {
- dev_err(sdev->dev, "Library file '%s' is missing\n", fw_filename);
- goto free_filename;
+ if (optional) {
+ ret = firmware_request_nowarn(&fw_lib->sof_fw.fw, lib_filename,
+ sdev->dev);
+ if (ret < 0) {
+ /* optional library, override the error */
+ ret = 0;
+ goto free_fw_lib;
+ }
} else {
- dev_dbg(sdev->dev, "Library file '%s' loaded\n", fw_filename);
+ ret = request_firmware(&fw_lib->sof_fw.fw, lib_filename,
+ sdev->dev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Library file '%s' is missing\n",
+ lib_filename);
+ goto free_fw_lib;
+ }
}
+ dev_dbg(sdev->dev, "Library file '%s' loaded\n", lib_filename);
+
payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib);
if (payload_offset <= 0) {
if (!payload_offset)
@@ -251,22 +249,117 @@ static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev,
if (unlikely(ret))
goto release;
- kfree(fw_filename);
-
return 0;
release:
release_firmware(fw_lib->sof_fw.fw);
/* Allocated within sof_ipc4_fw_parse_ext_man() */
devm_kfree(sdev->dev, fw_lib->modules);
-free_filename:
- kfree(fw_filename);
free_fw_lib:
devm_kfree(sdev->dev, fw_lib);
return ret;
}
+/**
+ * sof_ipc4_complete_split_release - loads the library parts of a split firmware
+ * @sdev: SOF device
+ *
+ * With IPC4 the firmware can be a single binary or a split release.
+ * - single binary: only the basefw
+ * - split release: basefw and two libraries (openmodules, debug)
+ *
+ * With split firmware release it is also allowed that for example only the
+ * debug library is present (the openmodules content is built in the basefw).
+ *
+ * To handle the permutations try to load the openmodules then the debug
+ * libraries as optional ones after the basefw boot.
+ *
+ * The libraries for the split release are stored alongside the basefw on the
+ * filesystem.
+ */
+int sof_ipc4_complete_split_release(struct snd_sof_dev *sdev)
+{
+ static const char * const lib_bundle[] = { "openmodules", "debug" };
+ const char *fw_filename = sdev->pdata->fw_filename;
+ const char *lib_filename, *p;
+ size_t lib_name_base_size;
+ unsigned long lib_id = 1;
+ char *lib_name_base;
+ int i;
+
+ p = strstr(fw_filename, ".ri");
+ if (!p || strlen(p) != 3) {
+ dev_info(sdev->dev,
+ "%s: Firmware name '%s' is missing .ri extension\n",
+ __func__, fw_filename);
+ return 0;
+ }
+
+ /* Space for the firmware basename + '\0', without the extension */
+ lib_name_base_size = strlen(fw_filename) - 2;
+ lib_name_base = kzalloc(lib_name_base_size, GFP_KERNEL);
+ if (!lib_name_base)
+ return -ENOMEM;
+
+ /*
+ * strscpy will 0 terminate the copied string, removing the '.ri' from
+ * the end of the fw_filename, for example:
+ * fw_filename: "sof-ptl.ri\0"
+ * lib_name_base: "sof-ptl\0"
+ */
+ strscpy(lib_name_base, fw_filename, lib_name_base_size);
+
+ for (i = 0; i < ARRAY_SIZE(lib_bundle); i++) {
+ int ret;
+
+ lib_filename = kasprintf(GFP_KERNEL, "%s/%s-%s.ri",
+ sdev->pdata->fw_filename_prefix,
+ lib_name_base, lib_bundle[i]);
+ if (!lib_filename) {
+ kfree(lib_name_base);
+ return -ENOMEM;
+ }
+
+ ret = sof_ipc4_load_library(sdev, lib_id, lib_filename, true);
+ if (ret)
+ dev_warn(sdev->dev, "%s: Failed to load %s: %d\n",
+ __func__, lib_filename, ret);
+ else
+ lib_id++;
+
+ kfree(lib_filename);
+ }
+
+ kfree(lib_name_base);
+
+ return 0;
+}
+
+static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev,
+ unsigned long lib_id, const guid_t *uuid)
+{
+ const char *lib_filename;
+ int ret;
+
+ if (!sdev->pdata->fw_lib_prefix) {
+ dev_err(sdev->dev,
+ "Library loading is not supported due to not set library path\n");
+ return -EINVAL;
+ }
+
+ lib_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin",
+ sdev->pdata->fw_lib_prefix, uuid);
+ if (!lib_filename)
+ return -ENOMEM;
+
+ ret = sof_ipc4_load_library(sdev, lib_id, lib_filename, false);
+
+ kfree(lib_filename);
+
+ return ret;
+}
+
struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev,
const guid_t *uuid)
{
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index 18fff2df76f9..1a2841899ff5 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -313,7 +313,7 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
set_fifo_size = false;
break;
default:
- dev_err(sdev->dev, "Unexpected state %d", state);
+ spcm_err(spcm, direction, "Unexpected pipeline state %d\n", state);
return -EINVAL;
}
@@ -333,8 +333,8 @@ static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (!pipeline->use_chain_dma) {
- dev_err(sdev->dev,
- "All pipelines in chained DMA stream should have use_chain_dma attribute set.");
+ spcm_err(spcm, direction,
+ "All pipelines in chained DMA path should have use_chain_dma attribute set.");
return -EINVAL;
}
@@ -389,12 +389,12 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
int ret;
int i;
- dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state);
-
spcm = snd_sof_find_spcm_dai(component, rtd);
if (!spcm)
return -EINVAL;
+ spcm_dbg(spcm, substream->stream, "cmd: %d, state: %d\n", cmd, state);
+
pipeline_list = &spcm->stream[substream->stream].pipeline_list;
/* nothing to trigger if the list is empty */
@@ -465,7 +465,7 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
*/
ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list);
if (ret < 0) {
- dev_err(sdev->dev, "failed to pause all pipelines\n");
+ spcm_err(spcm, substream->stream, "failed to pause all pipelines\n");
goto free;
}
@@ -494,7 +494,9 @@ skip_pause_transition:
/* else set the RUNNING/RESET state in the DSP */
ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list);
if (ret < 0) {
- dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state);
+ spcm_err(spcm, substream->stream,
+ "failed to set final state %d for all pipelines\n",
+ state);
/*
* workaround: if the firmware is crashed while setting the
* pipelines to reset state we must ignore the error code and
@@ -610,12 +612,11 @@ static int sof_ipc4_pcm_dai_link_fixup_rate(struct snd_sof_dev *sdev,
* Copier does not change sampling rate, so we
* need to only consider the input pin information.
*/
+ be_rate = pin_fmts[0].audio_fmt.sampling_frequency;
for (i = 0; i < num_input_formats; i++) {
unsigned int val = pin_fmts[i].audio_fmt.sampling_frequency;
- if (i == 0)
- be_rate = val;
- else if (val != be_rate)
+ if (val != be_rate)
single_be_rate = false;
if (val == fe_rate) {
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index ea3323b90343..b798810eff91 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -101,6 +101,7 @@ extern const struct sof_ipc_fw_tracing_ops ipc4_mtrace_ops;
int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 state);
int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core);
+int sof_ipc4_complete_split_release(struct snd_sof_dev *sdev);
int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev);
int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev);
struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev,
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index c04c62478827..a23f55ed5696 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1815,15 +1815,19 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
bool single_output_bitdepth;
int i;
- dev_dbg(sdev->dev, "copier %s, type %d", swidget->widget->name, swidget->id);
-
switch (swidget->id) {
case snd_soc_dapm_aif_in:
case snd_soc_dapm_aif_out:
{
+ struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
+ struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
struct sof_ipc4_gtw_attributes *gtw_attr;
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline;
+
+ dev_dbg(sdev->dev,
+ "Host copier %s, type %d, ChainDMA: %s, stream_tag: %d\n",
+ swidget->widget->name, swidget->id,
+ str_yes_no(pipeline->use_chain_dma),
+ platform_params->stream_tag);
/* parse the deep buffer dma size */
ret = sof_update_ipc_object(scomp, &deep_buffer_dma_ms,
@@ -1840,9 +1844,6 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
- pipe_widget = swidget->spipe->pipe_widget;
- pipeline = pipe_widget->private;
-
if (pipeline->use_chain_dma) {
u32 host_dma_id;
u32 fifo_size;
@@ -1896,6 +1897,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
+ dev_dbg(sdev->dev, "Dai copier %s, type %d, ChainDMA: %s\n",
+ swidget->widget->name, swidget->id,
+ str_yes_no(pipeline->use_chain_dma));
+
if (pipeline->use_chain_dma)
return 0;
@@ -1929,6 +1934,9 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
}
case snd_soc_dapm_buffer:
{
+ dev_dbg(sdev->dev, "Module copier %s, type %d\n",
+ swidget->widget->name, swidget->id);
+
ipc4_copier = (struct sof_ipc4_copier *)swidget->private;
copier_data = &ipc4_copier->data;
available_fmt = &ipc4_copier->available_fmt;
@@ -3401,9 +3409,6 @@ static int sof_ipc4_dai_get_param(struct snd_sof_dev *sdev, struct snd_sof_dai *
static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify)
{
- struct snd_sof_pcm *spcm;
- int dir, ret;
-
/*
* This function is called during system suspend, we need to make sure
* that all streams have been freed up.
@@ -3415,21 +3420,8 @@ static int sof_ipc4_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif
*
* This will also make sure that paused streams handled correctly.
*/
- list_for_each_entry(spcm, &sdev->pcm_list, list) {
- for_each_pcm_streams(dir) {
- struct snd_pcm_substream *substream = spcm->stream[dir].substream;
-
- if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored)
- continue;
- if (spcm->stream[dir].list) {
- ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true);
- if (ret < 0)
- return ret;
- }
- }
- }
- return 0;
+ return sof_pcm_free_all_streams(sdev);
}
static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link)
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 4386cbae16d4..2ed0c52fb2f1 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -825,8 +825,14 @@ static void sof_ipc4_exit(struct snd_sof_dev *sdev)
static int sof_ipc4_post_boot(struct snd_sof_dev *sdev)
{
- if (sdev->first_boot)
+ if (sdev->first_boot) {
+ int ret = sof_ipc4_complete_split_release(sdev);
+
+ if (ret)
+ return ret;
+
return sof_ipc4_query_fw_configuration(sdev);
+ }
return sof_ipc4_reload_fw_libraries(sdev);
}
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
index 7cffcad00f9b..2c2c4cd323fc 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195-clk.c
@@ -8,6 +8,7 @@
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/string_choices.h>
#include "mt8195.h"
#include "mt8195-clk.h"
#include "../adsp_helper.h"
@@ -114,7 +115,7 @@ static int adsp_default_clk_init(struct snd_sof_dev *sdev, bool enable)
struct adsp_priv *priv = sdev->pdata->hw_pdata;
int ret;
- dev_dbg(dev, "%s: %s\n", __func__, enable ? "on" : "off");
+ dev_dbg(dev, "%s: %s\n", __func__, str_on_off(enable));
if (enable) {
ret = clk_set_parent(priv->clk[CLK_TOP_ADSP],
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index 35a7462d8b69..372ed71a17aa 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -99,8 +99,8 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
dpcm_end_walk_at_be);
if (ret < 0) {
- dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
- snd_pcm_direction_name(dir));
+ spcm_err(spcm, dir, "dai %s has no valid %s path\n",
+ dai->name, snd_pcm_direction_name(dir));
return ret;
}
@@ -108,8 +108,7 @@ sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_run
ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir);
if (ret < 0) {
- dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
- spcm->pcm.pcm_id, dir);
+ spcm_err(spcm, dir, "Widget list set up failed\n");
spcm->stream[dir].list = NULL;
snd_soc_dapm_dai_free_widgets(&list);
return ret;
@@ -139,6 +138,8 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
+ spcm_dbg(spcm, substream->stream, "Entry: hw_params\n");
+
/*
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
@@ -151,12 +152,9 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
spcm->prepared[substream->stream] = false;
}
- dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
-
ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params);
if (ret < 0) {
- dev_err(component->dev, "platform hw params failed\n");
+ spcm_err(spcm, substream->stream, "platform hw params failed\n");
return ret;
}
@@ -191,6 +189,84 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
return 0;
}
+static int sof_pcm_stream_free(struct snd_sof_dev *sdev,
+ struct snd_pcm_substream *substream,
+ struct snd_sof_pcm *spcm, int dir,
+ bool free_widget_list)
+{
+ const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
+ int ret;
+ int err = 0;
+
+ if (spcm->prepared[substream->stream]) {
+ /* stop DMA first if needed */
+ if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
+ snd_sof_pcm_platform_trigger(sdev, substream,
+ SNDRV_PCM_TRIGGER_STOP);
+
+ /* free PCM in the DSP */
+ if (pcm_ops && pcm_ops->hw_free) {
+ ret = pcm_ops->hw_free(sdev->component, substream);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "pcm_ops->hw_free failed %d\n", ret);
+ err = ret;
+ }
+ }
+
+ spcm->prepared[substream->stream] = false;
+ spcm->pending_stop[substream->stream] = false;
+ }
+
+ /* reset the DMA */
+ ret = snd_sof_pcm_platform_hw_free(sdev, substream);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "platform hw free failed %d\n", ret);
+ if (!err)
+ err = ret;
+ }
+
+ /* free widget list */
+ if (free_widget_list) {
+ ret = sof_widget_list_free(sdev, spcm, dir);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "sof_widget_list_free failed %d\n", ret);
+ if (!err)
+ err = ret;
+ }
+ }
+
+ return err;
+}
+
+int sof_pcm_free_all_streams(struct snd_sof_dev *sdev)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_sof_pcm *spcm;
+ int dir, ret;
+
+ list_for_each_entry(spcm, &sdev->pcm_list, list) {
+ for_each_pcm_streams(dir) {
+ substream = spcm->stream[dir].substream;
+
+ if (!substream || !substream->runtime ||
+ spcm->stream[dir].suspend_ignored)
+ continue;
+
+ if (spcm->stream[dir].list) {
+ ret = sof_pcm_stream_free(sdev, substream, spcm,
+ dir, true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int sof_pcm_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
@@ -207,8 +283,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: free stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
+ spcm_dbg(spcm, substream->stream, "Entry: hw_free\n");
ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true);
@@ -233,6 +308,8 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
+ spcm_dbg(spcm, substream->stream, "Entry: prepare\n");
+
if (spcm->prepared[substream->stream]) {
if (!spcm->pending_stop[substream->stream])
return 0;
@@ -246,15 +323,12 @@ static int sof_pcm_prepare(struct snd_soc_component *component,
return ret;
}
- dev_dbg(component->dev, "pcm: prepare stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
-
/* set hw_params */
ret = sof_pcm_hw_params(component,
substream, &spcm->params[substream->stream]);
if (ret < 0) {
- dev_err(component->dev,
- "error: set pcm hw_params after resume\n");
+ spcm_err(spcm, substream->stream,
+ "failed to set hw_params after resume\n");
return ret;
}
@@ -284,8 +358,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n",
- spcm->pcm.pcm_id, substream->stream, cmd);
+ spcm_dbg(spcm, substream->stream, "Entry: trigger (cmd: %d)\n", cmd);
spcm->pending_stop[substream->stream] = false;
@@ -334,7 +407,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
reset_hw_params = true;
break;
default:
- dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd);
+ spcm_err(spcm, substream->stream, "Unhandled trigger cmd %d\n", cmd);
return -EINVAL;
}
@@ -436,9 +509,7 @@ static int sof_pcm_open(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: open stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
-
+ spcm_dbg(spcm, substream->stream, "Entry: open\n");
caps = &spcm->pcm.caps[substream->stream];
@@ -458,15 +529,6 @@ static int sof_pcm_open(struct snd_soc_component *component,
*/
runtime->hw.buffer_bytes_max = le32_to_cpu(caps->buffer_size_max);
- dev_dbg(component->dev, "period min %zd max %zd bytes\n",
- runtime->hw.period_bytes_min,
- runtime->hw.period_bytes_max);
- dev_dbg(component->dev, "period count %d max %d\n",
- runtime->hw.periods_min,
- runtime->hw.periods_max);
- dev_dbg(component->dev, "buffer max %zd bytes\n",
- runtime->hw.buffer_bytes_max);
-
/* set wait time - TODO: come from topology */
substream->wait_time = 500;
@@ -476,10 +538,19 @@ static int sof_pcm_open(struct snd_soc_component *component,
spcm->prepared[substream->stream] = false;
ret = snd_sof_pcm_platform_open(sdev, substream);
- if (ret < 0)
- dev_err(component->dev, "error: pcm open failed %d\n", ret);
+ if (ret < 0) {
+ spcm_err(spcm, substream->stream,
+ "platform pcm open failed %d\n", ret);
+ return ret;
+ }
- return ret;
+ spcm_dbg(spcm, substream->stream, "period bytes min %zd, max %zd\n",
+ runtime->hw.period_bytes_min, runtime->hw.period_bytes_max);
+ spcm_dbg(spcm, substream->stream, "period count min %d, max %d\n",
+ runtime->hw.periods_min, runtime->hw.periods_max);
+ spcm_dbg(spcm, substream->stream, "buffer bytes max %zd\n", runtime->hw.buffer_bytes_max);
+
+ return 0;
}
static int sof_pcm_close(struct snd_soc_component *component,
@@ -498,13 +569,12 @@ static int sof_pcm_close(struct snd_soc_component *component,
if (!spcm)
return -EINVAL;
- dev_dbg(component->dev, "pcm: close stream %d dir %d\n",
- spcm->pcm.pcm_id, substream->stream);
+ spcm_dbg(spcm, substream->stream, "Entry: close\n");
err = snd_sof_pcm_platform_close(sdev, substream);
if (err < 0) {
- dev_err(component->dev, "error: pcm close failed %d\n",
- err);
+ spcm_err(spcm, substream->stream,
+ "platform pcm close failed %d\n", err);
/*
* keep going, no point in preventing the close
* from happening
@@ -536,7 +606,8 @@ static int sof_pcm_new(struct snd_soc_component *component,
return 0;
}
- dev_dbg(component->dev, "creating new PCM %s\n", spcm->pcm.pcm_name);
+ dev_dbg(spcm->scomp->dev, "pcm%u (%s): Entry: pcm_construct\n",
+ spcm->pcm.pcm_id, spcm->pcm.pcm_name);
/* do we need to pre-allocate playback audio buffer pages */
if (!spcm->pcm.playback)
@@ -544,16 +615,15 @@ static int sof_pcm_new(struct snd_soc_component *component,
caps = &spcm->pcm.caps[stream];
- /* pre-allocate playback audio buffer pages */
- dev_dbg(component->dev,
- "spcm: allocate %s playback DMA buffer size 0x%x max 0x%x\n",
- caps->name, caps->buffer_size_min, caps->buffer_size_max);
-
if (!pcm->streams[stream].substream) {
- dev_err(component->dev, "error: NULL playback substream!\n");
+ spcm_err(spcm, stream, "NULL playback substream!\n");
return -EINVAL;
}
+ /* pre-allocate playback audio buffer pages */
+ spcm_dbg(spcm, stream, "allocate %s playback DMA buffer size 0x%x max 0x%x\n",
+ caps->name, caps->buffer_size_min, caps->buffer_size_max);
+
snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0, le32_to_cpu(caps->buffer_size_max));
@@ -566,16 +636,15 @@ capture:
caps = &spcm->pcm.caps[stream];
- /* pre-allocate capture audio buffer pages */
- dev_dbg(component->dev,
- "spcm: allocate %s capture DMA buffer size 0x%x max 0x%x\n",
- caps->name, caps->buffer_size_min, caps->buffer_size_max);
-
if (!pcm->streams[stream].substream) {
- dev_err(component->dev, "error: NULL capture substream!\n");
+ spcm_err(spcm, stream, "NULL capture substream!\n");
return -EINVAL;
}
+ /* pre-allocate capture audio buffer pages */
+ spcm_dbg(spcm, stream, "allocate %s capture DMA buffer size 0x%x max 0x%x\n",
+ caps->name, caps->buffer_size_min, caps->buffer_size_max);
+
snd_pcm_set_managed_buffer(pcm->streams[stream].substream,
SNDRV_DMA_TYPE_DEV_SG, sdev->dev,
0, le32_to_cpu(caps->buffer_size_max));
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c
index 9a52781bf8d8..a9664b4cf43f 100644
--- a/sound/soc/sof/sof-audio.c
+++ b/sound/soc/sof/sof-audio.c
@@ -829,55 +829,6 @@ bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev)
return false;
}
-int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
- struct snd_sof_pcm *spcm, int dir, bool free_widget_list)
-{
- const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm);
- int ret;
- int err = 0;
-
- if (spcm->prepared[substream->stream]) {
- /* stop DMA first if needed */
- if (pcm_ops && pcm_ops->platform_stop_during_hw_free)
- snd_sof_pcm_platform_trigger(sdev, substream, SNDRV_PCM_TRIGGER_STOP);
-
- /* free PCM in the DSP */
- if (pcm_ops && pcm_ops->hw_free) {
- ret = pcm_ops->hw_free(sdev->component, substream);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: pcm_ops hw_free failed %d\n",
- __func__, ret);
- err = ret;
- }
- }
-
- spcm->prepared[substream->stream] = false;
- spcm->pending_stop[substream->stream] = false;
- }
-
- /* reset the DMA */
- ret = snd_sof_pcm_platform_hw_free(sdev, substream);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: platform hw free failed %d\n",
- __func__, ret);
- if (!err)
- err = ret;
- }
-
- /* free widget list */
- if (free_widget_list) {
- ret = sof_widget_list_free(sdev, spcm, dir);
- if (ret < 0) {
- dev_err(sdev->dev, "%s: sof_widget_list_free failed %d\n",
- __func__, ret);
- if (!err)
- err = ret;
- }
- }
-
- return err;
-}
-
/*
* Generic object lookup APIs.
*/
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index 62f3c11a9216..36ab75e11779 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -617,6 +617,20 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp,
void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream);
void snd_sof_pcm_init_elapsed_work(struct work_struct *work);
+/*
+ * snd_sof_pcm specific wrappers for dev_dbg() and dev_err() to provide
+ * consistent and useful prints.
+ */
+#define spcm_dbg(__spcm, __dir, __fmt, ...) \
+ dev_dbg((__spcm)->scomp->dev, "pcm%u (%s), dir %d: " __fmt, \
+ (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \
+ ##__VA_ARGS__)
+
+#define spcm_err(__spcm, __dir, __fmt, ...) \
+ dev_err((__spcm)->scomp->dev, "%s: pcm%u (%s), dir %d: " __fmt, \
+ __func__, (__spcm)->pcm.pcm_id, (__spcm)->pcm.pcm_name, __dir, \
+ ##__VA_ARGS__)
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_COMPRESS)
void snd_sof_compr_fragment_elapsed(struct snd_compr_stream *cstream);
void snd_sof_compr_init_elapsed_work(struct work_struct *work);
@@ -649,8 +663,7 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev,
struct snd_sof_pcm *spcm);
-int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream,
- struct snd_sof_pcm *spcm, int dir, bool free_widget_list);
+int sof_pcm_free_all_streams(struct snd_sof_dev *sdev);
int get_token_u32(void *elem, void *object, u32 offset);
int get_token_u16(void *elem, void *object, u32 offset);
int get_token_comp_format(void *elem, void *object, u32 offset);
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 688cc7ac1714..dc9cb8324067 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -1273,8 +1273,8 @@ static int sof_widget_parse_tokens(struct snd_soc_component *scomp, struct snd_s
struct snd_sof_tuple *new_tuples;
num_tuples += token_list[object_token_list[i]].count * (num_sets - 1);
- new_tuples = krealloc(swidget->tuples,
- sizeof(*new_tuples) * num_tuples, GFP_KERNEL);
+ new_tuples = krealloc_array(swidget->tuples,
+ num_tuples, sizeof(*new_tuples), GFP_KERNEL);
if (!new_tuples) {
ret = -ENOMEM;
goto err;
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index cea4b0d54378..defea7f53f11 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -13,7 +13,7 @@ snd-soc-tegra210-dmic-y := tegra210_dmic.o
snd-soc-tegra210-i2s-y := tegra210_i2s.o
snd-soc-tegra186-asrc-y := tegra186_asrc.o
snd-soc-tegra186-dspk-y := tegra186_dspk.o
-snd-soc-tegra210-admaif-y := tegra210_admaif.o
+snd-soc-tegra210-admaif-y := tegra210_admaif.o tegra_isomgr_bw.o
snd-soc-tegra210-mvc-y := tegra210_mvc.o
snd-soc-tegra210-sfc-y := tegra210_sfc.o
snd-soc-tegra210-amx-y := tegra210_amx.o
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index 58fdb0e79954..f56d1e03239d 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES.
+// SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
// All rights reserved.
//
// tegra210_admaif.c - Tegra ADMAIF driver
@@ -13,6 +13,7 @@
#include <linux/regmap.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include "tegra_isomgr_bw.h"
#include "tegra210_admaif.h"
#include "tegra_cif.h"
#include "tegra_pcm.h"
@@ -262,6 +263,18 @@ static int tegra_admaif_set_pack_mode(struct regmap *map, unsigned int reg,
return 0;
}
+static int tegra_admaif_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return tegra_isomgr_adma_setbw(substream, dai, true);
+}
+
+static void tegra_admaif_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ tegra_isomgr_adma_setbw(substream, dai, false);
+}
+
static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -554,6 +567,8 @@ static const struct snd_soc_dai_ops tegra_admaif_dai_ops = {
.probe = tegra_admaif_dai_probe,
.hw_params = tegra_admaif_hw_params,
.trigger = tegra_admaif_trigger,
+ .shutdown = tegra_admaif_shutdown,
+ .prepare = tegra_admaif_prepare,
};
#define DAI(dai_name) \
@@ -800,6 +815,12 @@ static int tegra_admaif_probe(struct platform_device *pdev)
regcache_cache_only(admaif->regmap, true);
+ err = tegra_isomgr_adma_register(&pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add interconnect path\n");
+ return err;
+ }
+
regmap_update_bits(admaif->regmap, admaif->soc_data->global_base +
TEGRA_ADMAIF_GLOBAL_ENABLE, 1, 1);
@@ -851,6 +872,7 @@ static int tegra_admaif_probe(struct platform_device *pdev)
static void tegra_admaif_remove(struct platform_device *pdev)
{
+ tegra_isomgr_adma_unregister(&pdev->dev);
pm_runtime_disable(&pdev->dev);
}
diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h
index 96686dc92081..748f886ee74e 100644
--- a/sound/soc/tegra/tegra210_admaif.h
+++ b/sound/soc/tegra/tegra210_admaif.h
@@ -1,8 +1,8 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * tegra210_admaif.h - Tegra ADMAIF registers
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
+ * All rights reserved.
*
- * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
+ * tegra210_admaif.h - Tegra ADMAIF registers
*
*/
@@ -157,6 +157,7 @@ struct tegra_admaif {
unsigned int *mono_to_stereo[ADMAIF_PATHS];
unsigned int *stereo_to_mono[ADMAIF_PATHS];
struct regmap *regmap;
+ struct tegra_adma_isomgr *adma_isomgr;
};
#endif
diff --git a/sound/soc/tegra/tegra_isomgr_bw.c b/sound/soc/tegra/tegra_isomgr_bw.c
new file mode 100644
index 000000000000..18e802bca6a6
--- /dev/null
+++ b/sound/soc/tegra/tegra_isomgr_bw.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
+// All rights reserved.
+//
+// ADMA bandwidth calculation
+
+#include <linux/interconnect.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "tegra_isomgr_bw.h"
+#include "tegra210_admaif.h"
+
+/* Max possible rate is 192KHz x 16channel x 4bytes */
+#define MAX_BW_PER_DEV 12288
+
+int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai, bool is_running)
+{
+ struct device *dev = dai->dev;
+ struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ struct tegra_adma_isomgr *adma_isomgr = admaif->adma_isomgr;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm *pcm = substream->pcm;
+ u32 type = substream->stream, bandwidth = 0;
+ int sample_bytes;
+
+ if (!adma_isomgr)
+ return 0;
+
+ if (!runtime || !pcm)
+ return -EINVAL;
+
+ if (pcm->device >= adma_isomgr->max_pcm_device) {
+ dev_err(dev, "%s: PCM device number %d is greater than %d\n", __func__,
+ pcm->device, adma_isomgr->max_pcm_device);
+ return -EINVAL;
+ }
+
+ /*
+ * No action if stream is running and bandwidth is already set or
+ * stream is not running and bandwidth is already reset
+ */
+ if ((adma_isomgr->bw_per_dev[type][pcm->device] && is_running) ||
+ (!adma_isomgr->bw_per_dev[type][pcm->device] && !is_running))
+ return 0;
+
+ if (is_running) {
+ sample_bytes = snd_pcm_format_width(runtime->format) / 8;
+ if (sample_bytes < 0)
+ return sample_bytes;
+
+ /* KB/s kilo bytes per sec */
+ bandwidth = runtime->channels * (runtime->rate / 1000) *
+ sample_bytes;
+ }
+
+ mutex_lock(&adma_isomgr->mutex);
+
+ if (is_running) {
+ if (bandwidth + adma_isomgr->current_bandwidth > adma_isomgr->max_bw)
+ bandwidth = adma_isomgr->max_bw - adma_isomgr->current_bandwidth;
+
+ adma_isomgr->current_bandwidth += bandwidth;
+ } else {
+ adma_isomgr->current_bandwidth -= adma_isomgr->bw_per_dev[type][pcm->device];
+ }
+
+ mutex_unlock(&adma_isomgr->mutex);
+
+ adma_isomgr->bw_per_dev[type][pcm->device] = bandwidth;
+
+ dev_dbg(dev, "Setting up bandwidth to %d KBps\n", adma_isomgr->current_bandwidth);
+
+ return icc_set_bw(adma_isomgr->icc_path_handle,
+ adma_isomgr->current_bandwidth, adma_isomgr->max_bw);
+}
+
+int tegra_isomgr_adma_register(struct device *dev)
+{
+ struct tegra_admaif *admaif = dev_get_drvdata(dev);
+ struct tegra_adma_isomgr *adma_isomgr;
+ int i;
+
+ adma_isomgr = devm_kzalloc(dev, sizeof(struct tegra_adma_isomgr), GFP_KERNEL);
+ if (!adma_isomgr)
+ return -ENOMEM;
+
+ adma_isomgr->icc_path_handle = devm_of_icc_get(dev, "write");
+ if (IS_ERR(adma_isomgr->icc_path_handle))
+ return dev_err_probe(dev, PTR_ERR(adma_isomgr->icc_path_handle),
+ "failed to acquire interconnect path\n");
+
+ /* Either INTERCONNECT config OR interconnect property is not defined */
+ if (!adma_isomgr->icc_path_handle) {
+ devm_kfree(dev, adma_isomgr);
+ return 0;
+ }
+
+ adma_isomgr->max_pcm_device = admaif->soc_data->num_ch;
+ adma_isomgr->max_bw = STREAM_TYPE * MAX_BW_PER_DEV * adma_isomgr->max_pcm_device;
+
+ for (i = 0; i < STREAM_TYPE; i++) {
+ adma_isomgr->bw_per_dev[i] = devm_kzalloc(dev, adma_isomgr->max_pcm_device *
+ sizeof(u32), GFP_KERNEL);
+ if (!adma_isomgr->bw_per_dev[i])
+ return -ENOMEM;
+ }
+
+ adma_isomgr->current_bandwidth = 0;
+ mutex_init(&adma_isomgr->mutex);
+ admaif->adma_isomgr = adma_isomgr;
+
+ return 0;
+}
+
+void tegra_isomgr_adma_unregister(struct device *dev)
+{
+ struct tegra_admaif *admaif = dev_get_drvdata(dev);
+
+ if (!admaif->adma_isomgr)
+ return;
+
+ mutex_destroy(&admaif->adma_isomgr->mutex);
+}
+
+MODULE_AUTHOR("Mohan Kumar <mkumard@nvidia.com>");
+MODULE_DESCRIPTION("Tegra ADMA Bandwidth Request driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra/tegra_isomgr_bw.h b/sound/soc/tegra/tegra_isomgr_bw.h
new file mode 100644
index 000000000000..86db3cfd4e43
--- /dev/null
+++ b/sound/soc/tegra/tegra_isomgr_bw.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES.
+ * All rights reserved.
+ *
+ * tegra_isomgr_bw.h - Definitions for ADMA bandwidth calculation
+ *
+ */
+
+#ifndef __TEGRA_ISOMGR_BW_H__
+#define __TEGRA_ISOMGR_BW_H__
+
+/* Playback and Capture streams */
+#define STREAM_TYPE 2
+
+struct tegra_adma_isomgr {
+ /* Protect pcm devices bandwidth */
+ struct mutex mutex;
+ /* interconnect path handle */
+ struct icc_path *icc_path_handle;
+ u32 *bw_per_dev[STREAM_TYPE];
+ u32 current_bandwidth;
+ u32 max_pcm_device;
+ u32 max_bw;
+};
+
+int tegra_isomgr_adma_register(struct device *dev);
+void tegra_isomgr_adma_unregister(struct device *dev);
+int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai, bool is_running);
+
+#endif