aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/sound/nau8821.txt2
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml8
-rw-r--r--Documentation/devicetree/bindings/sound/tas2780.yaml72
-rw-r--r--Documentation/sound/soc/dai.rst2
-rw-r--r--include/sound/simple_card_utils.h3
-rw-r--r--sound/soc/amd/acp-es8336.c15
-rw-r--r--sound/soc/amd/acp/Kconfig11
-rw-r--r--sound/soc/amd/acp/Makefile2
-rw-r--r--sound/soc/amd/acp/acp-i2s.c169
-rw-r--r--sound/soc/amd/acp/acp-legacy-mach.c32
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c104
-rw-r--r--sound/soc/amd/acp/acp-mach.h6
-rw-r--r--sound/soc/amd/acp/acp-pci.c6
-rw-r--r--sound/soc/amd/acp/acp-pdm.c8
-rw-r--r--sound/soc/amd/acp/acp-platform.c32
-rw-r--r--sound/soc/amd/acp/acp-rembrandt.c401
-rw-r--r--sound/soc/amd/acp/acp-renoir.c38
-rw-r--r--sound/soc/amd/acp/amd.h86
-rw-r--r--sound/soc/amd/acp/chip_offset_byte.h40
-rw-r--r--sound/soc/atmel/mchp-pdmc.c2
-rw-r--r--sound/soc/codecs/Kconfig8
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/arizona.c4
-rw-r--r--sound/soc/codecs/cs47l92.c8
-rw-r--r--sound/soc/codecs/nau8821.c35
-rw-r--r--sound/soc/codecs/nau8821.h1
-rw-r--r--sound/soc/codecs/nau8825.c2
-rw-r--r--sound/soc/codecs/rt274.c1
-rw-r--r--sound/soc/codecs/rt286.c1
-rw-r--r--sound/soc/codecs/rt298.c43
-rw-r--r--sound/soc/codecs/rt5640.c30
-rw-r--r--sound/soc/codecs/tas2764.c46
-rw-r--r--sound/soc/codecs/tas2764.h6
-rw-r--r--sound/soc/codecs/tas2780.c663
-rw-r--r--sound/soc/codecs/tas2780.h101
-rw-r--r--sound/soc/codecs/tlv320adcx140.c13
-rw-r--r--sound/soc/codecs/wcd9335.c17
-rw-r--r--sound/soc/codecs/wm5102.c21
-rw-r--r--sound/soc/codecs/wm8998.c21
-rw-r--r--sound/soc/fsl/fsl_utils.c2
-rw-r--r--sound/soc/generic/audio-graph-card2-custom-sample.dtsi101
-rw-r--r--sound/soc/generic/audio-graph-card2.c39
-rw-r--r--sound/soc/generic/simple-card-utils.c26
-rw-r--r--sound/soc/intel/boards/sof_cs42l42.c86
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c10
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-adl-match.c7
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c40
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-dai-adda.c2
-rw-r--r--sound/soc/qcom/qdsp6/q6apm.c1
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c1
-rw-r--r--sound/soc/sof/ipc3-topology.c13
-rw-r--r--sound/soc/sof/ipc3.c11
-rw-r--r--sound/soc/sof/ipc4-topology.c59
-rw-r--r--sound/soc/ti/omap-mcbsp-priv.h2
-rw-r--r--sound/soc/ti/omap-mcbsp-st.c14
-rw-r--r--sound/soc/ti/omap-mcbsp.c19
56 files changed, 2207 insertions, 288 deletions
diff --git a/Documentation/devicetree/bindings/sound/nau8821.txt b/Documentation/devicetree/bindings/sound/nau8821.txt
index 6c3baf7a5f21..7c84e7c7327a 100644
--- a/Documentation/devicetree/bindings/sound/nau8821.txt
+++ b/Documentation/devicetree/bindings/sound/nau8821.txt
@@ -34,7 +34,7 @@ Optional properties:
- nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
- nuvoton,dmic-clk-threshold: the ADC threshold of DMIC clock.
-
+ - nuvoton,key_enable: Headset button detection switch.
Example:
diff --git a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
index e9a533080b32..ef18a572a1ff 100644
--- a/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,lpass-cpu.yaml
@@ -25,12 +25,12 @@ properties:
- qcom,sc7280-lpass-cpu
reg:
- minItems: 2
+ minItems: 1
maxItems: 6
description: LPAIF core registers
reg-names:
- minItems: 2
+ minItems: 1
maxItems: 6
clocks:
@@ -42,12 +42,12 @@ properties:
maxItems: 10
interrupts:
- minItems: 2
+ minItems: 1
maxItems: 4
description: LPAIF DMA buffer interrupt
interrupt-names:
- minItems: 2
+ minItems: 1
maxItems: 4
qcom,adsp:
diff --git a/Documentation/devicetree/bindings/sound/tas2780.yaml b/Documentation/devicetree/bindings/sound/tas2780.yaml
new file mode 100644
index 000000000000..5a0de0995066
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/tas2780.yaml
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2020-2022 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/sound/tas2780.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Texas Instruments TAS2780 Smart PA
+
+maintainers:
+ - Raphael Xu <[email protected]>
+
+description: |
+ The TAS2780 is a mono, digital input Class-D audio amplifier optimized for
+ efficiently driving high peak power into small loudspeakers.
+ Integrated speaker voltage and current sense provides for
+ real time monitoring of loudspeaker behavior.
+
+properties:
+ compatible:
+ enum:
+ - ti,tas2780
+
+ reg:
+ maxItems: 1
+ description: |
+ I2C address of the device can be between 0x38 to 0x45.
+
+ reset-gpios:
+ maxItems: 1
+ description: GPIO used to reset the device.
+
+ interrupts:
+ maxItems: 1
+
+ ti,imon-slot-no:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: TDM TX current sense time slot.
+
+ ti,vmon-slot-no:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: TDM TX voltage sense time slot.
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ i2c0 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ codec: codec@38 {
+ compatible = "ti,tas2780";
+ reg = <0x38>;
+ #sound-dai-cells = <1>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <14>;
+ reset-gpios = <&gpio1 15 0>;
+ shutdown-gpios = <&gpio1 15 0>;
+ ti,imon-slot-no = <0>;
+ ti,vmon-slot-no = <2>;
+ };
+ };
+
+...
diff --git a/Documentation/sound/soc/dai.rst b/Documentation/sound/soc/dai.rst
index 009b07e5a0f3..bf8431386d26 100644
--- a/Documentation/sound/soc/dai.rst
+++ b/Documentation/sound/soc/dai.rst
@@ -10,7 +10,7 @@ AC97
====
AC97 is a five wire interface commonly found on many PC sound cards. It is
-now also popular in many portable devices. This DAI has a reset line and time
+now also popular in many portable devices. This DAI has a RESET line and time
multiplexes its data on its SDATA_OUT (playback) and SDATA_IN (capture) lines.
The bit clock (BCLK) is always driven by the CODEC (usually 12.288MHz) and the
frame (FRAME) (usually 48kHz) is always driven by the controller. Each AC97
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index fe2337fde1f4..ab55f40896e0 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -51,7 +51,6 @@ struct prop_nums {
int cpus;
int codecs;
int platforms;
- int c2c;
};
struct asoc_simple_priv {
@@ -64,7 +63,6 @@ struct asoc_simple_priv {
struct snd_soc_dai_link_component *platforms;
struct asoc_simple_data adata;
struct snd_soc_codec_conf *codec_conf;
- struct snd_soc_pcm_stream *c2c_conf;
struct prop_nums num;
unsigned int mclk_fs;
} *dai_props;
@@ -75,7 +73,6 @@ struct asoc_simple_priv {
struct snd_soc_dai_link_component *dlcs;
struct snd_soc_dai_link_component dummy;
struct snd_soc_codec_conf *codec_conf;
- struct snd_soc_pcm_stream *c2c_conf;
struct gpio_desc *pa_gpio;
const struct snd_soc_ops *ops;
unsigned int dpcm_selectable:1;
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
index eec3d57092fa..d501618b78f6 100644
--- a/sound/soc/amd/acp-es8336.c
+++ b/sound/soc/amd/acp-es8336.c
@@ -20,10 +20,8 @@
#include <linux/input.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/module.h>
#include <linux/acpi.h>
-#include "../codecs/es8316.h"
#include "acp.h"
#define DUAL_CHANNEL 2
@@ -33,8 +31,8 @@
static unsigned long acp2x_machine_id;
static struct snd_soc_jack st_jack;
-struct device *codec_dev;
-struct gpio_desc *gpio_pa;
+static struct device *codec_dev;
+static struct gpio_desc *gpio_pa;
static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
@@ -207,6 +205,8 @@ static int st_es8336_late_probe(struct snd_soc_card *card)
dev_err(card->dev, "can not find codec dev\n");
ret = devm_acpi_dev_add_driver_gpios(codec_dev, acpi_es8336_gpios);
+ if (ret)
+ dev_warn(card->dev, "Failed to add driver gpios\n");
gpio_pa = gpiod_get_optional(codec_dev, "pa-enable", GPIOD_OUT_LOW);
if (IS_ERR(gpio_pa)) {
@@ -214,6 +214,7 @@ static int st_es8336_late_probe(struct snd_soc_card *card)
"could not get pa-enable GPIO\n");
gpiod_put(gpio_pa);
put_device(codec_dev);
+ return ret;
}
return 0;
}
@@ -294,11 +295,6 @@ static int st_es8336_probe(struct platform_device *pdev)
return 0;
}
-static int st_es8336_remove(struct platform_device *pdev)
-{
- return 0;
-}
-
#ifdef CONFIG_ACPI
static const struct acpi_device_id st_audio_acpi_match[] = {
{"AMDI8336", 0},
@@ -314,7 +310,6 @@ static struct platform_driver st_mach_driver = {
.pm = &snd_soc_pm_ops,
},
.probe = st_es8336_probe,
- .remove = st_es8336_remove,
};
module_platform_driver(st_mach_driver);
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index 7e56d2644105..ce0037810743 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -40,6 +40,17 @@ config SND_AMD_ASOC_RENOIR
help
This option enables Renoir I2S support on AMD platform.
+config SND_AMD_ASOC_REMBRANDT
+ tristate "AMD ACP ASOC Rembrandt Support"
+ select SND_SOC_AMD_ACP_PCM
+ select SND_SOC_AMD_ACP_I2S
+ select SND_SOC_AMD_ACP_PDM
+ depends on X86 && PCI
+ help
+ This option enables Rembrandt I2S support on AMD platform.
+ Say Y if you want to enable AUDIO on Rembrandt
+ If unsure select "N".
+
config SND_SOC_AMD_MACH_COMMON
tristate
depends on X86 && PCI && I2C
diff --git a/sound/soc/amd/acp/Makefile b/sound/soc/amd/acp/Makefile
index 657ddfadf0bb..d9abb0ee5218 100644
--- a/sound/soc/amd/acp/Makefile
+++ b/sound/soc/amd/acp/Makefile
@@ -12,6 +12,7 @@ snd-acp-pci-objs := acp-pci.o
#platform specific driver
snd-acp-renoir-objs := acp-renoir.o
+snd-acp-rembrandt-objs := acp-rembrandt.o
#machine specific driver
snd-acp-mach-objs := acp-mach-common.o
@@ -24,6 +25,7 @@ obj-$(CONFIG_SND_SOC_AMD_ACP_PDM) += snd-acp-pdm.o
obj-$(CONFIG_SND_SOC_AMD_ACP_PCI) += snd-acp-pci.o
obj-$(CONFIG_SND_AMD_ASOC_RENOIR) += snd-acp-renoir.o
+obj-$(CONFIG_SND_AMD_ASOC_REMBRANDT) += snd-acp-rembrandt.o
obj-$(CONFIG_SND_SOC_AMD_MACH_COMMON) += snd-acp-mach.o
obj-$(CONFIG_SND_SOC_AMD_LEGACY_MACH) += snd-acp-legacy-mach.o
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
index ce9aca8dd6f5..393f729ef561 100644
--- a/sound/soc/amd/acp/acp-i2s.c
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -30,11 +30,14 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
{
struct device *dev = dai->component->dev;
struct acp_dev_data *adata;
+ struct acp_resource *rsrc;
u32 val;
u32 xfer_resolution;
u32 reg_val;
+ u32 lrclk_div_val, bclk_div_val;
adata = snd_soc_dai_get_drvdata(dai);
+ rsrc = adata->rsrc;
/* These values are as per Hardware Spec */
switch (params_format(params)) {
@@ -63,6 +66,9 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_ITER;
break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -75,6 +81,9 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_IRER;
break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -86,6 +95,74 @@ static int acp_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_
val = val | (xfer_resolution << 3);
writel(val, adata->acp_base + reg_val);
+ if (rsrc->soc_mclk) {
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 768;
+ break;
+ case 16000:
+ bclk_div_val = 384;
+ break;
+ case 24000:
+ bclk_div_val = 256;
+ break;
+ case 32000:
+ bclk_div_val = 192;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 128;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 64;
+ break;
+ case 192000:
+ bclk_div_val = 32;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 32;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ switch (params_rate(params)) {
+ case 8000:
+ bclk_div_val = 384;
+ break;
+ case 16000:
+ bclk_div_val = 192;
+ break;
+ case 24000:
+ bclk_div_val = 128;
+ break;
+ case 32000:
+ bclk_div_val = 96;
+ break;
+ case 44100:
+ case 48000:
+ bclk_div_val = 64;
+ break;
+ case 88200:
+ case 96000:
+ bclk_div_val = 32;
+ break;
+ case 192000:
+ bclk_div_val = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ lrclk_div_val = 64;
+ break;
+ default:
+ return -EINVAL;
+ }
+ adata->lrclk_div = lrclk_div_val;
+ adata->bclk_div = bclk_div_val;
+ }
return 0;
}
@@ -94,6 +171,7 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
struct acp_stream *stream = substream->runtime->private_data;
struct device *dev = dai->component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
u32 val, period_bytes, reg_val, ier_val, water_val, buf_size, buf_reg;
period_bytes = frames_to_bytes(substream->runtime, substream->runtime->period_size);
@@ -118,6 +196,12 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
ier_val = ACP_I2STDM_IER;
buf_reg = ACP_I2S_TX_RINGBUFSIZE;
break;
+ case I2S_HS_INSTANCE:
+ water_val = ACP_HS_TX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_ITER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_TX_RINGBUFSIZE;
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -136,6 +220,12 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
ier_val = ACP_I2STDM_IER;
buf_reg = ACP_I2S_RX_RINGBUFSIZE;
break;
+ case I2S_HS_INSTANCE:
+ water_val = ACP_HS_RX_INTR_WATERMARK_SIZE;
+ reg_val = ACP_HSTDM_IRER;
+ ier_val = ACP_HSTDM_IER;
+ buf_reg = ACP_HS_RX_RINGBUFSIZE;
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -147,6 +237,8 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
val = val | BIT(0);
writel(val, adata->acp_base + reg_val);
writel(1, adata->acp_base + ier_val);
+ if (rsrc->soc_mclk)
+ acp_set_i2s_clk(adata, dai->driver->id);
return 0;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -159,6 +251,9 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_ITER;
break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_ITER;
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -172,6 +267,9 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
case I2S_SP_INSTANCE:
reg_val = ACP_I2STDM_IRER;
break;
+ case I2S_HS_INSTANCE:
+ reg_val = ACP_HSTDM_IRER;
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -187,6 +285,9 @@ static int acp_i2s_trigger(struct snd_pcm_substream *substream, int cmd, struct
if (!(readl(adata->acp_base + ACP_I2STDM_ITER) & BIT(0)) &&
!(readl(adata->acp_base + ACP_I2STDM_IRER) & BIT(0)))
writel(0, adata->acp_base + ACP_I2STDM_IER);
+ if (!(readl(adata->acp_base + ACP_HSTDM_ITER) & BIT(0)) &&
+ !(readl(adata->acp_base + ACP_HSTDM_IRER) & BIT(0)))
+ writel(0, adata->acp_base + ACP_HSTDM_IER);
return 0;
default:
return -EINVAL;
@@ -199,6 +300,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
{
struct device *dev = dai->component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
struct acp_stream *stream = substream->runtime->private_data;
u32 reg_dma_size = 0, reg_fifo_size = 0, reg_fifo_addr = 0;
u32 phy_addr = 0, acp_fifo_addr = 0, ext_int_ctrl;
@@ -208,7 +310,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
case I2S_SP_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
reg_dma_size = ACP_I2S_TX_DMA_SIZE;
- acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ acp_fifo_addr = rsrc->sram_pte_offset +
SP_PB_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_I2S_TX_FIFOADDR;
reg_fifo_size = ACP_I2S_TX_FIFOSIZE;
@@ -217,7 +319,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
writel(phy_addr, adata->acp_base + ACP_I2S_TX_RINGBUFADDR);
} else {
reg_dma_size = ACP_I2S_RX_DMA_SIZE;
- acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ acp_fifo_addr = rsrc->sram_pte_offset +
SP_CAPT_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_I2S_RX_FIFOADDR;
reg_fifo_size = ACP_I2S_RX_FIFOSIZE;
@@ -228,7 +330,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
case I2S_BT_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
reg_dma_size = ACP_BT_TX_DMA_SIZE;
- acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ acp_fifo_addr = rsrc->sram_pte_offset +
BT_PB_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_BT_TX_FIFOADDR;
reg_fifo_size = ACP_BT_TX_FIFOSIZE;
@@ -237,7 +339,7 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
writel(phy_addr, adata->acp_base + ACP_BT_TX_RINGBUFADDR);
} else {
reg_dma_size = ACP_BT_RX_DMA_SIZE;
- acp_fifo_addr = ACP_SRAM_PTE_OFFSET +
+ acp_fifo_addr = rsrc->sram_pte_offset +
BT_CAPT_FIFO_ADDR_OFFSET;
reg_fifo_addr = ACP_BT_RX_FIFOADDR;
reg_fifo_size = ACP_BT_RX_FIFOSIZE;
@@ -246,6 +348,27 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
writel(phy_addr, adata->acp_base + ACP_BT_RX_RINGBUFADDR);
}
break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg_dma_size = ACP_HS_TX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_PB_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_TX_FIFOADDR;
+ reg_fifo_size = ACP_HS_TX_FIFOSIZE;
+
+ phy_addr = I2S_HS_TX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_HS_TX_RINGBUFADDR);
+ } else {
+ reg_dma_size = ACP_HS_RX_DMA_SIZE;
+ acp_fifo_addr = rsrc->sram_pte_offset +
+ HS_CAPT_FIFO_ADDR_OFFSET;
+ reg_fifo_addr = ACP_HS_RX_FIFOADDR;
+ reg_fifo_size = ACP_HS_RX_FIFOSIZE;
+
+ phy_addr = I2S_HS_RX_MEM_WINDOW_START + stream->reg_offset;
+ writel(phy_addr, adata->acp_base + ACP_HS_RX_RINGBUFADDR);
+ }
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -255,11 +378,15 @@ static int acp_i2s_prepare(struct snd_pcm_substream *substream, struct snd_soc_d
writel(acp_fifo_addr, adata->acp_base + reg_fifo_addr);
writel(FIFO_SIZE, adata->acp_base + reg_fifo_size);
- ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
- ext_int_ctrl |= BIT(I2S_RX_THRESHOLD) | BIT(BT_RX_THRESHOLD)
- | BIT(I2S_TX_THRESHOLD) | BIT(BT_TX_THRESHOLD);
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_int_ctrl |= BIT(I2S_RX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_RX_THRESHOLD(rsrc->offset)) |
+ BIT(I2S_TX_THRESHOLD(rsrc->offset)) |
+ BIT(BT_TX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_RX_THRESHOLD(rsrc->offset)) |
+ BIT(HS_TX_THRESHOLD(rsrc->offset));
- writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
return 0;
}
@@ -268,32 +395,45 @@ static int acp_i2s_startup(struct snd_pcm_substream *substream, struct snd_soc_d
{
struct acp_stream *stream = substream->runtime->private_data;
struct device *dev = dai->component->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
unsigned int dir = substream->stream;
unsigned int irq_bit = 0;
switch (dai->driver->id) {
case I2S_SP_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
- irq_bit = BIT(I2S_TX_THRESHOLD);
+ irq_bit = BIT(I2S_TX_THRESHOLD(rsrc->offset));
stream->pte_offset = ACP_SRAM_SP_PB_PTE_OFFSET;
stream->fifo_offset = SP_PB_FIFO_ADDR_OFFSET;
} else {
- irq_bit = BIT(I2S_RX_THRESHOLD);
+ irq_bit = BIT(I2S_RX_THRESHOLD(rsrc->offset));
stream->pte_offset = ACP_SRAM_SP_CP_PTE_OFFSET;
stream->fifo_offset = SP_CAPT_FIFO_ADDR_OFFSET;
}
break;
case I2S_BT_INSTANCE:
if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
- irq_bit = BIT(BT_TX_THRESHOLD);
+ irq_bit = BIT(BT_TX_THRESHOLD(rsrc->offset));
stream->pte_offset = ACP_SRAM_BT_PB_PTE_OFFSET;
stream->fifo_offset = BT_PB_FIFO_ADDR_OFFSET;
} else {
- irq_bit = BIT(BT_RX_THRESHOLD);
+ irq_bit = BIT(BT_RX_THRESHOLD(rsrc->offset));
stream->pte_offset = ACP_SRAM_BT_CP_PTE_OFFSET;
stream->fifo_offset = BT_CAPT_FIFO_ADDR_OFFSET;
}
break;
+ case I2S_HS_INSTANCE:
+ if (dir == SNDRV_PCM_STREAM_PLAYBACK) {
+ irq_bit = BIT(HS_TX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_HS_PB_PTE_OFFSET;
+ stream->fifo_offset = HS_PB_FIFO_ADDR_OFFSET;
+ } else {
+ irq_bit = BIT(HS_RX_THRESHOLD(rsrc->offset));
+ stream->pte_offset = ACP_SRAM_HS_CP_PTE_OFFSET;
+ stream->fifo_offset = HS_CAPT_FIFO_ADDR_OFFSET;
+ }
+ break;
default:
dev_err(dev, "Invalid dai id %x\n", dai->driver->id);
return -EINVAL;
@@ -319,6 +459,7 @@ int asoc_acp_i2s_probe(struct snd_soc_dai *dai)
{
struct device *dev = dai->component->dev;
struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_resource *rsrc = adata->rsrc;
unsigned int val;
if (!adata->acp_base) {
@@ -326,8 +467,8 @@ int asoc_acp_i2s_probe(struct snd_soc_dai *dai)
return -EINVAL;
}
- val = readl(adata->acp_base + ACP_I2S_PIN_CONFIG);
- if (val != I2S_MODE) {
+ val = readl(adata->acp_base + rsrc->i2s_pin_cfg_offset);
+ if (val != rsrc->i2s_mode) {
dev_err(dev, "I2S Mode not supported val %x\n", val);
return -EINVAL;
}
diff --git a/sound/soc/amd/acp/acp-legacy-mach.c b/sound/soc/amd/acp/acp-legacy-mach.c
index 7f04a048ca3a..1f4878ff7d37 100644
--- a/sound/soc/amd/acp/acp-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-legacy-mach.c
@@ -47,6 +47,28 @@ static struct acp_card_drvdata rt5682s_rt1019_data = {
.dmic_codec_id = DMIC,
};
+static struct acp_card_drvdata max_nau8825_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = NAU8825,
+ .amp_codec_id = MAX98360A,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .platform = REMBRANDT,
+};
+
+static struct acp_card_drvdata rt5682s_rt1019_rmb_data = {
+ .hs_cpu_id = I2S_HS,
+ .amp_cpu_id = I2S_HS,
+ .dmic_cpu_id = DMIC,
+ .hs_codec_id = RT5682S,
+ .amp_codec_id = RT1019,
+ .dmic_codec_id = DMIC,
+ .soc_mclk = true,
+ .platform = REMBRANDT,
+};
+
static const struct snd_kcontrol_new acp_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -112,6 +134,14 @@ static const struct platform_device_id board_ids[] = {
.name = "acp3xalc5682s1019",
.driver_data = (kernel_ulong_t)&rt5682s_rt1019_data,
},
+ {
+ .name = "rmb-nau8825-max",
+ .driver_data = (kernel_ulong_t)&max_nau8825_data,
+ },
+ {
+ .name = "rmb-rt5682s-rt1019",
+ .driver_data = (kernel_ulong_t)&rt5682s_rt1019_rmb_data,
+ },
{ }
};
static struct platform_driver acp_asoc_audio = {
@@ -130,4 +160,6 @@ MODULE_DESCRIPTION("ACP chrome audio support");
MODULE_ALIAS("platform:acp3xalc56821019");
MODULE_ALIAS("platform:acp3xalc5682sm98360");
MODULE_ALIAS("platform:acp3xalc5682s1019");
+MODULE_ALIAS("platform:rmb-nau8825-max");
+MODULE_ALIAS("platform:rmb-rt5682s-rt1019");
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index 7530cab24bc8..f0c49127aad1 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -313,9 +313,6 @@ static const struct snd_soc_ops acp_card_dmic_ops = {
SND_SOC_DAILINK_DEF(rt1019,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:00", "rt1019-aif"),
COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
-SND_SOC_DAILINK_DEF(rt1019_1,
- DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC1019:02", "rt1019-aif"),
- COMP_CODEC("i2c-10EC1019:01", "rt1019-aif")));
static const struct snd_soc_dapm_route rt1019_map_lr[] = {
{ "Left Spk", NULL, "Left SPO" },
@@ -333,17 +330,6 @@ static struct snd_soc_codec_conf rt1019_conf[] = {
},
};
-static struct snd_soc_codec_conf rt1019_1_conf[] = {
- {
- .dlc = COMP_CODEC_CONF("i2c-10EC1019:02"),
- .name_prefix = "Left",
- },
- {
- .dlc = COMP_CODEC_CONF("i2c-10EC1019:01"),
- .name_prefix = "Right",
- },
-};
-
static int acp_card_rt1019_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_card *card = rtd->card;
@@ -559,6 +545,12 @@ static struct snd_soc_dai_link_component platform_component[] = {
}
};
+static struct snd_soc_dai_link_component platform_rmb_component[] = {
+ {
+ .name = "acp_asoc_rembrandt.0",
+ }
+};
+
static struct snd_soc_dai_link_component sof_component[] = {
{
.name = "0000:04:00.5",
@@ -567,6 +559,8 @@ static struct snd_soc_dai_link_component sof_component[] = {
SND_SOC_DAILINK_DEF(i2s_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-sp")));
+SND_SOC_DAILINK_DEF(i2s_hs,
+ DAILINK_COMP_ARRAY(COMP_CPU("acp-i2s-hs")));
SND_SOC_DAILINK_DEF(sof_sp,
DAILINK_COMP_ARRAY(COMP_CPU("acp-sof-sp")));
SND_SOC_DAILINK_DEF(sof_hs,
@@ -716,10 +710,6 @@ int acp_sofdsp_dai_links_create(struct snd_soc_card *card)
links[i].init = acp_card_rt1019_init;
card->codec_conf = rt1019_conf;
card->num_configs = ARRAY_SIZE(rt1019_conf);
- links[i].codecs = rt1019_1;
- links[i].num_codecs = ARRAY_SIZE(rt1019_1);
- card->codec_conf = rt1019_1_conf;
- card->num_configs = ARRAY_SIZE(rt1019_1_conf);
}
i++;
}
@@ -792,6 +782,40 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
i++;
}
+ if (drv_data->hs_cpu_id == I2S_HS) {
+ links[i].name = "acp-headset-codec";
+ links[i].id = HEADSET_BE_ID;
+ links[i].cpus = i2s_hs;
+ links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+ if (drv_data->platform == REMBRANDT) {
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ } else {
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ }
+ links[i].dpcm_playback = 1;
+ links[i].dpcm_capture = 1;
+ if (!drv_data->hs_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->hs_codec_id == NAU8825) {
+ links[i].codecs = nau8825;
+ links[i].num_codecs = ARRAY_SIZE(nau8825);
+ links[i].init = acp_card_nau8825_init;
+ links[i].ops = &acp_card_nau8825_ops;
+ }
+ if (drv_data->hs_codec_id == RT5682S) {
+ links[i].codecs = rt5682s;
+ links[i].num_codecs = ARRAY_SIZE(rt5682s);
+ links[i].init = acp_card_rt5682s_init;
+ links[i].ops = &acp_card_rt5682s_ops;
+ }
+ i++;
+ }
+
if (drv_data->amp_cpu_id == I2S_SP) {
links[i].name = "acp-amp-codec";
links[i].id = AMP_BE_ID;
@@ -822,6 +846,41 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
i++;
}
+ if (drv_data->amp_cpu_id == I2S_HS) {
+ links[i].name = "acp-amp-codec";
+ links[i].id = AMP_BE_ID;
+ links[i].cpus = i2s_hs;
+ links[i].num_cpus = ARRAY_SIZE(i2s_hs);
+ if (drv_data->platform == REMBRANDT) {
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ } else {
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ }
+ links[i].dpcm_playback = 1;
+ if (!drv_data->amp_codec_id) {
+ /* Use dummy codec if codec id not specified */
+ links[i].codecs = dummy_codec;
+ links[i].num_codecs = ARRAY_SIZE(dummy_codec);
+ }
+ if (drv_data->amp_codec_id == MAX98360A) {
+ links[i].codecs = max98360a;
+ links[i].num_codecs = ARRAY_SIZE(max98360a);
+ links[i].ops = &acp_card_maxim_ops;
+ links[i].init = acp_card_maxim_init;
+ }
+ if (drv_data->amp_codec_id == RT1019) {
+ links[i].codecs = rt1019;
+ links[i].num_codecs = ARRAY_SIZE(rt1019);
+ links[i].ops = &acp_card_rt1019_ops;
+ links[i].init = acp_card_rt1019_init;
+ card->codec_conf = rt1019_conf;
+ card->num_configs = ARRAY_SIZE(rt1019_conf);
+ }
+ i++;
+ }
+
if (drv_data->dmic_cpu_id == DMIC) {
links[i].name = "acp-dmic-codec";
links[i].id = DMIC_BE_ID;
@@ -835,8 +894,13 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
}
links[i].cpus = pdm_dmic;
links[i].num_cpus = ARRAY_SIZE(pdm_dmic);
- links[i].platforms = platform_component;
- links[i].num_platforms = ARRAY_SIZE(platform_component);
+ if (drv_data->platform == REMBRANDT) {
+ links[i].platforms = platform_rmb_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_rmb_component);
+ } else {
+ links[i].platforms = platform_component;
+ links[i].num_platforms = ARRAY_SIZE(platform_component);
+ }
links[i].ops = &acp_card_dmic_ops;
links[i].dpcm_capture = 1;
}
diff --git a/sound/soc/amd/acp/acp-mach.h b/sound/soc/amd/acp/acp-mach.h
index c95ee1c52eb1..20583ef902df 100644
--- a/sound/soc/amd/acp/acp-mach.h
+++ b/sound/soc/amd/acp/acp-mach.h
@@ -41,6 +41,11 @@ enum codec_endpoints {
NAU8825,
};
+enum platform_end_point {
+ RENOIR = 0,
+ REMBRANDT,
+};
+
struct acp_card_drvdata {
unsigned int hs_cpu_id;
unsigned int amp_cpu_id;
@@ -49,6 +54,7 @@ struct acp_card_drvdata {
unsigned int amp_codec_id;
unsigned int dmic_codec_id;
unsigned int dai_fmt;
+ unsigned int platform;
struct clk *wclk;
struct clk *bclk;
bool soc_mclk;
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index c893963ee2d0..c03bcd31fc95 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -82,6 +82,12 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
chip->name = "acp_asoc_renoir";
chip->acp_rev = ACP3X_DEV;
break;
+ case 0x6f:
+ res_acp = acp3x_res;
+ num_res = ARRAY_SIZE(acp3x_res);
+ chip->name = "acp_asoc_rembrandt";
+ chip->acp_rev = ACP6X_DEV;
+ break;
default:
dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
return -EINVAL;
diff --git a/sound/soc/amd/acp/acp-pdm.c b/sound/soc/amd/acp/acp-pdm.c
index 7a0b26a30051..66ec6b6a5972 100644
--- a/sound/soc/amd/acp/acp-pdm.c
+++ b/sound/soc/amd/acp/acp-pdm.c
@@ -160,9 +160,9 @@ static int acp_dmic_dai_startup(struct snd_pcm_substream *substream,
stream->reg_offset = ACP_REGION2_OFFSET;
/* Enable DMIC Interrupts */
- ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
ext_int_ctrl |= PDM_DMA_INTR_MASK;
- writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
return 0;
}
@@ -175,9 +175,9 @@ static void acp_dmic_dai_shutdown(struct snd_pcm_substream *substream,
u32 ext_int_ctrl;
/* Disable DMIC interrupts */
- ext_int_ctrl = readl(adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ ext_int_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, 0));
ext_int_ctrl |= ~PDM_DMA_INTR_MASK;
- writel(ext_int_ctrl, adata->acp_base + ACP_EXTERNAL_INTR_CNTL);
+ writel(ext_int_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, 0));
}
const struct snd_soc_dai_ops acp_dmic_dai_ops = {
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
index 3c4fd8b80589..327e17736dbd 100644
--- a/sound/soc/amd/acp/acp-platform.c
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -91,25 +91,38 @@ EXPORT_SYMBOL_NS_GPL(acp_machine_select, SND_SOC_ACP_COMMON);
static irqreturn_t i2s_irq_handler(int irq, void *data)
{
struct acp_dev_data *adata = data;
+ struct acp_resource *rsrc = adata->rsrc;
struct acp_stream *stream;
u16 i2s_flag = 0;
- u32 val, i;
+ u32 val, val1, i;
if (!adata)
return IRQ_NONE;
- val = readl(adata->acp_base + ACP_EXTERNAL_INTR_STAT);
+ if (adata->rsrc->no_of_ctrls == 2)
+ val1 = readl(ACP_EXTERNAL_INTR_STAT(adata, (rsrc->irqp_used - 1)));
+
+ val = readl(ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
for (i = 0; i < ACP_MAX_STREAM; i++) {
stream = adata->stream[i];
if (stream && (val & stream->irq_bit)) {
- writel(stream->irq_bit, adata->acp_base + ACP_EXTERNAL_INTR_STAT);
+ writel(stream->irq_bit,
+ ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
snd_pcm_period_elapsed(stream->substream);
i2s_flag = 1;
break;
}
+ if (adata->rsrc->no_of_ctrls == 2) {
+ if (stream && (val1 & stream->irq_bit)) {
+ writel(stream->irq_bit, ACP_EXTERNAL_INTR_STAT(adata,
+ (rsrc->irqp_used - 1)));
+ snd_pcm_period_elapsed(stream->substream);
+ i2s_flag = 1;
+ break;
+ }
+ }
}
-
if (i2s_flag)
return IRQ_HANDLED;
@@ -118,6 +131,7 @@ static irqreturn_t i2s_irq_handler(int irq, void *data)
static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream *stream)
{
+ struct acp_resource *rsrc = adata->rsrc;
u32 pte_reg, pte_size, reg_val;
/* Use ATU base Group5 */
@@ -126,15 +140,17 @@ static void config_pte_for_stream(struct acp_dev_data *adata, struct acp_stream
stream->reg_offset = 0x02000000;
/* Group Enable */
- reg_val = ACP_SRAM_PTE_OFFSET;
+ reg_val = rsrc->sram_pte_offset;
writel(reg_val | BIT(31), adata->acp_base + pte_reg);
writel(PAGE_SIZE_4K_ENABLE, adata->acp_base + pte_size);
+ writel(0x01, adata->acp_base + ACPAXI2AXI_ATU_CTRL);
}
static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
{
struct acp_stream *stream = adata->stream[cpu_id];
struct snd_pcm_substream *substream = stream->substream;
+ struct acp_resource *rsrc = adata->rsrc;
dma_addr_t addr = substream->dma_buffer.addr;
int num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT);
u32 low, high, val;
@@ -146,9 +162,9 @@ static void config_acp_dma(struct acp_dev_data *adata, int cpu_id, int size)
/* Load the low address of page int ACP SRAM through SRBM */
low = lower_32_bits(addr);
high = upper_32_bits(addr);
- writel(low, adata->acp_base + ACP_SCRATCH_REG_0 + val);
+ writel(low, adata->acp_base + rsrc->scratch_reg_offset + val);
high |= BIT(31);
- writel(high, adata->acp_base + ACP_SCRATCH_REG_0 + val + 4);
+ writel(high, adata->acp_base + rsrc->scratch_reg_offset + val + 4);
/* Move to next physically contiguous page */
val += 8;
@@ -187,7 +203,7 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs
}
runtime->private_data = stream;
- writel(1, adata->acp_base + ACP_EXTERNAL_INTR_ENB);
+ writel(1, ACP_EXTERNAL_INTR_ENB(adata));
return ret;
}
diff --git a/sound/soc/amd/acp/acp-rembrandt.c b/sound/soc/amd/acp/acp-rembrandt.c
new file mode 100644
index 000000000000..2b57c0ca4e99
--- /dev/null
+++ b/sound/soc/amd/acp/acp-rembrandt.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
+//
+// This file is provided under a dual BSD/GPLv2 license. When using or
+// redistributing this file, you may do so under either license.
+//
+// Copyright(c) 2022 Advanced Micro Devices, Inc.
+//
+// Authors: Ajit Kumar Pandey <[email protected]>
+// V sujith kumar Reddy <[email protected]>
+/*
+ * Hardware interface for Renoir ACP block
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <linux/dma-mapping.h>
+
+#include "amd.h"
+
+#define DRV_NAME "acp_asoc_rembrandt"
+
+#define ACP6X_PGFSM_CONTROL 0x1024
+#define ACP6X_PGFSM_STATUS 0x1028
+
+#define ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK 0x00010001
+
+#define ACP_PGFSM_CNTL_POWER_ON_MASK 0x01
+#define ACP_PGFSM_CNTL_POWER_OFF_MASK 0x00
+#define ACP_PGFSM_STATUS_MASK 0x03
+#define ACP_POWERED_ON 0x00
+#define ACP_POWER_ON_IN_PROGRESS 0x01
+#define ACP_POWERED_OFF 0x02
+#define ACP_POWER_OFF_IN_PROGRESS 0x03
+
+#define ACP_ERROR_MASK 0x20000000
+#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+
+
+static int rmb_acp_init(void __iomem *base);
+static int rmb_acp_deinit(void __iomem *base);
+
+static struct acp_resource rsrc = {
+ .offset = 0,
+ .no_of_ctrls = 2,
+ .irqp_used = 1,
+ .soc_mclk = true,
+ .irq_reg_offset = 0x1a00,
+ .i2s_pin_cfg_offset = 0x1440,
+ .i2s_mode = 0x0a,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x03802800,
+};
+
+static struct snd_soc_acpi_codecs amp_rt1019 = {
+ .num_codecs = 1,
+ .codecs = {"10EC1019"}
+};
+
+static struct snd_soc_acpi_codecs amp_max = {
+ .num_codecs = 1,
+ .codecs = {"MX98360A"}
+};
+
+static struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[] = {
+ {
+ .id = "10508825",
+ .drv_name = "rmb-nau8825-max",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_max,
+ },
+ {
+ .id = "AMDI0007",
+ .drv_name = "rembrandt-acp",
+ },
+ {
+ .id = "RTL5682",
+ .drv_name = "rmb-rt5682s-rt1019",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &amp_rt1019,
+ },
+ {},
+};
+
+static struct snd_soc_dai_driver acp_rmb_dai[] = {
+{
+ .name = "acp-i2s-sp",
+ .id = I2S_SP_INSTANCE,
+ .playback = {
+ .stream_name = "I2S SP Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S SP Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-i2s-bt",
+ .id = I2S_BT_INSTANCE,
+ .playback = {
+ .stream_name = "I2S BT Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S BT Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-i2s-hs",
+ .id = I2S_HS_INSTANCE,
+ .playback = {
+ .stream_name = "I2S HS Playback",
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ },
+ .capture = {
+ .stream_name = "I2S HS Capture",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 8,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &asoc_acp_cpu_dai_ops,
+ .probe = &asoc_acp_i2s_probe,
+},
+{
+ .name = "acp-pdm-dmic",
+ .id = DMIC_INSTANCE,
+ .capture = {
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 2,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &acp_dmic_dai_ops,
+},
+};
+
+static int acp6x_power_on(void __iomem *base)
+{
+ u32 val;
+ int timeout;
+
+ val = readl(base + ACP6X_PGFSM_STATUS);
+
+ if (val == ACP_POWERED_ON)
+ return 0;
+
+ if ((val & ACP_PGFSM_STATUS_MASK) !=
+ ACP_POWER_ON_IN_PROGRESS)
+ writel(ACP_PGFSM_CNTL_POWER_ON_MASK,
+ base + ACP6X_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP6X_PGFSM_STATUS);
+ if (!val)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_power_off(void __iomem *base)
+{
+ u32 val;
+ int timeout;
+
+ writel(ACP_PGFSM_CNTL_POWER_OFF_MASK,
+ base + ACP6X_PGFSM_CONTROL);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP6X_PGFSM_STATUS);
+ if ((val & ACP_PGFSM_STATUS_MASK) == ACP_POWERED_OFF)
+ return 0;
+ udelay(1);
+ }
+ return -ETIMEDOUT;
+}
+
+static int acp6x_reset(void __iomem *base)
+{
+ u32 val;
+ int timeout;
+
+ writel(1, base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP_SOFT_RESET);
+ if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
+ break;
+ cpu_relax();
+ }
+ writel(0, base + ACP_SOFT_RESET);
+ timeout = 0;
+ while (++timeout < 500) {
+ val = readl(base + ACP_SOFT_RESET);
+ if (!val)
+ return 0;
+ cpu_relax();
+ }
+ return -ETIMEDOUT;
+}
+
+static void acp6x_enable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+ u32 ext_intr_ctrl;
+
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+ ext_intr_ctrl |= ACP_ERROR_MASK;
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
+}
+
+static void acp6x_disable_interrupts(struct acp_dev_data *adata)
+{
+ struct acp_resource *rsrc = adata->rsrc;
+
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
+ ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
+}
+
+static int rmb_acp_init(void __iomem *base)
+{
+ int ret;
+
+ /* power on */
+ ret = acp6x_power_on(base);
+ if (ret) {
+ pr_err("ACP power on failed\n");
+ return ret;
+ }
+ writel(0x01, base + ACP_CONTROL);
+
+ /* Reset */
+ ret = acp6x_reset(base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rmb_acp_deinit(void __iomem *base)
+{
+ int ret = 0;
+
+ /* Reset */
+ ret = acp6x_reset(base);
+ if (ret) {
+ pr_err("ACP reset failed\n");
+ return ret;
+ }
+
+ writel(0x00, base + ACP_CONTROL);
+
+ /* power off */
+ ret = acp6x_power_off(base);
+ if (ret) {
+ pr_err("ACP power off failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rembrandt_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_chip_info *chip;
+ struct acp_dev_data *adata;
+ struct resource *res;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ if (chip->acp_rev != ACP6X_DEV) {
+ dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
+ return -ENODEV;
+ }
+
+ rmb_acp_init(chip->base);
+
+ adata = devm_kzalloc(dev, sizeof(struct acp_dev_data), GFP_KERNEL);
+ if (!adata)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acp_mem");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_MEM FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->acp_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!adata->acp_base)
+ return -ENOMEM;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "acp_dai_irq");
+ if (!res) {
+ dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n");
+ return -ENODEV;
+ }
+
+ adata->i2s_irq = res->start;
+ adata->dev = dev;
+ adata->dai_driver = acp_rmb_dai;
+ adata->num_dai = ARRAY_SIZE(acp_rmb_dai);
+ adata->rsrc = &rsrc;
+
+ adata->machines = snd_soc_acpi_amd_rmb_acp_machines;
+ acp_machine_select(adata);
+
+ dev_set_drvdata(dev, adata);
+ acp6x_enable_interrupts(adata);
+ acp_platform_register(dev);
+
+ return 0;
+}
+
+static int rembrandt_audio_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
+ struct acp_chip_info *chip;
+
+ chip = dev_get_platdata(&pdev->dev);
+ if (!chip || !chip->base) {
+ dev_err(&pdev->dev, "ACP chip data is NULL\n");
+ return -ENODEV;
+ }
+
+ rmb_acp_deinit(chip->base);
+
+ acp6x_disable_interrupts(adata);
+ acp_platform_unregister(dev);
+ return 0;
+}
+
+static struct platform_driver rembrandt_driver = {
+ .probe = rembrandt_audio_probe,
+ .remove = rembrandt_audio_remove,
+ .driver = {
+ .name = "acp_asoc_rembrandt",
+ },
+};
+
+module_platform_driver(rembrandt_driver);
+
+MODULE_DESCRIPTION("AMD ACP Rembrandt Driver");
+MODULE_IMPORT_NS(SND_SOC_ACP_COMMON);
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/amd/acp/acp-renoir.c b/sound/soc/amd/acp/acp-renoir.c
index 8375c00ff4c3..2a89a0d2e601 100644
--- a/sound/soc/amd/acp/acp-renoir.c
+++ b/sound/soc/amd/acp/acp-renoir.c
@@ -39,6 +39,17 @@
#define ACP_ERROR_MASK 0x20000000
#define ACP_EXT_INTR_STAT_CLEAR_MASK 0xFFFFFFFF
+static struct acp_resource rsrc = {
+ .offset = 20,
+ .no_of_ctrls = 1,
+ .irqp_used = 0,
+ .irq_reg_offset = 0x1800,
+ .i2s_pin_cfg_offset = 0x1400,
+ .i2s_mode = 0x04,
+ .scratch_reg_offset = 0x12800,
+ .sram_pte_offset = 0x02052800,
+};
+
static struct snd_soc_acpi_codecs amp_rt1019 = {
.num_codecs = 1,
.codecs = {"10EC1019"}
@@ -186,20 +197,24 @@ static int acp3x_reset(void __iomem *base)
return readl_poll_timeout(base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP_TIMEOUT);
}
-static void acp3x_enable_interrupts(void __iomem *base)
+static void acp3x_enable_interrupts(struct acp_dev_data *adata)
{
+ struct acp_resource *rsrc = adata->rsrc;
u32 ext_intr_ctrl;
- writel(0x01, base + ACP_EXTERNAL_INTR_ENB);
- ext_intr_ctrl = readl(base + ACP_EXTERNAL_INTR_CNTL);
+ writel(0x01, ACP_EXTERNAL_INTR_ENB(adata));
+ ext_intr_ctrl = readl(ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
ext_intr_ctrl |= ACP_ERROR_MASK;
- writel(ext_intr_ctrl, base + ACP_EXTERNAL_INTR_CNTL);
+ writel(ext_intr_ctrl, ACP_EXTERNAL_INTR_CNTL(adata, rsrc->irqp_used));
}
-static void acp3x_disable_interrupts(void __iomem *base)
+static void acp3x_disable_interrupts(struct acp_dev_data *adata)
{
- writel(ACP_EXT_INTR_STAT_CLEAR_MASK, base + ACP_EXTERNAL_INTR_STAT);
- writel(0x00, base + ACP_EXTERNAL_INTR_ENB);
+ struct acp_resource *rsrc = adata->rsrc;
+
+ writel(ACP_EXT_INTR_STAT_CLEAR_MASK,
+ ACP_EXTERNAL_INTR_STAT(adata, rsrc->irqp_used));
+ writel(0x00, ACP_EXTERNAL_INTR_ENB(adata));
}
static int rn_acp_init(void __iomem *base)
@@ -218,8 +233,6 @@ static int rn_acp_init(void __iomem *base)
if (ret)
return ret;
- acp3x_enable_interrupts(base);
-
return 0;
}
@@ -227,8 +240,6 @@ static int rn_acp_deinit(void __iomem *base)
{
int ret = 0;
- acp3x_disable_interrupts(base);
-
/* Reset */
ret = acp3x_reset(base);
if (ret)
@@ -290,11 +301,13 @@ static int renoir_audio_probe(struct platform_device *pdev)
adata->dev = dev;
adata->dai_driver = acp_renoir_dai;
adata->num_dai = ARRAY_SIZE(acp_renoir_dai);
+ adata->rsrc = &rsrc;
adata->machines = snd_soc_acpi_amd_acp_machines;
acp_machine_select(adata);
dev_set_drvdata(dev, adata);
+ acp3x_enable_interrupts(adata);
acp_platform_register(dev);
return 0;
@@ -303,11 +316,14 @@ static int renoir_audio_probe(struct platform_device *pdev)
static int renoir_audio_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct acp_dev_data *adata = dev_get_drvdata(dev);
struct acp_chip_info *chip;
int ret;
chip = dev_get_platdata(&pdev->dev);
+ acp3x_disable_interrupts(adata);
+
ret = rn_acp_deinit(chip->base);
if (ret)
dev_err(&pdev->dev, "ACP de-init Failed (%pe)\n", ERR_PTR(ret));
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index 8fd38bf4d3bd..af9603724a68 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -19,10 +19,12 @@
#include "chip_offset_byte.h"
#define ACP3X_DEV 3
+#define ACP6X_DEV 6
#define I2S_SP_INSTANCE 0x00
#define I2S_BT_INSTANCE 0x01
#define DMIC_INSTANCE 0x02
+#define I2S_HS_INSTANCE 0x03
#define MEM_WINDOW_START 0x4080000
@@ -32,30 +34,37 @@
#define ACP3x_I2STDM_REG_END 0x1242410
#define ACP3x_BT_TDM_REG_START 0x1242800
#define ACP3x_BT_TDM_REG_END 0x1242810
-#define I2S_MODE 0x04
-#define I2S_RX_THRESHOLD 27
-#define I2S_TX_THRESHOLD 28
-#define BT_TX_THRESHOLD 26
-#define BT_RX_THRESHOLD 25
-#define ACP_SRAM_PTE_OFFSET 0x02052800
+#define THRESHOLD(bit, base) ((bit) + (base))
+#define I2S_RX_THRESHOLD(base) THRESHOLD(7, base)
+#define I2S_TX_THRESHOLD(base) THRESHOLD(8, base)
+#define BT_TX_THRESHOLD(base) THRESHOLD(6, base)
+#define BT_RX_THRESHOLD(base) THRESHOLD(5, base)
+#define HS_TX_THRESHOLD(base) THRESHOLD(4, base)
+#define HS_RX_THRESHOLD(base) THRESHOLD(3, base)
#define ACP_SRAM_SP_PB_PTE_OFFSET 0x0
#define ACP_SRAM_SP_CP_PTE_OFFSET 0x100
#define ACP_SRAM_BT_PB_PTE_OFFSET 0x200
#define ACP_SRAM_BT_CP_PTE_OFFSET 0x300
#define ACP_SRAM_PDM_PTE_OFFSET 0x400
+#define ACP_SRAM_HS_PB_PTE_OFFSET 0x500
+#define ACP_SRAM_HS_CP_PTE_OFFSET 0x600
#define PAGE_SIZE_4K_ENABLE 0x2
#define I2S_SP_TX_MEM_WINDOW_START 0x4000000
#define I2S_SP_RX_MEM_WINDOW_START 0x4020000
#define I2S_BT_TX_MEM_WINDOW_START 0x4040000
#define I2S_BT_RX_MEM_WINDOW_START 0x4060000
+#define I2S_HS_TX_MEM_WINDOW_START 0x40A0000
+#define I2S_HS_RX_MEM_WINDOW_START 0x40C0000
#define SP_PB_FIFO_ADDR_OFFSET 0x500
#define SP_CAPT_FIFO_ADDR_OFFSET 0x700
#define BT_PB_FIFO_ADDR_OFFSET 0x900
#define BT_CAPT_FIFO_ADDR_OFFSET 0xB00
+#define HS_PB_FIFO_ADDR_OFFSET 0xD00
+#define HS_CAPT_FIFO_ADDR_OFFSET 0xF00
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
#define PLAYBACK_MAX_PERIOD_SIZE 8192
@@ -73,7 +82,7 @@
#define ACP3x_ITER_IRER_SAMP_LEN_MASK 0x38
-#define ACP_MAX_STREAM 6
+#define ACP_MAX_STREAM 8
struct acp_chip_info {
char *name; /* Platform name */
@@ -92,6 +101,18 @@ struct acp_stream {
u32 fifo_offset;
};
+struct acp_resource {
+ int offset;
+ int no_of_ctrls;
+ int irqp_used;
+ bool soc_mclk;
+ u32 irq_reg_offset;
+ u32 i2s_pin_cfg_offset;
+ int i2s_mode;
+ u64 scratch_reg_offset;
+ u64 sram_pte_offset;
+};
+
struct acp_dev_data {
char *name;
struct device *dev;
@@ -106,6 +127,22 @@ struct acp_dev_data {
struct snd_soc_acpi_mach *machines;
struct platform_device *mach_dev;
+
+ u32 bclk_div;
+ u32 lrclk_div;
+
+ struct acp_resource *rsrc;
+};
+
+union acp_i2stdm_mstrclkgen {
+ struct {
+ u32 i2stdm_master_mode : 1;
+ u32 i2stdm_format_mode : 1;
+ u32 i2stdm_lrclk_div_val : 9;
+ u32 i2stdm_bclk_div_val : 11;
+ u32:10;
+ } bitfields, bits;
+ u32 u32_all;
};
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
@@ -134,6 +171,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
high = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_TX_LINEARPOSITIONCNTR_LOW);
break;
+ case I2S_HS_INSTANCE:
+ high = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_HS_TX_LINEARPOSITIONCNTR_LOW);
+ break;
default:
dev_err(adata->dev, "Invalid dai id %x\n", dai_id);
return -EINVAL;
@@ -148,6 +189,10 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
high = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_I2S_RX_LINEARPOSITIONCNTR_LOW);
break;
+ case I2S_HS_INSTANCE:
+ high = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_HIGH);
+ low = readl(adata->acp_base + ACP_HS_RX_LINEARPOSITIONCNTR_LOW);
+ break;
case DMIC_INSTANCE:
high = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_HIGH);
low = readl(adata->acp_base + ACP_WOV_RX_LINEARPOSITIONCNTR_LOW);
@@ -163,4 +208,31 @@ static inline u64 acp_get_byte_count(struct acp_dev_data *adata, int dai_id, int
return byte_count;
}
+static inline void acp_set_i2s_clk(struct acp_dev_data *adata, int dai_id)
+{
+ union acp_i2stdm_mstrclkgen mclkgen;
+ u32 master_reg;
+
+ switch (dai_id) {
+ case I2S_SP_INSTANCE:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ case I2S_BT_INSTANCE:
+ master_reg = ACP_I2STDM1_MSTRCLKGEN;
+ break;
+ case I2S_HS_INSTANCE:
+ master_reg = ACP_I2STDM2_MSTRCLKGEN;
+ break;
+ default:
+ master_reg = ACP_I2STDM0_MSTRCLKGEN;
+ break;
+ }
+
+ mclkgen.bits.i2stdm_master_mode = 0x1;
+ mclkgen.bits.i2stdm_format_mode = 0x00;
+
+ mclkgen.bits.i2stdm_bclk_div_val = adata->bclk_div;
+ mclkgen.bits.i2stdm_lrclk_div_val = adata->lrclk_div;
+ writel(mclkgen.u32_all, adata->acp_base + master_reg);
+}
#endif
diff --git a/sound/soc/amd/acp/chip_offset_byte.h b/sound/soc/amd/acp/chip_offset_byte.h
index 88f6fa597cd6..ce3948e0679c 100644
--- a/sound/soc/amd/acp/chip_offset_byte.h
+++ b/sound/soc/amd/acp/chip_offset_byte.h
@@ -20,11 +20,13 @@
#define ACP_SOFT_RESET 0x1000
#define ACP_CONTROL 0x1004
-#define ACP_EXTERNAL_INTR_ENB 0x1800
-#define ACP_EXTERNAL_INTR_CNTL 0x1804
-#define ACP_EXTERNAL_INTR_STAT 0x1808
-#define ACP_I2S_PIN_CONFIG 0x1400
-#define ACP_SCRATCH_REG_0 0x12800
+#define ACP_EXTERNAL_INTR_REG_ADDR(adata, offset, ctrl) \
+ (adata->acp_base + adata->rsrc->irq_reg_offset + offset + (ctrl * 0x04))
+
+#define ACP_EXTERNAL_INTR_ENB(adata) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x0, 0x0)
+#define ACP_EXTERNAL_INTR_CNTL(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, 0x4, ctrl)
+#define ACP_EXTERNAL_INTR_STAT(adata, ctrl) ACP_EXTERNAL_INTR_REG_ADDR(adata, \
+ (0x4 + (adata->rsrc->no_of_ctrls * 0x04)), ctrl)
/* Registers from ACP_AUDIO_BUFFERS block */
@@ -64,6 +66,24 @@
#define ACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x2084
#define ACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x2088
#define ACP_BT_TX_INTR_WATERMARK_SIZE 0x208C
+#define ACP_HS_RX_RINGBUFADDR 0x3A90
+#define ACP_HS_RX_RINGBUFSIZE 0x3A94
+#define ACP_HS_RX_LINKPOSITIONCNTR 0x3A98
+#define ACP_HS_RX_FIFOADDR 0x3A9C
+#define ACP_HS_RX_FIFOSIZE 0x3AA0
+#define ACP_HS_RX_DMA_SIZE 0x3AA4
+#define ACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x3AA8
+#define ACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x3AAC
+#define ACP_HS_RX_INTR_WATERMARK_SIZE 0x3AB0
+#define ACP_HS_TX_RINGBUFADDR 0x3AB4
+#define ACP_HS_TX_RINGBUFSIZE 0x3AB8
+#define ACP_HS_TX_LINKPOSITIONCNTR 0x3ABC
+#define ACP_HS_TX_FIFOADDR 0x3AC0
+#define ACP_HS_TX_FIFOSIZE 0x3AC4
+#define ACP_HS_TX_DMA_SIZE 0x3AC8
+#define ACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x3ACC
+#define ACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x3AD0
+#define ACP_HS_TX_INTR_WATERMARK_SIZE 0x3AD4
#define ACP_I2STDM_IER 0x2400
#define ACP_I2STDM_IRER 0x2404
@@ -79,6 +99,13 @@
#define ACP_BTTDM_ITER 0x280C
#define ACP_BTTDM_TXFRMT 0x2810
+/* Registers from ACP_HS_TDM block */
+#define ACP_HSTDM_IER 0x2814
+#define ACP_HSTDM_IRER 0x2818
+#define ACP_HSTDM_RXFRMT 0x281C
+#define ACP_HSTDM_ITER 0x2820
+#define ACP_HSTDM_TXFRMT 0x2824
+
/* Registers from ACP_WOV_PDM block */
#define ACP_WOV_PDM_ENABLE 0x2C04
@@ -99,4 +126,7 @@
#define ACP_PDM_VAD_DYNAMIC_CLK_GATING_EN 0x2C64
#define ACP_WOV_ERROR_STATUS_REGISTER 0x2C68
+#define ACP_I2STDM0_MSTRCLKGEN 0x2414
+#define ACP_I2STDM1_MSTRCLKGEN 0x2418
+#define ACP_I2STDM2_MSTRCLKGEN 0x241C
#endif
diff --git a/sound/soc/atmel/mchp-pdmc.c b/sound/soc/atmel/mchp-pdmc.c
index aba7c5cde62c..44aefbd5b62c 100644
--- a/sound/soc/atmel/mchp-pdmc.c
+++ b/sound/soc/atmel/mchp-pdmc.c
@@ -985,7 +985,7 @@ static int mchp_pdmc_probe(struct platform_device *pdev)
return -ENOMEM;
dd->dev = &pdev->dev;
- ret = mchp_pdmc_dt_init(dd);
+ ret = mchp_pdmc_dt_init(dd);
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ee7e028e8402..d16b4efb88a7 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -219,6 +219,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2562
imply SND_SOC_TAS2764
imply SND_SOC_TAS2770
+ imply SND_SOC_TAS2780
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
@@ -1535,6 +1536,13 @@ config SND_SOC_TAS2770
tristate "Texas Instruments TAS2770 speaker amplifier"
depends on I2C
+config SND_SOC_TAS2780
+ tristate "Texas Instruments TAS2780 Mono Audio amplifier"
+ depends on I2C
+ help
+ Enable support for Texas Instruments TAS2780 high-efficiency
+ digital input mono Class-D audio power amplifiers.
+
config SND_SOC_TAS5086
tristate "Texas Instruments TAS5086 speaker amplifier"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 60354579fe5c..92fd441d426a 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -348,6 +348,7 @@ snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
snd-soc-tas2562-objs := tas2562.o
snd-soc-tas2764-objs := tas2764.o
+snd-soc-tas2780-objs := tas2780.o
# Mux
snd-soc-simple-mux-objs := simple-mux.o
@@ -592,6 +593,7 @@ obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
+obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index e32871b3f68a..7434aeeda292 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -1760,8 +1760,8 @@ static bool arizona_aif_cfg_changed(struct snd_soc_component *component,
if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
return true;
- val = snd_soc_component_read(component, base + ARIZONA_AIF_TX_BCLK_RATE);
- if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK))
+ val = snd_soc_component_read(component, base + ARIZONA_AIF_RX_BCLK_RATE);
+ if (lrclk != (val & ARIZONA_AIF1RX_BCPF_MASK))
return true;
val = snd_soc_component_read(component, base + ARIZONA_AIF_FRAME_CTRL_1);
diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c
index 59da34b480a8..fe576d64e089 100644
--- a/sound/soc/codecs/cs47l92.c
+++ b/sound/soc/codecs/cs47l92.c
@@ -119,7 +119,13 @@ static int cs47l92_put_demux(struct snd_kcontrol *kcontrol,
end:
snd_soc_dapm_mutex_unlock(dapm);
- return snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+ if (ret < 0) {
+ dev_err(madera->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
+
+ return change;
}
static SOC_ENUM_SINGLE_DECL(cs47l92_outdemux_enum,
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index 6453e93678d2..2600be250a3c 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -29,6 +29,8 @@
#define NAU_FVCO_MAX 100000000
#define NAU_FVCO_MIN 90000000
+#define NAU8821_BUTTON SND_JACK_BTN_0
+
/* the maximum frequency of CLK_ADC and CLK_DAC */
#define CLK_DA_AD_MAX 6144000
@@ -911,6 +913,20 @@ static void nau8821_eject_jack(struct nau8821 *nau8821)
/* Recover to normal channel input */
regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
NAU8821_ADC_R_SRC_EN, 0);
+ if (nau8821->key_enable) {
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN);
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS);
+ }
+
}
static void nau8821_jdet_work(struct work_struct *work)
@@ -940,6 +956,15 @@ static void nau8821_jdet_work(struct work_struct *work)
*/
regmap_update_bits(regmap, NAU8821_R2B_ADC_RATE,
NAU8821_ADC_R_SRC_EN, NAU8821_ADC_R_SRC_EN);
+ if (nau8821->key_enable) {
+ regmap_update_bits(regmap, NAU8821_R0F_INTERRUPT_MASK,
+ NAU8821_IRQ_KEY_RELEASE_EN |
+ NAU8821_IRQ_KEY_PRESS_EN, 0);
+ regmap_update_bits(regmap,
+ NAU8821_R12_INTERRUPT_DIS_CTRL,
+ NAU8821_IRQ_KEY_RELEASE_DIS |
+ NAU8821_IRQ_KEY_PRESS_DIS, 0);
+ }
} else {
dev_dbg(nau8821->dev, "Headphone connected\n");
event |= SND_JACK_HEADPHONE;
@@ -999,6 +1024,13 @@ static irqreturn_t nau8821_interrupt(int irq, void *data)
nau8821_eject_jack(nau8821);
event_mask |= SND_JACK_HEADSET;
clear_irq = NAU8821_JACK_EJECT_IRQ_MASK;
+ } else if (active_irq & NAU8821_KEY_SHORT_PRESS_IRQ) {
+ event |= NAU8821_BUTTON;
+ event_mask |= NAU8821_BUTTON;
+ clear_irq = NAU8821_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8821_KEY_RELEASE_IRQ) {
+ event_mask = NAU8821_BUTTON;
+ clear_irq = NAU8821_KEY_RELEASE_IRQ;
} else if ((active_irq & NAU8821_JACK_INSERT_IRQ_MASK) ==
NAU8821_JACK_INSERT_DETECTED) {
regmap_update_bits(regmap, NAU8821_R71_ANALOG_ADC_1,
@@ -1489,6 +1521,7 @@ static void nau8821_print_device_properties(struct nau8821 *nau8821)
nau8821->jack_eject_debounce);
dev_dbg(dev, "dmic-clk-threshold: %d\n",
nau8821->dmic_clk_threshold);
+ dev_dbg(dev, "key_enable: %d\n", nau8821->key_enable);
}
static int nau8821_read_device_properties(struct device *dev,
@@ -1502,6 +1535,8 @@ static int nau8821_read_device_properties(struct device *dev,
"nuvoton,jkdet-pull-enable");
nau8821->jkdet_pull_up = device_property_read_bool(dev,
"nuvoton,jkdet-pull-up");
+ nau8821->key_enable = device_property_read_bool(dev,
+ "nuvoton,key-enable");
ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
&nau8821->jkdet_polarity);
if (ret)
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
index a92edfeb9d3a..c44251f54d48 100644
--- a/sound/soc/codecs/nau8821.h
+++ b/sound/soc/codecs/nau8821.h
@@ -525,6 +525,7 @@ struct nau8821 {
int jack_eject_debounce;
int fs;
int dmic_clk_threshold;
+ int key_enable;
};
int nau8821_enable_jack_detect(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 907ec88c759a..54ef7b0fa878 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -1440,7 +1440,7 @@ static struct snd_soc_dai_driver nau8825_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 1,
- .channels_max = 1,
+ .channels_max = 2, /* Only 1 channel of data */
.rates = NAU8825_RATES,
.formats = NAU8825_FORMATS,
},
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 6b208f9eb503..f2c50b11e4d0 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -993,6 +993,7 @@ static void rt274_remove(struct snd_soc_component *component)
struct rt274_priv *rt274 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt274->jack_detect_work);
+ rt274->component = NULL;
}
#ifdef CONFIG_PM
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index b2b0b2b1e4d0..c4f7c4c2d793 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -960,6 +960,7 @@ static void rt286_remove(struct snd_soc_component *component)
struct rt286_priv *rt286 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt286->jack_detect_work);
+ rt286->component = NULL;
}
#ifdef CONFIG_PM
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 266a2cc55b8d..b0b53d4f07df 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -329,33 +329,31 @@ static void rt298_jack_detect_work(struct work_struct *work)
static int rt298_mic_detect(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
- struct snd_soc_dapm_context *dapm;
- bool hp = false;
- bool mic = false;
- int status = 0;
- /* If jack in NULL, disable HS jack */
- if (!jack) {
+ rt298->jack = jack;
+
+ if (jack) {
+ /* Enable IRQ */
+ if (rt298->jack->status & SND_JACK_HEADPHONE)
+ snd_soc_dapm_force_enable_pin(dapm, "LDO1");
+ if (rt298->jack->status & SND_JACK_MICROPHONE) {
+ snd_soc_dapm_force_enable_pin(dapm, "HV");
+ snd_soc_dapm_force_enable_pin(dapm, "VREF");
+ }
+ regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt298->jack, rt298->jack->status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ } else {
+ /* Disable IRQ */
regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x0);
- dapm = snd_soc_component_get_dapm(component);
+ snd_soc_dapm_disable_pin(dapm, "HV");
+ snd_soc_dapm_disable_pin(dapm, "VREF");
snd_soc_dapm_disable_pin(dapm, "LDO1");
- snd_soc_dapm_sync(dapm);
- return 0;
}
-
- rt298->jack = jack;
- regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
-
- rt298_jack_detect(rt298, &hp, &mic);
- if (hp)
- status |= SND_JACK_HEADPHONE;
-
- if (mic)
- status |= SND_JACK_MICROPHONE;
-
- snd_soc_jack_report(rt298->jack, status,
- SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+ snd_soc_dapm_sync(dapm);
return 0;
}
@@ -1024,6 +1022,7 @@ static void rt298_remove(struct snd_soc_component *component)
struct rt298_priv *rt298 = snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt298->jack_detect_work);
+ rt298->component = NULL;
}
#ifdef CONFIG_PM
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 5092856a262d..38ab8d4291c2 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1984,7 +1984,12 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000);
snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000);
snd_soc_component_write(component, RT5640_PWR_MIXER, 0x0000);
- snd_soc_component_write(component, RT5640_PWR_ANLG1, 0x0000);
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER)
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x0018);
+ else
+ snd_soc_component_write(component, RT5640_PWR_ANLG1,
+ 0x0000);
snd_soc_component_write(component, RT5640_PWR_ANLG2, 0x0000);
break;
@@ -2393,9 +2398,15 @@ static void rt5640_jack_work(struct work_struct *work)
static irqreturn_t rt5640_irq(int irq, void *data)
{
struct rt5640_priv *rt5640 = data;
+ int delay = 0;
+
+ if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
+ cancel_delayed_work_sync(&rt5640->jack_work);
+ delay = 100;
+ }
if (rt5640->jack)
- queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ queue_delayed_work(system_long_wq, &rt5640->jack_work, delay);
return IRQ_HANDLED;
}
@@ -2588,6 +2599,12 @@ static void rt5640_enable_hda_jack_detect(
snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_VREF2, RT5640_PWR_VREF2);
+ usleep_range(10000, 15000);
+ snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
+ RT5640_PWR_FV2, RT5640_PWR_FV2);
+
rt5640->jack = jack;
ret = request_irq(rt5640->irq, rt5640_irq,
@@ -2707,16 +2724,13 @@ static int rt5640_probe(struct snd_soc_component *component)
if (device_property_read_u32(component->dev,
"realtek,jack-detect-source", &val) == 0) {
- if (val <= RT5640_JD_SRC_GPIO4) {
+ if (val <= RT5640_JD_SRC_GPIO4)
rt5640->jd_src = val << RT5640_JD_SFT;
- } else if (val == RT5640_JD_SRC_HDA_HEADER) {
+ else if (val == RT5640_JD_SRC_HDA_HEADER)
rt5640->jd_src = RT5640_JD_SRC_HDA_HEADER;
- snd_soc_component_update_bits(component, RT5640_DUMMY1,
- 0x0300, 0x0);
- } else {
+ else
dev_warn(component->dev, "Warning: Invalid jack-detect-source value: %d, leaving jack-detect disabled\n",
val);
- }
}
if (!device_property_read_bool(component->dev, "realtek,jack-detect-not-inverted"))
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 42f0c1e449ba..846d9d3ecc9d 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -42,10 +42,12 @@ static void tas2764_reset(struct tas2764_priv *tas2764)
gpiod_set_value_cansleep(tas2764->reset_gpio, 0);
msleep(20);
gpiod_set_value_cansleep(tas2764->reset_gpio, 1);
+ usleep_range(1000, 2000);
}
snd_soc_component_write(tas2764->component, TAS2764_SW_RST,
TAS2764_RST);
+ usleep_range(1000, 2000);
}
static int tas2764_set_bias_level(struct snd_soc_component *component,
@@ -107,8 +109,10 @@ static int tas2764_codec_resume(struct snd_soc_component *component)
struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
int ret;
- if (tas2764->sdz_gpio)
+ if (tas2764->sdz_gpio) {
gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ }
ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
TAS2764_PWR_CTRL_MASK,
@@ -131,7 +135,8 @@ static const char * const tas2764_ASI1_src[] = {
};
static SOC_ENUM_SINGLE_DECL(
- tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, 4, tas2764_ASI1_src);
+ tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, TAS2764_TDM_CFG2_SCFG_SHIFT,
+ tas2764_ASI1_src);
static const struct snd_kcontrol_new tas2764_asi1_mux =
SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum);
@@ -329,20 +334,22 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
- u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
- int iface;
+ u8 tdm_rx_start_slot = 0, asi_cfg_0 = 0, asi_cfg_1 = 0;
int ret;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
+ fallthrough;
case SND_SOC_DAIFMT_NB_NF:
asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING;
break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
+ fallthrough;
case SND_SOC_DAIFMT_IB_NF:
asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING;
break;
- default:
- dev_err(tas2764->dev, "ASI format Inverse is not found\n");
- return -EINVAL;
}
ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
@@ -353,13 +360,13 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
+ asi_cfg_0 ^= TAS2764_TDM_CFG0_FRAME_START;
+ fallthrough;
case SND_SOC_DAIFMT_DSP_A:
- iface = TAS2764_TDM_CFG2_SCFG_I2S;
tdm_rx_start_slot = 1;
break;
case SND_SOC_DAIFMT_DSP_B:
case SND_SOC_DAIFMT_LEFT_J:
- iface = TAS2764_TDM_CFG2_SCFG_LEFT_J;
tdm_rx_start_slot = 0;
break;
default:
@@ -368,14 +375,15 @@ static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
- TAS2764_TDM_CFG1_MASK,
- (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT));
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0,
+ TAS2764_TDM_CFG0_FRAME_START,
+ asi_cfg_0);
if (ret < 0)
return ret;
- ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2,
- TAS2764_TDM_CFG2_SCFG_MASK, iface);
+ ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1,
+ TAS2764_TDM_CFG1_MASK,
+ (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT));
if (ret < 0)
return ret;
@@ -501,8 +509,10 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
tas2764->component = component;
- if (tas2764->sdz_gpio)
+ if (tas2764->sdz_gpio) {
gpiod_set_value_cansleep(tas2764->sdz_gpio, 1);
+ usleep_range(1000, 2000);
+ }
tas2764_reset(tas2764);
@@ -526,12 +536,12 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
}
static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0);
-static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10000, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10050, 50, 1);
static const struct snd_kcontrol_new tas2764_snd_controls[] = {
SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0,
TAS2764_DVC_MAX, 1, tas2764_playback_volume),
- SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 0, 0x14, 0,
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 1, 0x14, 0,
tas2764_digital_tlv),
};
@@ -555,7 +565,7 @@ static const struct reg_default tas2764_reg_defaults[] = {
{ TAS2764_SW_RST, 0x00 },
{ TAS2764_PWR_CTRL, 0x1a },
{ TAS2764_DVC, 0x00 },
- { TAS2764_CHNL_0, 0x00 },
+ { TAS2764_CHNL_0, 0x28 },
{ TAS2764_TDM_CFG0, 0x09 },
{ TAS2764_TDM_CFG1, 0x02 },
{ TAS2764_TDM_CFG2, 0x0a },
diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h
index 67d6fd903c42..f015f22a083b 100644
--- a/sound/soc/codecs/tas2764.h
+++ b/sound/soc/codecs/tas2764.h
@@ -47,6 +47,7 @@
#define TAS2764_TDM_CFG0_MASK GENMASK(3, 1)
#define TAS2764_TDM_CFG0_44_1_48KHZ BIT(3)
#define TAS2764_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1))
+#define TAS2764_TDM_CFG0_FRAME_START BIT(0)
/* TDM Configuration Reg1 */
#define TAS2764_TDM_CFG1 TAS2764_REG(0X0, 0x09)
@@ -66,10 +67,7 @@
#define TAS2764_TDM_CFG2_RXS_16BITS 0x0
#define TAS2764_TDM_CFG2_RXS_24BITS BIT(0)
#define TAS2764_TDM_CFG2_RXS_32BITS BIT(1)
-#define TAS2764_TDM_CFG2_SCFG_MASK GENMASK(5, 4)
-#define TAS2764_TDM_CFG2_SCFG_I2S 0x0
-#define TAS2764_TDM_CFG2_SCFG_LEFT_J BIT(4)
-#define TAS2764_TDM_CFG2_SCFG_RIGHT_J BIT(5)
+#define TAS2764_TDM_CFG2_SCFG_SHIFT 4
/* TDM Configuration Reg3 */
#define TAS2764_TDM_CFG3 TAS2764_REG(0X0, 0x0c)
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
new file mode 100644
index 000000000000..a6db6f0e5431
--- /dev/null
+++ b/sound/soc/codecs/tas2780.c
@@ -0,0 +1,663 @@
+// SPDX-License-Identifier: GPL-2.0
+// Driver for the Texas Instruments TAS2780 Mono
+// Audio amplifier
+// Copyright (C) 2022 Texas Instruments Inc.
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#include "tas2780.h"
+
+struct tas2780_priv {
+ struct snd_soc_component *component;
+ struct gpio_desc *reset_gpio;
+ struct regmap *regmap;
+ struct device *dev;
+ int v_sense_slot;
+ int i_sense_slot;
+};
+
+static void tas2780_reset(struct tas2780_priv *tas2780)
+{
+ int ret = 0;
+
+ if (tas2780->reset_gpio) {
+ gpiod_set_value_cansleep(tas2780->reset_gpio, 0);
+ usleep_range(2000, 2050);
+ gpiod_set_value_cansleep(tas2780->reset_gpio, 1);
+ usleep_range(2000, 2050);
+ }
+
+ snd_soc_component_write(tas2780->component, TAS2780_SW_RST,
+ TAS2780_RST);
+ if (ret)
+ dev_err(tas2780->dev, "%s:errCode:0x%x Reset error!\n",
+ __func__, ret);
+}
+
+#ifdef CONFIG_PM
+static int tas2780_codec_suspend(struct snd_soc_component *component)
+{
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL,
+ TAS2780_PWR_CTRL_MASK, TAS2780_PWR_CTRL_SHUTDOWN);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%0x:power down error\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+ regcache_cache_only(tas2780->regmap, true);
+ regcache_mark_dirty(tas2780->regmap);
+err:
+ return ret;
+}
+
+static int tas2780_codec_resume(struct snd_soc_component *component)
+{
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL,
+ TAS2780_PWR_CTRL_MASK, TAS2780_PWR_CTRL_ACTIVE);
+
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%0x:power down error\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+ regcache_cache_only(tas2780->regmap, false);
+ ret = regcache_sync(tas2780->regmap);
+err:
+ return ret;
+}
+#endif
+
+static const char * const tas2780_ASI1_src[] = {
+ "I2C offset", "Left", "Right", "LeftRightDiv2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ tas2780_ASI1_src_enum, TAS2780_TDM_CFG2, 4, tas2780_ASI1_src);
+
+static const struct snd_kcontrol_new tas2780_asi1_mux =
+ SOC_DAPM_ENUM("ASI1 Source", tas2780_ASI1_src_enum);
+
+static const struct snd_kcontrol_new isense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2780_PWR_CTRL,
+ TAS2780_ISENSE_POWER_EN, 1, 1);
+static const struct snd_kcontrol_new vsense_switch =
+ SOC_DAPM_SINGLE("Switch", TAS2780_PWR_CTRL,
+ TAS2780_VSENSE_POWER_EN, 1, 1);
+
+static const struct snd_soc_dapm_widget tas2780_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2780_asi1_mux),
+ SND_SOC_DAPM_SWITCH("ISENSE", TAS2780_PWR_CTRL,
+ TAS2780_ISENSE_POWER_EN, 1, &isense_switch),
+ SND_SOC_DAPM_SWITCH("VSENSE", TAS2780_PWR_CTRL,
+ TAS2780_VSENSE_POWER_EN, 1, &vsense_switch),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_SIGGEN("VMON"),
+ SND_SOC_DAPM_SIGGEN("IMON")
+};
+
+static const struct snd_soc_dapm_route tas2780_audio_map[] = {
+ {"ASI1 Sel", "I2C offset", "ASI1"},
+ {"ASI1 Sel", "Left", "ASI1"},
+ {"ASI1 Sel", "Right", "ASI1"},
+ {"ASI1 Sel", "LeftRightDiv2", "ASI1"},
+ {"OUT", NULL, "ASI1 Sel"},
+ {"ISENSE", "Switch", "IMON"},
+ {"VSENSE", "Switch", "VMON"},
+};
+
+static int tas2780_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ ret = snd_soc_component_update_bits(component, TAS2780_PWR_CTRL,
+ TAS2780_PWR_CTRL_MASK,
+ mute ? TAS2780_PWR_CTRL_MUTE : 0);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s: Failed to set powercontrol\n",
+ __func__);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_set_bitwidth(struct tas2780_priv *tas2780, int bitwidth)
+{
+ struct snd_soc_component *component = tas2780->component;
+ int sense_en;
+ int val;
+ int ret;
+ int slot_size;
+
+ switch (bitwidth) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXW_MASK,
+ TAS2780_TDM_CFG2_RXW_16BITS);
+ slot_size = TAS2780_TDM_CFG2_RXS_16BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXW_MASK,
+ TAS2780_TDM_CFG2_RXW_24BITS);
+ slot_size = TAS2780_TDM_CFG2_RXS_24BITS;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXW_MASK,
+ TAS2780_TDM_CFG2_RXW_32BITS);
+ slot_size = TAS2780_TDM_CFG2_RXS_32BITS;
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x set bitwidth error\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXS_MASK, slot_size);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x set RX slot size error\n",
+ __func__, ret);
+ goto err;
+ }
+
+ val = snd_soc_component_read(tas2780->component, TAS2780_PWR_CTRL);
+ if (val < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x read PWR_CTRL error\n",
+ __func__, val);
+ ret = val;
+ goto err;
+ }
+
+ if (val & (1 << TAS2780_VSENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2780_TDM_CFG5_VSNS_ENABLE;
+
+ ret = snd_soc_component_update_bits(tas2780->component,
+ TAS2780_TDM_CFG5, TAS2780_TDM_CFG5_VSNS_ENABLE, sense_en);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x enable vSNS error\n",
+ __func__, ret);
+ goto err;
+ }
+
+ if (val & (1 << TAS2780_ISENSE_POWER_EN))
+ sense_en = 0;
+ else
+ sense_en = TAS2780_TDM_CFG6_ISNS_ENABLE;
+
+ ret = snd_soc_component_update_bits(tas2780->component,
+ TAS2780_TDM_CFG6, TAS2780_TDM_CFG6_ISNS_ENABLE, sense_en);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x enable iSNS error\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_set_samplerate(
+ struct tas2780_priv *tas2780, int samplerate)
+{
+ struct snd_soc_component *component = tas2780->component;
+ int ramp_rate_val;
+ int ret;
+
+ switch (samplerate) {
+ case 48000:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_48KHZ |
+ TAS2780_TDM_CFG0_44_1_48KHZ;
+ break;
+ case 44100:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_44_1KHZ |
+ TAS2780_TDM_CFG0_44_1_48KHZ;
+ break;
+ case 96000:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_48KHZ |
+ TAS2780_TDM_CFG0_88_2_96KHZ;
+ break;
+ case 88200:
+ ramp_rate_val = TAS2780_TDM_CFG0_SMP_44_1KHZ |
+ TAS2780_TDM_CFG0_88_2_96KHZ;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG0,
+ TAS2780_TDM_CFG0_SMP_MASK | TAS2780_TDM_CFG0_MASK,
+ ramp_rate_val);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set ramp_rate_val\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = tas2780_set_bitwidth(tas2780, params_format(params));
+ if (ret < 0)
+ return ret;
+
+ return tas2780_set_samplerate(tas2780, params_rate(params));
+}
+
+static int tas2780_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0;
+ int iface;
+ int ret = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ asi_cfg_1 = TAS2780_TDM_CFG1_RX_RISING;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asi_cfg_1 = TAS2780_TDM_CFG1_RX_FALLING;
+ break;
+ default:
+ dev_err(tas2780->dev, "ASI format Inverse is not found\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1,
+ TAS2780_TDM_CFG1_RX_MASK, asi_cfg_1);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set asi_cfg_1\n",
+ __func__, ret);
+ goto err;
+ }
+
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S)
+ || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_DSP_A)){
+ iface = TAS2780_TDM_CFG2_SCFG_I2S;
+ tdm_rx_start_slot = 1;
+ } else {
+ if (((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_DSP_B)
+ || ((fmt & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_LEFT_J)) {
+ iface = TAS2780_TDM_CFG2_SCFG_LEFT_J;
+ tdm_rx_start_slot = 0;
+ } else {
+ dev_err(tas2780->dev,
+ "%s:DAI Format is not found, fmt=0x%x\n",
+ __func__, fmt);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG1,
+ TAS2780_TDM_CFG1_MASK,
+ (tdm_rx_start_slot << TAS2780_TDM_CFG1_51_SHIFT));
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set tdm_rx_start_slot\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_SCFG_MASK, iface);
+ if (ret < 0) {
+ dev_err(tas2780->dev, "%s:errCode:0x%x Failed to set iface\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static int tas2780_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int left_slot, right_slot;
+ int slots_cfg;
+ int slot_size;
+ int ret = 0;
+
+ if (tx_mask == 0 || rx_mask != 0)
+ return -EINVAL;
+
+ if (slots == 1) {
+ if (tx_mask != 1)
+ return -EINVAL;
+ left_slot = 0;
+ right_slot = 0;
+ } else {
+ left_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << left_slot);
+ if (tx_mask == 0) {
+ right_slot = left_slot;
+ } else {
+ right_slot = __ffs(tx_mask);
+ tx_mask &= ~(1 << right_slot);
+ }
+ }
+
+ if (tx_mask != 0 || left_slot >= slots || right_slot >= slots)
+ return -EINVAL;
+
+ slots_cfg = (right_slot << TAS2780_TDM_CFG3_RXS_SHIFT) | left_slot;
+ ret = snd_soc_component_write(component, TAS2780_TDM_CFG3, slots_cfg);
+ if (ret) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set slots_cfg\n",
+ __func__, ret);
+ goto err;
+ }
+
+ switch (slot_width) {
+ case 16:
+ slot_size = TAS2780_TDM_CFG2_RXS_16BITS;
+ break;
+ case 24:
+ slot_size = TAS2780_TDM_CFG2_RXS_24BITS;
+ break;
+ case 32:
+ slot_size = TAS2780_TDM_CFG2_RXS_32BITS;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG2,
+ TAS2780_TDM_CFG2_RXS_MASK, slot_size);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set slot_size\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG5,
+ TAS2780_TDM_CFG5_50_MASK, tas2780->v_sense_slot);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set v_sense_slot\n",
+ __func__, ret);
+ goto err;
+ }
+
+ ret = snd_soc_component_update_bits(component, TAS2780_TDM_CFG6,
+ TAS2780_TDM_CFG6_50_MASK, tas2780->i_sense_slot);
+ if (ret < 0) {
+ dev_err(tas2780->dev,
+ "%s:errCode:0x%x Failed to set i_sense_slot\n",
+ __func__, ret);
+ goto err;
+ }
+ ret = 0;
+err:
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tas2780_dai_ops = {
+ .mute_stream = tas2780_mute,
+ .hw_params = tas2780_hw_params,
+ .set_fmt = tas2780_set_fmt,
+ .set_tdm_slot = tas2780_set_dai_tdm_slot,
+ .no_capture_mute = 1,
+};
+
+#define TAS2780_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TAS2780_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200)
+
+static struct snd_soc_dai_driver tas2780_dai_driver[] = {
+ {
+ .name = "tas2780 ASI1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASI1 Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = TAS2780_RATES,
+ .formats = TAS2780_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAS2780_RATES,
+ .formats = TAS2780_FORMATS,
+ },
+ .ops = &tas2780_dai_ops,
+ .symmetric_rate = 1,
+ },
+};
+
+static int tas2780_codec_probe(struct snd_soc_component *component)
+{
+ struct tas2780_priv *tas2780 =
+ snd_soc_component_get_drvdata(component);
+ int ret = 0;
+
+ tas2780->component = component;
+
+ tas2780_reset(tas2780);
+ ret = snd_soc_component_update_bits(component,
+ TAS2780_IC_CFG, TAS2780_IC_CFG_MASK,
+ TAS2780_IC_CFG_ENABLE);
+ if (ret < 0)
+ dev_err(tas2780->dev, "%s:errCode:0x%0x\n",
+ __func__, ret);
+
+ return ret;
+}
+
+static DECLARE_TLV_DB_SCALE(tas2780_digital_tlv, 1100, 50, 0);
+static DECLARE_TLV_DB_SCALE(tas2780_playback_volume, -10000, 50, 0);
+
+static const struct snd_kcontrol_new tas2780_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", TAS2780_DVC, 0,
+ TAS2780_DVC_MAX, 1, tas2780_playback_volume),
+ SOC_SINGLE_TLV("Amp Gain Volume", TAS2780_CHNL_0, 0, 0x14, 0,
+ tas2780_digital_tlv),
+};
+
+static const struct snd_soc_component_driver soc_component_driver_tas2780 = {
+ .probe = tas2780_codec_probe,
+#ifdef CONFIG_PM
+ .suspend = tas2780_codec_suspend,
+ .resume = tas2780_codec_resume,
+#endif
+ .controls = tas2780_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2780_snd_controls),
+ .dapm_widgets = tas2780_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2780_dapm_widgets),
+ .dapm_routes = tas2780_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2780_audio_map),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+static const struct reg_default tas2780_reg_defaults[] = {
+ { TAS2780_PAGE, 0x00 },
+ { TAS2780_SW_RST, 0x00 },
+ { TAS2780_PWR_CTRL, 0x1a },
+ { TAS2780_DVC, 0x00 },
+ { TAS2780_CHNL_0, 0x00 },
+ { TAS2780_TDM_CFG0, 0x09 },
+ { TAS2780_TDM_CFG1, 0x02 },
+ { TAS2780_TDM_CFG2, 0x0a },
+ { TAS2780_TDM_CFG3, 0x10 },
+ { TAS2780_TDM_CFG5, 0x42 },
+};
+
+static const struct regmap_range_cfg tas2780_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 1 * 128,
+ .selector_reg = TAS2780_PAGE,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tas2780_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_defaults = tas2780_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas2780_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .ranges = tas2780_regmap_ranges,
+ .num_ranges = ARRAY_SIZE(tas2780_regmap_ranges),
+ .max_register = 1 * 128,
+};
+
+static int tas2780_parse_dt(struct device *dev, struct tas2780_priv *tas2780)
+{
+ int ret = 0;
+
+ tas2780->reset_gpio = devm_gpiod_get_optional(tas2780->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(tas2780->reset_gpio)) {
+ if (PTR_ERR(tas2780->reset_gpio) == -EPROBE_DEFER) {
+ tas2780->reset_gpio = NULL;
+ return -EPROBE_DEFER;
+ }
+ }
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no",
+ &tas2780->i_sense_slot);
+ if (ret)
+ tas2780->i_sense_slot = 0;
+
+ ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no",
+ &tas2780->v_sense_slot);
+ if (ret)
+ tas2780->v_sense_slot = 2;
+
+ return 0;
+}
+
+static int tas2780_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tas2780_priv *tas2780;
+ int result;
+
+ tas2780 = devm_kzalloc(&client->dev, sizeof(struct tas2780_priv),
+ GFP_KERNEL);
+ if (!tas2780)
+ return -ENOMEM;
+ tas2780->dev = &client->dev;
+ i2c_set_clientdata(client, tas2780);
+ dev_set_drvdata(&client->dev, tas2780);
+
+ tas2780->regmap = devm_regmap_init_i2c(client, &tas2780_i2c_regmap);
+ if (IS_ERR(tas2780->regmap)) {
+ result = PTR_ERR(tas2780->regmap);
+ dev_err(&client->dev, "Failed to allocate register map: %d\n",
+ result);
+ return result;
+ }
+
+ if (client->dev.of_node) {
+ result = tas2780_parse_dt(&client->dev, tas2780);
+ if (result) {
+ dev_err(tas2780->dev,
+ "%s: Failed to parse devicetree\n", __func__);
+ return result;
+ }
+ }
+
+ return devm_snd_soc_register_component(tas2780->dev,
+ &soc_component_driver_tas2780, tas2780_dai_driver,
+ ARRAY_SIZE(tas2780_dai_driver));
+}
+
+static const struct i2c_device_id tas2780_i2c_id[] = {
+ { "tas2780", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas2780_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id tas2780_of_match[] = {
+ { .compatible = "ti,tas2780" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tas2780_of_match);
+#endif
+
+static struct i2c_driver tas2780_i2c_driver = {
+ .driver = {
+ .name = "tas2780",
+ .of_match_table = of_match_ptr(tas2780_of_match),
+ },
+ .probe = tas2780_i2c_probe,
+ .id_table = tas2780_i2c_id,
+};
+module_i2c_driver(tas2780_i2c_driver);
+
+MODULE_AUTHOR("Raphael Xu <[email protected]>");
+MODULE_DESCRIPTION("TAS2780 I2C Smart Amplifier driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2780.h b/sound/soc/codecs/tas2780.h
new file mode 100644
index 000000000000..661c25df4e29
--- /dev/null
+++ b/sound/soc/codecs/tas2780.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * TAS2780.h - ALSA SoC Texas Instruments TAS2780 Mono Audio Amplifier
+ *
+ * Copyright (C) 2020-2022 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Raphael Xu <[email protected]>
+ */
+
+#ifndef __TAS2780_H__
+#define __TAS2780_H__
+
+/* Book Control Register */
+#define TAS2780_BOOKCTL_PAGE 0
+#define TAS2780_BOOKCTL_REG 127
+#define TAS2780_REG(page, reg) ((page * 128) + reg)
+
+/* Page */
+#define TAS2780_PAGE TAS2780_REG(0X0, 0x00)
+#define TAS2780_PAGE_PAGE_MASK 255
+
+/* Software Reset */
+#define TAS2780_SW_RST TAS2780_REG(0X0, 0x01)
+#define TAS2780_RST BIT(0)
+
+/* Power Control */
+#define TAS2780_PWR_CTRL TAS2780_REG(0X0, 0x02)
+#define TAS2780_PWR_CTRL_MASK GENMASK(1, 0)
+#define TAS2780_PWR_CTRL_ACTIVE 0x0
+#define TAS2780_PWR_CTRL_MUTE BIT(0)
+#define TAS2780_PWR_CTRL_SHUTDOWN BIT(1)
+
+#define TAS2780_VSENSE_POWER_EN 3
+#define TAS2780_ISENSE_POWER_EN 4
+
+/* Digital Volume Control */
+#define TAS2780_DVC TAS2780_REG(0X0, 0x1a)
+#define TAS2780_DVC_MAX 0xc9
+
+#define TAS2780_CHNL_0 TAS2780_REG(0X0, 0x03)
+
+/* TDM Configuration Reg0 */
+#define TAS2780_TDM_CFG0 TAS2780_REG(0X0, 0x08)
+#define TAS2780_TDM_CFG0_SMP_MASK BIT(5)
+#define TAS2780_TDM_CFG0_SMP_48KHZ 0x0
+#define TAS2780_TDM_CFG0_SMP_44_1KHZ BIT(5)
+#define TAS2780_TDM_CFG0_MASK GENMASK(3, 1)
+#define TAS2780_TDM_CFG0_44_1_48KHZ BIT(3)
+#define TAS2780_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1))
+
+/* TDM Configuration Reg1 */
+#define TAS2780_TDM_CFG1 TAS2780_REG(0X0, 0x09)
+#define TAS2780_TDM_CFG1_MASK GENMASK(5, 1)
+#define TAS2780_TDM_CFG1_51_SHIFT 1
+#define TAS2780_TDM_CFG1_RX_MASK BIT(0)
+#define TAS2780_TDM_CFG1_RX_RISING 0x0
+#define TAS2780_TDM_CFG1_RX_FALLING BIT(0)
+
+/* TDM Configuration Reg2 */
+#define TAS2780_TDM_CFG2 TAS2780_REG(0X0, 0x0a)
+#define TAS2780_TDM_CFG2_RXW_MASK GENMASK(3, 2)
+#define TAS2780_TDM_CFG2_RXW_16BITS 0x0
+#define TAS2780_TDM_CFG2_RXW_24BITS BIT(3)
+#define TAS2780_TDM_CFG2_RXW_32BITS (BIT(3) | BIT(2))
+#define TAS2780_TDM_CFG2_RXS_MASK GENMASK(1, 0)
+#define TAS2780_TDM_CFG2_RXS_16BITS 0x0
+#define TAS2780_TDM_CFG2_RXS_24BITS BIT(0)
+#define TAS2780_TDM_CFG2_RXS_32BITS BIT(1)
+#define TAS2780_TDM_CFG2_SCFG_MASK GENMASK(5, 4)
+#define TAS2780_TDM_CFG2_SCFG_I2S 0x0
+#define TAS2780_TDM_CFG2_SCFG_LEFT_J BIT(4)
+#define TAS2780_TDM_CFG2_SCFG_RIGHT_J BIT(5)
+
+/* TDM Configuration Reg3 */
+#define TAS2780_TDM_CFG3 TAS2780_REG(0X0, 0x0c)
+#define TAS2780_TDM_CFG3_RXS_MASK GENMASK(7, 4)
+#define TAS2780_TDM_CFG3_RXS_SHIFT 0x4
+#define TAS2780_TDM_CFG3_MASK GENMASK(3, 0)
+
+/* TDM Configuration Reg4 */
+#define TAS2780_TDM_CFG4 TAS2780_REG(0X0, 0x0d)
+#define TAS2780_TDM_CFG4_TX_OFFSET_MASK GENMASK(3, 1)
+
+/* TDM Configuration Reg5 */
+#define TAS2780_TDM_CFG5 TAS2780_REG(0X0, 0x0e)
+#define TAS2780_TDM_CFG5_VSNS_MASK BIT(6)
+#define TAS2780_TDM_CFG5_VSNS_ENABLE BIT(6)
+#define TAS2780_TDM_CFG5_50_MASK GENMASK(5, 0)
+
+/* TDM Configuration Reg6 */
+#define TAS2780_TDM_CFG6 TAS2780_REG(0X0, 0x0f)
+#define TAS2780_TDM_CFG6_ISNS_MASK BIT(6)
+#define TAS2780_TDM_CFG6_ISNS_ENABLE BIT(6)
+#define TAS2780_TDM_CFG6_50_MASK GENMASK(5, 0)
+
+/* IC CFG */
+#define TAS2780_IC_CFG TAS2780_REG(0X0, 0x5c)
+#define TAS2780_IC_CFG_MASK GENMASK(7, 6)
+#define TAS2780_IC_CFG_ENABLE (BIT(7) | BIT(6))
+
+#endif /* __TAS2780_H__ */
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 6618ac4a7d5c..2844a9d2bc4a 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -33,7 +33,6 @@ struct adcx140_priv {
bool micbias_vg;
unsigned int dai_fmt;
- unsigned int tdm_delay;
unsigned int slot_width;
};
@@ -790,12 +789,13 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
{
struct snd_soc_component *component = codec_dai->component;
struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component);
- unsigned int lsb;
- /* TDM based on DSP mode requires slots to be adjacent */
- lsb = __ffs(tx_mask);
- if ((lsb + 1) != __fls(tx_mask)) {
- dev_err(component->dev, "Invalid mask, slots must be adjacent\n");
+ /*
+ * The chip itself supports arbitrary masks, but the driver currently
+ * only supports adjacent slots beginning at the first slot.
+ */
+ if (tx_mask != GENMASK(__fls(tx_mask), 0)) {
+ dev_err(component->dev, "Only lower adjacent slots are supported\n");
return -EINVAL;
}
@@ -810,7 +810,6 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
return -EINVAL;
}
- adcx140->tdm_delay = lsb;
adcx140->slot_width = slot_width;
return 0;
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 3554b95462e8..beeeb35e8032 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -333,7 +333,7 @@ struct wcd9335_codec {
struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY];
unsigned int rx_port_value[WCD9335_RX_MAX];
- unsigned int tx_port_value;
+ unsigned int tx_port_value[WCD9335_TX_MAX];
int hph_l_gain;
int hph_r_gain;
u32 rx_bias_count;
@@ -1325,8 +1325,13 @@ static int slim_tx_mixer_get(struct snd_kcontrol *kc,
struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kc);
struct wcd9335_codec *wcd = dev_get_drvdata(dapm->dev);
+ struct snd_soc_dapm_widget *widget = snd_soc_dapm_kcontrol_widget(kc);
+ struct soc_mixer_control *mixer =
+ (struct soc_mixer_control *)kc->private_value;
+ int dai_id = widget->shift;
+ int port_id = mixer->shift;
- ucontrol->value.integer.value[0] = wcd->tx_port_value;
+ ucontrol->value.integer.value[0] = wcd->tx_port_value[port_id] == dai_id;
return 0;
}
@@ -1349,12 +1354,12 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kc,
case AIF2_CAP:
case AIF3_CAP:
/* only add to the list if value not set */
- if (enable && !(wcd->tx_port_value & BIT(port_id))) {
- wcd->tx_port_value |= BIT(port_id);
+ if (enable && wcd->tx_port_value[port_id] != dai_id) {
+ wcd->tx_port_value[port_id] = dai_id;
list_add_tail(&wcd->tx_chs[port_id].list,
&wcd->dai[dai_id].slim_ch_list);
- } else if (!enable && (wcd->tx_port_value & BIT(port_id))) {
- wcd->tx_port_value &= ~BIT(port_id);
+ } else if (!enable && wcd->tx_port_value[port_id] == dai_id) {
+ wcd->tx_port_value[port_id] = -1;
list_del_init(&wcd->tx_chs[port_id].list);
}
break;
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 8b1caac65c3a..af7d324e3352 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -680,12 +680,17 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ uint16_t dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data);
+ int ret = 0;
mutex_lock(&arizona->dac_comp_lock);
- arizona->dac_comp_coeff = get_unaligned_be16(ucontrol->value.bytes.data);
+ if (arizona->dac_comp_coeff != dac_comp_coeff) {
+ arizona->dac_comp_coeff = dac_comp_coeff;
+ ret = 1;
+ }
mutex_unlock(&arizona->dac_comp_lock);
- return 0;
+ return ret;
}
static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
@@ -706,12 +711,20 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
struct arizona *arizona = dev_get_drvdata(component->dev->parent);
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ int ret = 0;
+
+ if (ucontrol->value.integer.value[0] > mc->max)
+ return -EINVAL;
mutex_lock(&arizona->dac_comp_lock);
- arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
+ if (arizona->dac_comp_enabled != ucontrol->value.integer.value[0]) {
+ arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
+ ret = 1;
+ }
mutex_unlock(&arizona->dac_comp_lock);
- return 0;
+ return ret;
}
static const char * const wm5102_osr_text[] = {
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 328f1946f584..79fc6bbaa3aa 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -108,6 +108,7 @@ static int wm8998_inmux_put(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int mode_reg, mode_index;
unsigned int mux, inmode, src_val, mode_val;
+ int change, ret;
mux = ucontrol->value.enumerated.item[0];
if (mux > 1)
@@ -137,14 +138,20 @@ static int wm8998_inmux_put(struct snd_kcontrol *kcontrol,
snd_soc_component_update_bits(component, mode_reg,
ARIZONA_IN1_MODE_MASK, mode_val);
- snd_soc_component_update_bits(component, e->reg,
- ARIZONA_IN1L_SRC_MASK |
- ARIZONA_IN1L_SRC_SE_MASK,
- src_val);
+ change = snd_soc_component_update_bits(component, e->reg,
+ ARIZONA_IN1L_SRC_MASK |
+ ARIZONA_IN1L_SRC_SE_MASK,
+ src_val);
- return snd_soc_dapm_mux_update_power(dapm, kcontrol,
- ucontrol->value.enumerated.item[0],
- e, NULL);
+ ret = snd_soc_dapm_mux_update_power(dapm, kcontrol,
+ ucontrol->value.enumerated.item[0],
+ e, NULL);
+ if (ret < 0) {
+ dev_err(arizona->dev, "Failed to update demux power state: %d\n", ret);
+ return ret;
+ }
+
+ return change;
}
static const char * const wm8998_inmux_texts[] = {
diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c
index 3e969c7bc1c5..d0fc430f7033 100644
--- a/sound/soc/fsl/fsl_utils.c
+++ b/sound/soc/fsl/fsl_utils.c
@@ -147,7 +147,7 @@ void fsl_asoc_reparent_pll_clocks(struct device *dev, struct clk *clk,
if (reparent) {
ret = clk_set_parent(p, npll);
if (ret < 0)
- dev_warn(dev, "failed to set parent %s: %d\n", __clk_get_name(npll), ret);
+ dev_warn(dev, "failed to set parent:%d\n", ret);
}
}
EXPORT_SYMBOL(fsl_asoc_reparent_pll_clocks);
diff --git a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
index 8eee7b821ff7..fe547c18771f 100644
--- a/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
+++ b/sound/soc/generic/audio-graph-card2-custom-sample.dtsi
@@ -17,6 +17,23 @@
* CONFIG_SND_AUDIO_GRAPH_CARD2
* CONFIG_SND_AUDIO_GRAPH_CARD2_CUSTOM_SAMPLE
* CONFIG_SND_TEST_COMPONENT
+ *
+ *
+ * You can indicate more detail each device behavior as debug if you modify
+ * "compatible" on each test-component. see below
+ *
+ * test_cpu {
+ * - compatible = "test-cpu";
+ * + compatible = "test-cpu-verbose";
+ * ...
+ * };
+ *
+ * test_codec {
+ * - compatible = "test-codec";
+ * + compatible = "test-codec-verbose";
+ * ...
+ * };
+ *
*/
/ {
/*
@@ -101,35 +118,74 @@
"TC OUT", "TC DAI11 Playback",
"TC DAI9 Capture", "TC IN";
- links = <&cpu0 /* normal: cpu side only */
- &mcpu0 /* multi: cpu side only */
- &fe00 &fe01 &be0 /* dpcm: both FE / BE */
- &fe10 &fe11 &be1 /* dpcm-m: both FE / BE */
- &c2c /* c2c: cpu side only */
- &c2c_m /* c2c: cpu side only */
+ links = <
+ /*
+ * [Normal]: cpu side only
+ * cpu0/codec0
+ */
+ &cpu0
+
+ /*
+ * [Multi-CPU/Codec]: cpu side only
+ * cpu1/cpu2/codec1/codec2
+ */
+ &mcpu0
+
+ /*
+ * [DPCM]: both FE / BE
+ * cpu3/cpu4/codec3
+ */
+ &fe00 &fe01 &be0
+
+ /*
+ * [DPCM-Multi]: both FE / BE
+ * cpu5/cpu6/codec4/codec5
+ */
+ &fe10 &fe11 &be1
+
+ /*
+ * [Codec2Codec]: cpu side only
+ * codec6/codec7
+ */
+ &c2c
+
+ /*
+ * [Codec2Codec-Multi]: cpu side only
+ * codec8/codec9/codec10/codec11
+ */
+ &c2c_m
>;
multi {
ports@0 {
+ /* [Multi-CPU] */
mcpu0: port@0 { mcpu0_ep: endpoint { remote-endpoint = <&mcodec0_ep>; }; };
port@1 { mcpu1_ep: endpoint { remote-endpoint = <&cpu1_ep>; }; };
port@2 { mcpu2_ep: endpoint { remote-endpoint = <&cpu2_ep>; }; };
};
+
+ /* [Multi-Codec] */
ports@1 {
port@0 { mcodec0_ep: endpoint { remote-endpoint = <&mcpu0_ep>; }; };
port@1 { mcodec1_ep: endpoint { remote-endpoint = <&codec1_ep>; }; };
port@2 { mcodec2_ep: endpoint { remote-endpoint = <&codec2_ep>; }; };
};
+
+ /* [DPCM-Multi]::BE */
ports@2 {
port@0 { mbe_ep: endpoint { remote-endpoint = <&be10_ep>; }; };
port@1 { mbe1_ep: endpoint { remote-endpoint = <&codec4_ep>; }; };
port@2 { mbe2_ep: endpoint { remote-endpoint = <&codec5_ep>; }; };
};
+
+ /* [Codec2Codec-Multi]::CPU */
ports@3 {
port@0 { mc2c0_ep: endpoint { remote-endpoint = <&c2cmf_ep>; }; };
port@1 { mc2c00_ep: endpoint { remote-endpoint = <&codec8_ep>; }; };
port@2 { mc2c01_ep: endpoint { remote-endpoint = <&codec9_ep>; }; };
};
+
+ /* [Codec2Codec-Multi]::Codec */
ports@4 {
port@0 { mc2c1_ep: endpoint { remote-endpoint = <&c2cmb_ep>; }; };
port@1 { mc2c10_ep: endpoint { remote-endpoint = <&codec10_ep>; }; };
@@ -138,27 +194,36 @@
};
dpcm {
- /* FE */
ports@0 {
+ /* [DPCM]::FE */
fe00: port@0 { fe00_ep: endpoint { remote-endpoint = <&cpu3_ep>; }; };
fe01: port@1 { fe01_ep: endpoint { remote-endpoint = <&cpu4_ep>; }; };
+
+ /* [DPCM-Multi]::FE */
fe10: port@2 { fe10_ep: endpoint { remote-endpoint = <&cpu5_ep>; }; };
fe11: port@3 { fe11_ep: endpoint { remote-endpoint = <&cpu6_ep>; }; };
};
- /* BE */
+
ports@1 {
+ /* [DPCM]::BE */
be0: port@0 { be00_ep: endpoint { remote-endpoint = <&codec3_ep>; }; };
+
+ /* [DPCM-Multi]::BE */
be1: port@1 { be10_ep: endpoint { remote-endpoint = <&mbe_ep>; }; };
};
};
codec2codec {
+ /* [Codec2Codec] */
ports@0 {
- rate = <48000>;
+ /* use default settings */
c2c: port@0 { c2cf_ep: endpoint { remote-endpoint = <&codec6_ep>; }; };
port@1 { c2cb_ep: endpoint { remote-endpoint = <&codec7_ep>; }; };
};
+
+ /* [Codec2Codec-Multi] */
ports@1 {
+ /* use original settings */
rate = <48000>;
c2c_m: port@0 { c2cmf_ep: endpoint { remote-endpoint = <&mc2c0_ep>; }; };
port@1 { c2cmb_ep: endpoint { remote-endpoint = <&mc2c1_ep>; }; };
@@ -179,11 +244,18 @@
ports {
bitclock-master;
frame-master;
+ /* [Normal] */
cpu0: port@0 { cpu0_ep: endpoint { remote-endpoint = <&codec0_ep>; }; };
+
+ /* [Multi-CPU] */
port@1 { cpu1_ep: endpoint { remote-endpoint = <&mcpu1_ep>; }; };
port@2 { cpu2_ep: endpoint { remote-endpoint = <&mcpu2_ep>; }; };
+
+ /* [DPCM]::FE */
port@3 { cpu3_ep: endpoint { remote-endpoint = <&fe00_ep>; }; };
port@4 { cpu4_ep: endpoint { remote-endpoint = <&fe01_ep>; }; };
+
+ /* [DPCM-Multi]::FE */
port@5 { cpu5_ep: endpoint { remote-endpoint = <&fe10_ep>; }; };
port@6 { cpu6_ep: endpoint { remote-endpoint = <&fe11_ep>; }; };
};
@@ -206,16 +278,27 @@
*/
prefix = "TC";
+ /* [Normal] */
port@0 { codec0_ep: endpoint { remote-endpoint = <&cpu0_ep>; }; };
+
+ /* [Multi-Codec] */
port@1 { codec1_ep: endpoint { remote-endpoint = <&mcodec1_ep>; }; };
port@2 { codec2_ep: endpoint { remote-endpoint = <&mcodec2_ep>; }; };
+
+ /* [DPCM]::BE */
port@3 { codec3_ep: endpoint { remote-endpoint = <&be00_ep>; }; };
+
+ /* [DPCM-Multi]::BE */
port@4 { codec4_ep: endpoint { remote-endpoint = <&mbe1_ep>; }; };
port@5 { codec5_ep: endpoint { remote-endpoint = <&mbe2_ep>; }; };
+
+ /* [Codec2Codec] */
port@6 { bitclock-master;
frame-master;
codec6_ep: endpoint { remote-endpoint = <&c2cf_ep>; }; };
port@7 { codec7_ep: endpoint { remote-endpoint = <&c2cb_ep>; }; };
+
+ /* [Codec2Codec-Multi] */
port@8 { bitclock-master;
frame-master;
codec8_ep: endpoint { remote-endpoint = <&mc2c00_ep>; }; };
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index d34b29a49268..19e31d53422a 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -851,12 +851,10 @@ int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
struct link_info *li)
{
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
- struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
- struct snd_soc_pcm_stream *c2c_conf = dai_props->c2c_conf;
struct device_node *port0, *port1, *ports;
struct device_node *codec0_port, *codec1_port;
struct device_node *ep0, *ep1;
- u32 val;
+ u32 val = 0;
int ret = -EINVAL;
/*
@@ -880,19 +878,33 @@ int audio_graph2_link_c2c(struct asoc_simple_priv *priv,
ports = of_get_parent(port0);
port1 = of_get_next_child(ports, lnk);
- if (!of_get_property(ports, "rate", &val)) {
+ /*
+ * Card2 can use original Codec2Codec settings if DT has.
+ * It will use default settings if no settings on DT.
+ * see
+ * asoc_simple_init_for_codec2codec()
+ *
+ * Add more settings here if needed
+ */
+ of_property_read_u32(ports, "rate", &val);
+ if (val) {
struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_pcm_stream *c2c_conf;
- dev_err(dev, "Codec2Codec needs rate settings\n");
- goto err1;
- }
+ c2c_conf = devm_kzalloc(dev, sizeof(*c2c_conf), GFP_KERNEL);
+ if (!c2c_conf)
+ goto err1;
- c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */
- c2c_conf->rate_min =
- c2c_conf->rate_max = val;
- c2c_conf->channels_min =
- c2c_conf->channels_max = 2; /* update ME */
- dai_link->params = c2c_conf;
+ c2c_conf->formats = SNDRV_PCM_FMTBIT_S32_LE; /* update ME */
+ c2c_conf->rates = SNDRV_PCM_RATE_8000_384000;
+ c2c_conf->rate_min =
+ c2c_conf->rate_max = val;
+ c2c_conf->channels_min =
+ c2c_conf->channels_max = 2; /* update ME */
+
+ dai_link->params = c2c_conf;
+ dai_link->num_params = 1;
+ }
ep0 = port_to_endpoint(port0);
ep1 = port_to_endpoint(port1);
@@ -1086,7 +1098,6 @@ static int graph_count_c2c(struct asoc_simple_priv *priv,
li->num[li->link].cpus =
li->num[li->link].platforms = graph_counter(codec0);
li->num[li->link].codecs = graph_counter(codec1);
- li->num[li->link].c2c = 1;
of_node_put(ports);
of_node_put(port1);
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 7be84c7840cb..4a29e314fa95 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -527,6 +527,14 @@ static int asoc_simple_init_for_codec2codec(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hardware hw;
int i, ret, stream;
+ /* Do nothing if it already has Codec2Codec settings */
+ if (dai_link->params)
+ return 0;
+
+ /* Do nothing if it was DPCM :: BE */
+ if (dai_link->no_pcm)
+ return 0;
+
/* Only Codecs */
for_each_rtd_components(rtd, i, component) {
if (!asoc_simple_component_is_codec(component))
@@ -746,8 +754,7 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
struct asoc_simple_dai *dais;
struct snd_soc_dai_link_component *dlcs;
struct snd_soc_codec_conf *cconf = NULL;
- struct snd_soc_pcm_stream *c2c_conf = NULL;
- int i, dai_num = 0, dlc_num = 0, cnf_num = 0, c2c_num = 0;
+ int i, dai_num = 0, dlc_num = 0, cnf_num = 0;
dai_props = devm_kcalloc(dev, li->link, sizeof(*dai_props), GFP_KERNEL);
dai_link = devm_kcalloc(dev, li->link, sizeof(*dai_link), GFP_KERNEL);
@@ -766,8 +773,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
if (!li->num[i].cpus)
cnf_num += li->num[i].codecs;
-
- c2c_num += li->num[i].c2c;
}
dais = devm_kcalloc(dev, dai_num, sizeof(*dais), GFP_KERNEL);
@@ -781,12 +786,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
return -ENOMEM;
}
- if (c2c_num) {
- c2c_conf = devm_kcalloc(dev, c2c_num, sizeof(*c2c_conf), GFP_KERNEL);
- if (!c2c_conf)
- return -ENOMEM;
- }
-
dev_dbg(dev, "link %d, dais %d, ccnf %d\n",
li->link, dai_num, cnf_num);
@@ -800,7 +799,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
priv->dais = dais;
priv->dlcs = dlcs;
priv->codec_conf = cconf;
- priv->c2c_conf = c2c_conf;
card->dai_link = priv->dai_link;
card->num_links = li->link;
@@ -818,12 +816,6 @@ int asoc_simple_init_priv(struct asoc_simple_priv *priv,
dlcs += li->num[i].cpus;
dais += li->num[i].cpus;
-
- if (li->num[i].c2c) {
- /* Codec2Codec */
- dai_props[i].c2c_conf = c2c_conf;
- c2c_conf += li->num[i].c2c;
- }
} else {
/* DPCM Be's CPU = dummy */
dai_props[i].cpus =
diff --git a/sound/soc/intel/boards/sof_cs42l42.c b/sound/soc/intel/boards/sof_cs42l42.c
index a1a14d6d7c23..85ffd065895d 100644
--- a/sound/soc/intel/boards/sof_cs42l42.c
+++ b/sound/soc/intel/boards/sof_cs42l42.c
@@ -41,8 +41,13 @@
#define SOF_CS42L42_DAILINK_MASK (GENMASK(24, 10))
#define SOF_CS42L42_DAILINK(link1, link2, link3, link4, link5) \
((((link1) | ((link2) << 3) | ((link3) << 6) | ((link4) << 9) | ((link5) << 12)) << SOF_CS42L42_DAILINK_SHIFT) & SOF_CS42L42_DAILINK_MASK)
-#define SOF_MAX98357A_SPEAKER_AMP_PRESENT BIT(25)
-#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(26)
+#define SOF_BT_OFFLOAD_PRESENT BIT(25)
+#define SOF_CS42L42_SSP_BT_SHIFT 26
+#define SOF_CS42L42_SSP_BT_MASK (GENMASK(28, 26))
+#define SOF_CS42L42_SSP_BT(quirk) \
+ (((quirk) << SOF_CS42L42_SSP_BT_SHIFT) & SOF_CS42L42_SSP_BT_MASK)
+#define SOF_MAX98357A_SPEAKER_AMP_PRESENT BIT(29)
+#define SOF_MAX98360A_SPEAKER_AMP_PRESENT BIT(30)
enum {
LINK_NONE = 0,
@@ -50,6 +55,7 @@ enum {
LINK_SPK = 2,
LINK_DMIC = 3,
LINK_HDMI = 4,
+ LINK_BT = 5,
};
static struct snd_soc_jack_pin jack_pins[] = {
@@ -290,6 +296,13 @@ static struct snd_soc_dai_link_component dmic_component[] = {
}
};
+static struct snd_soc_dai_link_component dummy_component[] = {
+ {
+ .name = "snd-soc-dummy",
+ .dai_name = "snd-soc-dummy-dai",
+ }
+};
+
static int create_spk_amp_dai_links(struct device *dev,
struct snd_soc_dai_link *links,
struct snd_soc_dai_link_component *cpus,
@@ -479,9 +492,50 @@ devm_err:
return -ENOMEM;
}
+static int create_bt_offload_dai_links(struct device *dev,
+ struct snd_soc_dai_link *links,
+ struct snd_soc_dai_link_component *cpus,
+ int *id, int ssp_bt)
+{
+ /* bt offload */
+ if (!(sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT))
+ return 0;
+
+ links[*id].name = devm_kasprintf(dev, GFP_KERNEL, "SSP%d-BT",
+ ssp_bt);
+ if (!links[*id].name)
+ goto devm_err;
+
+ links[*id].id = *id;
+ links[*id].codecs = dummy_component;
+ links[*id].num_codecs = ARRAY_SIZE(dummy_component);
+ links[*id].platforms = platform_component;
+ links[*id].num_platforms = ARRAY_SIZE(platform_component);
+
+ links[*id].dpcm_playback = 1;
+ links[*id].dpcm_capture = 1;
+ links[*id].no_pcm = 1;
+ links[*id].cpus = &cpus[*id];
+ links[*id].num_cpus = 1;
+
+ links[*id].cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ "SSP%d Pin",
+ ssp_bt);
+ if (!links[*id].cpus->dai_name)
+ goto devm_err;
+
+ (*id)++;
+
+ return 0;
+
+devm_err:
+ return -ENOMEM;
+}
+
static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
int ssp_codec,
int ssp_amp,
+ int ssp_bt,
int dmic_be_num,
int hdmi_num)
{
@@ -534,6 +588,14 @@ static struct snd_soc_dai_link *sof_card_dai_links_create(struct device *dev,
goto devm_err;
}
break;
+ case LINK_BT:
+ ret = create_bt_offload_dai_links(dev, links, cpus, &id, ssp_bt);
+ if (ret < 0) {
+ dev_err(dev, "fail to create bt offload dai links, ret %d\n",
+ ret);
+ goto devm_err;
+ }
+ break;
case LINK_NONE:
/* caught here if it's not used as terminator in macro */
default:
@@ -555,7 +617,7 @@ static int sof_audio_probe(struct platform_device *pdev)
struct snd_soc_acpi_mach *mach;
struct sof_card_private *ctx;
int dmic_be_num, hdmi_num;
- int ret, ssp_amp, ssp_codec;
+ int ret, ssp_bt, ssp_amp, ssp_codec;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
@@ -580,6 +642,9 @@ static int sof_audio_probe(struct platform_device *pdev)
dev_dbg(&pdev->dev, "sof_cs42l42_quirk = %lx\n", sof_cs42l42_quirk);
+ ssp_bt = (sof_cs42l42_quirk & SOF_CS42L42_SSP_BT_MASK) >>
+ SOF_CS42L42_SSP_BT_SHIFT;
+
ssp_amp = (sof_cs42l42_quirk & SOF_CS42L42_SSP_AMP_MASK) >>
SOF_CS42L42_SSP_AMP_SHIFT;
@@ -590,9 +655,11 @@ static int sof_audio_probe(struct platform_device *pdev)
if (sof_cs42l42_quirk & SOF_SPEAKER_AMP_PRESENT)
sof_audio_card_cs42l42.num_links++;
+ if (sof_cs42l42_quirk & SOF_BT_OFFLOAD_PRESENT)
+ sof_audio_card_cs42l42.num_links++;
dai_links = sof_card_dai_links_create(&pdev->dev, ssp_codec, ssp_amp,
- dmic_be_num, hdmi_num);
+ ssp_bt, dmic_be_num, hdmi_num);
if (!dai_links)
return -ENOMEM;
@@ -633,6 +700,17 @@ static const struct platform_device_id board_ids[] = {
SOF_CS42L42_SSP_AMP(1)) |
SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_NONE),
},
+ {
+ .name = "adl_mx98360a_cs4242",
+ .driver_data = (kernel_ulong_t)(SOF_CS42L42_SSP_CODEC(0) |
+ SOF_SPEAKER_AMP_PRESENT |
+ SOF_MAX98360A_SPEAKER_AMP_PRESENT |
+ SOF_CS42L42_SSP_AMP(1) |
+ SOF_CS42L42_NUM_HDMIDEV(4) |
+ SOF_BT_OFFLOAD_PRESENT |
+ SOF_CS42L42_SSP_BT(2) |
+ SOF_CS42L42_DAILINK(LINK_HP, LINK_DMIC, LINK_HDMI, LINK_SPK, LINK_BT)),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index a24fb71d5ff3..1384716c6360 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -69,11 +69,10 @@ static unsigned long sof_rt5682_quirk = SOF_RT5682_MCLK_EN |
static int is_legacy_cpu;
-static struct snd_soc_jack sof_hdmi[3];
-
struct sof_hdmi_pcm {
struct list_head head;
struct snd_soc_dai *codec_dai;
+ struct snd_soc_jack hdmi_jack;
int device;
};
@@ -447,7 +446,6 @@ static int sof_card_late_probe(struct snd_soc_card *card)
char jack_name[NAME_SIZE];
struct sof_hdmi_pcm *pcm;
int err;
- int i = 0;
/* HDMI is not supported by SOF on Baytrail/CherryTrail */
if (is_legacy_cpu || !ctx->idisp_codec)
@@ -468,17 +466,15 @@ static int sof_card_late_probe(struct snd_soc_card *card)
snprintf(jack_name, sizeof(jack_name),
"HDMI/DP, pcm=%d Jack", pcm->device);
err = snd_soc_card_jack_new(card, jack_name,
- SND_JACK_AVOUT, &sof_hdmi[i]);
+ SND_JACK_AVOUT, &pcm->hdmi_jack);
if (err)
return err;
err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device,
- &sof_hdmi[i]);
+ &pcm->hdmi_jack);
if (err < 0)
return err;
-
- i++;
}
if (sof_rt5682_quirk & SOF_MAX98373_SPEAKER_AMP_PRESENT) {
diff --git a/sound/soc/intel/common/soc-acpi-intel-adl-match.c b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
index c1385161cdc8..fea087d3fa15 100644
--- a/sound/soc/intel/common/soc-acpi-intel-adl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-adl-match.c
@@ -479,6 +479,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_adl_machines[] = {
.drv_name = "adl_rt5682",
.sof_tplg_filename = "sof-adl-rt5682.tplg",
},
+ {
+ .id = "10134242",
+ .drv_name = "adl_mx98360a_cs4242",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &adl_max98360a_amp,
+ .sof_tplg_filename = "sof-adl-max98360a-cs42l42.tplg",
+ },
/* place amp-only boards in the end of table */
{
.id = "CSC3541",
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 2439a574ac2f..deb7b820325e 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -99,7 +99,6 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
struct nhlt_fmt_cfg *fmt_cfg;
struct wav_fmt_ext *wav_fmt;
unsigned long rate;
- bool present = false;
int rate_index = 0;
u16 channels, bps;
u8 clk_src;
@@ -112,9 +111,12 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
if (fmt->fmt_count == 0)
return;
+ fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
for (i = 0; i < fmt->fmt_count; i++) {
- fmt_cfg = &fmt->fmt_config[i];
- wav_fmt = &fmt_cfg->fmt_ext;
+ struct nhlt_fmt_cfg *saved_fmt_cfg = fmt_cfg;
+ bool present = false;
+
+ wav_fmt = &saved_fmt_cfg->fmt_ext;
channels = wav_fmt->fmt.channels;
bps = wav_fmt->fmt.bits_per_sample;
@@ -132,12 +134,18 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
* derive the rate.
*/
for (j = i; j < fmt->fmt_count; j++) {
- fmt_cfg = &fmt->fmt_config[j];
- wav_fmt = &fmt_cfg->fmt_ext;
+ struct nhlt_fmt_cfg *tmp_fmt_cfg = fmt_cfg;
+
+ wav_fmt = &tmp_fmt_cfg->fmt_ext;
if ((fs == wav_fmt->fmt.samples_per_sec) &&
- (bps == wav_fmt->fmt.bits_per_sample))
+ (bps == wav_fmt->fmt.bits_per_sample)) {
channels = max_t(u16, channels,
wav_fmt->fmt.channels);
+ saved_fmt_cfg = tmp_fmt_cfg;
+ }
+ /* Move to the next nhlt_fmt_cfg */
+ tmp_fmt_cfg = (struct nhlt_fmt_cfg *)(tmp_fmt_cfg->config.caps +
+ tmp_fmt_cfg->config.size);
}
rate = channels * bps * fs;
@@ -153,8 +161,11 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
/* Fill rate and parent for sclk/sclkfs */
if (!present) {
+ struct nhlt_fmt_cfg *first_fmt_cfg;
+
+ first_fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
i2s_config_ext = (struct skl_i2s_config_blob_ext *)
- fmt->fmt_config[0].config.caps;
+ first_fmt_cfg->config.caps;
/* MCLK Divider Source Select */
if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
@@ -168,6 +179,9 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
parent = skl_get_parent_clk(clk_src);
+ /* Move to the next nhlt_fmt_cfg */
+ fmt_cfg = (struct nhlt_fmt_cfg *)(fmt_cfg->config.caps +
+ fmt_cfg->config.size);
/*
* Do not copy the config data if there is no parent
* clock available for this clock source select
@@ -176,9 +190,9 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks,
continue;
sclk[id].rate_cfg[rate_index].rate = rate;
- sclk[id].rate_cfg[rate_index].config = fmt_cfg;
+ sclk[id].rate_cfg[rate_index].config = saved_fmt_cfg;
sclkfs[id].rate_cfg[rate_index].rate = rate;
- sclkfs[id].rate_cfg[rate_index].config = fmt_cfg;
+ sclkfs[id].rate_cfg[rate_index].config = saved_fmt_cfg;
sclk[id].parent_name = parent->name;
sclkfs[id].parent_name = parent->name;
@@ -192,13 +206,13 @@ static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk,
{
struct skl_i2s_config_blob_ext *i2s_config_ext;
struct skl_i2s_config_blob_legacy *i2s_config;
- struct nhlt_specific_cfg *fmt_cfg;
+ struct nhlt_fmt_cfg *fmt_cfg;
struct skl_clk_parent_src *parent;
u32 clkdiv, div_ratio;
u8 clk_src;
- fmt_cfg = &fmt->fmt_config[0].config;
- i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->caps;
+ fmt_cfg = (struct nhlt_fmt_cfg *)fmt->fmt_config;
+ i2s_config_ext = (struct skl_i2s_config_blob_ext *)fmt_cfg->config.caps;
/* MCLK Divider Source Select and divider */
if (is_legacy_blob(i2s_config_ext->hdr.sig)) {
@@ -227,7 +241,7 @@ static void skl_get_mclk(struct skl_dev *skl, struct skl_ssp_clk *mclk,
return;
mclk[id].rate_cfg[0].rate = parent->rate/div_ratio;
- mclk[id].rate_cfg[0].config = &fmt->fmt_config[0];
+ mclk[id].rate_cfg[0].config = fmt_cfg;
mclk[id].parent_name = parent->name;
}
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
index db71b032770d..6be6d4f3b585 100644
--- a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
@@ -295,8 +295,6 @@ static int mtk_adda_pad_top_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_PRE_PMU:
if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2_CLK_P2)
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x39);
- else if (afe_priv->mtkaif_protocol == MTKAIF_PROTOCOL_2)
- regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
else
regmap_write(afe->regmap, AFE_AUD_PAD_TOP, 0x31);
break;
diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c
index f424d7aa389a..794019286c70 100644
--- a/sound/soc/qcom/qdsp6/q6apm.c
+++ b/sound/soc/qcom/qdsp6/q6apm.c
@@ -75,6 +75,7 @@ static struct audioreach_graph *q6apm_get_audioreach_graph(struct q6apm *apm, ui
id = idr_alloc(&apm->graph_idr, graph, graph_id, graph_id + 1, GFP_KERNEL);
if (id < 0) {
dev_err(apm->dev, "Unable to allocate graph id (%d)\n", graph_id);
+ kfree(graph->graph);
kfree(graph);
mutex_unlock(&apm->lock);
return ERR_PTR(id);
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index ee33c5d2e948..f5f3540a9e18 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -803,7 +803,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
i2s->bclk_ratio = 64;
i2s->pinctrl = devm_pinctrl_get(&pdev->dev);
-
if (!IS_ERR(i2s->pinctrl)) {
i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on");
if (!IS_ERR_OR_NULL(i2s->bclk_on)) {
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c
index 99b62fe7a95c..9448d5338423 100644
--- a/sound/soc/sof/ipc3-topology.c
+++ b/sound/soc/sof/ipc3-topology.c
@@ -2348,15 +2348,10 @@ static int sof_ipc3_parse_manifest(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
- if (SOF_ABI_VERSION_MINOR(abi_version) > SOF_ABI_MINOR) {
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
- dev_warn(scomp->dev, "%s: Topology ABI is more recent than kernel\n",
- __func__);
- } else {
- dev_err(scomp->dev, "%s: Topology ABI is more recent than kernel\n",
- __func__);
- return -EINVAL;
- }
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS) &&
+ SOF_ABI_VERSION_MINOR(abi_version) > SOF_ABI_MINOR) {
+ dev_err(scomp->dev, "%s: Topology ABI is more recent than kernel\n", __func__);
+ return -EINVAL;
}
return 0;
diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c
index 1fb132b477bf..82fa320253be 100644
--- a/sound/soc/sof/ipc3.c
+++ b/sound/soc/sof/ipc3.c
@@ -758,13 +758,10 @@ int sof_ipc3_validate_fw_version(struct snd_sof_dev *sdev)
return -EINVAL;
}
- if (SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) {
- if (!IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS)) {
- dev_warn(sdev->dev, "FW ABI is more recent than kernel\n");
- } else {
- dev_err(sdev->dev, "FW ABI is more recent than kernel\n");
- return -EINVAL;
- }
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_STRICT_ABI_CHECKS) &&
+ SOF_ABI_VERSION_MINOR(v->abi_version) > SOF_ABI_MINOR) {
+ dev_err(sdev->dev, "FW ABI is more recent than kernel\n");
+ return -EINVAL;
}
if (ready->flags & SOF_IPC_INFO_BUILD) {
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 34f805431f2e..22ea628d78d0 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -263,6 +263,16 @@ err_in:
return ret;
}
+/* release the memory allocated in sof_ipc4_get_audio_fmt */
+static void sof_ipc4_free_audio_fmt(struct sof_ipc4_available_audio_format *available_fmt)
+
+{
+ kfree(available_fmt->base_config);
+ available_fmt->base_config = NULL;
+ kfree(available_fmt->out_audio_fmt);
+ available_fmt->out_audio_fmt = NULL;
+}
+
static void sof_ipc4_widget_free_comp(struct snd_sof_widget *swidget)
{
kfree(swidget->private);
@@ -341,7 +351,7 @@ static int sof_ipc4_widget_setup_pcm(struct snd_sof_widget *swidget)
GFP_KERNEL);
if (!available_fmt->dma_buffer_size) {
ret = -ENOMEM;
- goto free_copier;
+ goto free_available_fmt;
}
ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
@@ -392,8 +402,11 @@ free_gtw_attr:
kfree(ipc4_copier->gtw_attr);
err:
kfree(available_fmt->dma_buffer_size);
+free_available_fmt:
+ sof_ipc4_free_audio_fmt(available_fmt);
free_copier:
kfree(ipc4_copier);
+ swidget->private = NULL;
return ret;
}
@@ -439,7 +452,7 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
GFP_KERNEL);
if (!available_fmt->dma_buffer_size) {
ret = -ENOMEM;
- goto free_copier;
+ goto free_available_fmt;
}
ret = sof_update_ipc_object(scomp, available_fmt->dma_buffer_size,
@@ -539,8 +552,12 @@ free_copier_config:
kfree(ipc4_copier->copier_config);
err:
kfree(available_fmt->dma_buffer_size);
+free_available_fmt:
+ sof_ipc4_free_audio_fmt(available_fmt);
free_copier:
kfree(ipc4_copier);
+ dai->private = NULL;
+ dai->scomp = NULL;
return ret;
}
@@ -553,6 +570,12 @@ static void sof_ipc4_widget_free_comp_dai(struct snd_sof_widget *swidget)
if (!dai)
return;
+ if (!dai->private) {
+ kfree(dai);
+ swidget->private = NULL;
+ return;
+ }
+
ipc4_copier = dai->private;
available_fmt = &ipc4_copier->available_fmt;
@@ -668,10 +691,24 @@ static int sof_ipc4_widget_setup_comp_pga(struct snd_sof_widget *swidget)
return 0;
err:
+ sof_ipc4_free_audio_fmt(&gain->available_fmt);
kfree(gain);
+ swidget->private = NULL;
return ret;
}
+static void sof_ipc4_widget_free_comp_pga(struct snd_sof_widget *swidget)
+{
+ struct sof_ipc4_gain *gain = swidget->private;
+
+ if (!gain)
+ return;
+
+ sof_ipc4_free_audio_fmt(&gain->available_fmt);
+ kfree(swidget->private);
+ swidget->private = NULL;
+}
+
static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
{
struct snd_soc_component *scomp = swidget->scomp;
@@ -697,10 +734,24 @@ static int sof_ipc4_widget_setup_comp_mixer(struct snd_sof_widget *swidget)
return 0;
err:
+ sof_ipc4_free_audio_fmt(&mixer->available_fmt);
kfree(mixer);
+ swidget->private = NULL;
return ret;
}
+static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)
+{
+ struct sof_ipc4_mixer *mixer = swidget->private;
+
+ if (!mixer)
+ return;
+
+ sof_ipc4_free_audio_fmt(&mixer->available_fmt);
+ kfree(swidget->private);
+ swidget->private = NULL;
+}
+
static void
sof_ipc4_update_pipeline_mem_usage(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget,
struct sof_ipc4_base_module_cfg *base_config)
@@ -1735,11 +1786,11 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
[snd_soc_dapm_scheduler] = {sof_ipc4_widget_setup_comp_pipeline, sof_ipc4_widget_free_comp,
pipeline_token_list, ARRAY_SIZE(pipeline_token_list), NULL,
NULL, NULL},
- [snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp,
+ [snd_soc_dapm_pga] = {sof_ipc4_widget_setup_comp_pga, sof_ipc4_widget_free_comp_pga,
pga_token_list, ARRAY_SIZE(pga_token_list), NULL,
sof_ipc4_prepare_gain_module,
sof_ipc4_unprepare_generic_module},
- [snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp,
+ [snd_soc_dapm_mixer] = {sof_ipc4_widget_setup_comp_mixer, sof_ipc4_widget_free_comp_mixer,
mixer_token_list, ARRAY_SIZE(mixer_token_list),
NULL, sof_ipc4_prepare_mixer_module,
sof_ipc4_unprepare_generic_module},
diff --git a/sound/soc/ti/omap-mcbsp-priv.h b/sound/soc/ti/omap-mcbsp-priv.h
index 7865cda4bf0a..da519ea1f303 100644
--- a/sound/soc/ti/omap-mcbsp-priv.h
+++ b/sound/soc/ti/omap-mcbsp-priv.h
@@ -316,8 +316,6 @@ static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg,
/* Sidetone specific API */
int omap_mcbsp_st_init(struct platform_device *pdev);
-void omap_mcbsp_st_cleanup(struct platform_device *pdev);
-
int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp);
int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp);
diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c
index 0bc7d26c660a..7e8179cae92e 100644
--- a/sound/soc/ti/omap-mcbsp-st.c
+++ b/sound/soc/ti/omap-mcbsp-st.c
@@ -347,7 +347,7 @@ int omap_mcbsp_st_init(struct platform_device *pdev)
if (!st_data)
return -ENOMEM;
- st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick");
+ st_data->mcbsp_iclk = devm_clk_get(mcbsp->dev, "ick");
if (IS_ERR(st_data->mcbsp_iclk)) {
dev_warn(mcbsp->dev,
"Failed to get ick, sidetone might be broken\n");
@@ -359,7 +359,7 @@ int omap_mcbsp_st_init(struct platform_device *pdev)
if (!st_data->io_base_st)
return -ENOMEM;
- ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group);
+ ret = devm_device_add_group(mcbsp->dev, &sidetone_attr_group);
if (ret)
return ret;
@@ -368,16 +368,6 @@ int omap_mcbsp_st_init(struct platform_device *pdev)
return 0;
}
-void omap_mcbsp_st_cleanup(struct platform_device *pdev)
-{
- struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev);
-
- if (mcbsp->st_data) {
- sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
- clk_put(mcbsp->st_data->mcbsp_iclk);
- }
-}
-
static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c
index 76df0e7844f8..c4ac1f30b9fe 100644
--- a/sound/soc/ti/omap-mcbsp.c
+++ b/sound/soc/ti/omap-mcbsp.c
@@ -702,8 +702,7 @@ static int omap_mcbsp_init(struct platform_device *pdev)
mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10;
mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10;
- ret = sysfs_create_group(&mcbsp->dev->kobj,
- &additional_attr_group);
+ ret = devm_device_add_group(mcbsp->dev, &additional_attr_group);
if (ret) {
dev_err(mcbsp->dev,
"Unable to create additional controls\n");
@@ -711,16 +710,7 @@ static int omap_mcbsp_init(struct platform_device *pdev)
}
}
- ret = omap_mcbsp_st_init(pdev);
- if (ret)
- goto err_st;
-
- return 0;
-
-err_st:
- if (mcbsp->pdata->buffer_size)
- sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
- return ret;
+ return omap_mcbsp_st_init(pdev);
}
/*
@@ -1432,11 +1422,6 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)
if (cpu_latency_qos_request_active(&mcbsp->pm_qos_req))
cpu_latency_qos_remove_request(&mcbsp->pm_qos_req);
- if (mcbsp->pdata->buffer_size)
- sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
-
- omap_mcbsp_st_cleanup(pdev);
-
return 0;
}