aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/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/nau8825.c2
-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
37 files changed, 1281 insertions, 196 deletions
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/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/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/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;
}