summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-11-25 14:27:33 +0100
committerTakashi Iwai <tiwai@suse.de>2019-11-25 14:27:41 +0100
commit09578eacaaa44149738267083ccc050990409f86 (patch)
treecf614ee065fe5479e4638abebe12c9e1578b09d9 /sound/soc/codecs
parentc6b6fc206586cc07a18595d2a3d815a806a057d0 (diff)
parent8c4d2a0bfbd27d030e4652b714cd5a1598f3559b (diff)
Merge tag 'asoc-v5.5-2' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-linus
ASoC: More updates for v5.5 Some more development work for v5.5. Highlights include: - More cleanups from Morimoto-san. - Trigger word detection for RT5677. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig3
-rw-r--r--sound/soc/codecs/pcm3168a.c48
-rw-r--r--sound/soc/codecs/rt5645.c6
-rw-r--r--sound/soc/codecs/rt5677-spi.c13
-rw-r--r--sound/soc/codecs/rt5677.c445
-rw-r--r--sound/soc/codecs/rt5677.h11
-rw-r--r--sound/soc/codecs/rt5682.c28
-rw-r--r--sound/soc/codecs/tas2770.c10
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c45
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h8
-rw-r--r--sound/soc/codecs/wm2200.c5
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8904.c73
-rw-r--r--sound/soc/codecs/wm8904.h1
-rw-r--r--sound/soc/codecs/wm_adsp.c81
-rw-r--r--sound/soc/codecs/wm_adsp.h4
16 files changed, 646 insertions, 137 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 925e5f852348..4abf37b5083f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -679,7 +679,8 @@ config SND_SOC_DA7210
tristate
config SND_SOC_DA7213
- tristate
+ tristate "Dialog DA7213 CODEC"
+ depends on I2C
config SND_SOC_DA7218
tristate
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 313500ab36df..9711fab296eb 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -9,7 +9,9 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -59,6 +61,7 @@ struct pcm3168a_priv {
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
struct regmap *regmap;
struct clk *scki;
+ struct gpio_desc *gpio_rst;
unsigned long sysclk;
struct pcm3168a_io_params io_params[2];
@@ -643,6 +646,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case PCM3168A_RST_SMODE:
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
@@ -702,6 +706,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
+ /*
+ * Request the reset (connected to RST pin) gpio line as non exclusive
+ * as the same reset line might be connected to multiple pcm3168a codec
+ *
+ * The RST is low active, we want the GPIO line to be high initially, so
+ * request the initial level to LOW which in practice means DEASSERTED:
+ * The deasserted level of GPIO_ACTIVE_LOW is HIGH.
+ */
+ pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(pcm3168a->gpio_rst)) {
+ ret = PTR_ERR(pcm3168a->gpio_rst);
+ if (ret != -EPROBE_DEFER )
+ dev_err(dev, "failed to acquire RST gpio: %d\n", ret);
+
+ return ret;
+ }
+
pcm3168a->scki = devm_clk_get(dev, "scki");
if (IS_ERR(pcm3168a->scki)) {
ret = PTR_ERR(pcm3168a->scki);
@@ -743,10 +766,18 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
- ret = pcm3168a_reset(pcm3168a);
- if (ret) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
- goto err_regulator;
+ if (pcm3168a->gpio_rst) {
+ /*
+ * The device is taken out from reset via GPIO line, wait for
+ * 3846 SCKI clock cycles for the internal reset de-assertion
+ */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+ } else {
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
}
pm_runtime_set_active(dev);
@@ -785,6 +816,15 @@ static void pcm3168a_disable(struct device *dev)
void pcm3168a_remove(struct device *dev)
{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ /*
+ * The RST is low active, we want the GPIO line to be low when the
+ * driver is removed, so set level to 1 which in practice means
+ * ASSERTED:
+ * The asserted level of GPIO_ACTIVE_LOW is LOW.
+ */
+ gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
pm_runtime_disable(dev);
#ifndef CONFIG_PM
pcm3168a_disable(dev);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index a15e4ecd2a24..92d67010aeed 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3270,6 +3270,9 @@ static void rt5645_jack_detect_work(struct work_struct *work)
snd_soc_jack_report(rt5645->mic_jack,
report, SND_JACK_MICROPHONE);
return;
+ case 4:
+ val = snd_soc_component_read32(rt5645->component, RT5645_A_JD_CTRL1) & 0x0020;
+ break;
default: /* read rt5645 jd1_1 status */
val = snd_soc_component_read32(rt5645->component, RT5645_INT_IRQ_ST) & 0x1000;
break;
@@ -3603,7 +3606,7 @@ static const struct rt5645_platform_data intel_braswell_platform_data = {
static const struct rt5645_platform_data buddy_platform_data = {
.dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
.dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
- .jd_mode = 3,
+ .jd_mode = 4,
.level_trigger_irq = true,
};
@@ -4012,6 +4015,7 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
RT5645_JD1_MODE_1);
break;
case 3:
+ case 4:
regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
RT5645_JD1_MODE_MASK,
RT5645_JD1_MODE_2);
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index 0c90f99b693f..7810b1d7de32 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -26,6 +26,7 @@
#include <sound/soc.h>
+#include "rt5677.h"
#include "rt5677-spi.h"
#define DRV_NAME "rt5677spi"
@@ -111,10 +112,16 @@ static int rt5677_spi_pcm_close(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *codec_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(codec_component);
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
cancel_delayed_work_sync(&rt5677_dsp->copy_work);
+ rt5677->set_dsp_vad(codec_component, false);
return 0;
}
@@ -153,9 +160,15 @@ static int rt5677_spi_prepare(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_component *rt5677_component =
+ snd_soc_rtdcom_lookup(rtd, "rt5677");
+ struct rt5677_priv *rt5677 =
+ snd_soc_component_get_drvdata(rt5677_component);
struct rt5677_dsp *rt5677_dsp =
snd_soc_component_get_drvdata(component);
+ rt5677->set_dsp_vad(rt5677_component, true);
rt5677_dsp->dma_offset = 0;
rt5677_dsp->avail_bytes = 0;
return 0;
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 315a3d39bc09..e9a051a50ab2 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -38,6 +38,10 @@
#define RT5677_DEVICE_ID 0x6327
+/* Register controlling boot vector */
+#define RT5677_DSP_BOOT_VECTOR 0x1801f090
+#define RT5677_MODEL_ADDR 0x5FFC9800
+
#define RT5677_PR_RANGE_BASE (0xff + 1)
#define RT5677_PR_SPACING 0x100
@@ -298,6 +302,7 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_I2C_MASTER_CTRL7:
case RT5677_I2C_MASTER_CTRL8:
case RT5677_HAP_GENE_CTRL2:
+ case RT5677_PWR_ANLG2: /* Modified by DSP firmware */
case RT5677_PWR_DSP_ST:
case RT5677_PRIV_DATA:
case RT5677_ASRC_22:
@@ -308,6 +313,8 @@ static bool rt5677_volatile_register(struct device *dev, unsigned int reg)
case RT5677_IRQ_CTRL1:
case RT5677_IRQ_CTRL2:
case RT5677_GPIO_ST:
+ case RT5677_GPIO_CTRL1: /* Modified by DSP firmware */
+ case RT5677_GPIO_CTRL2: /* Modified by DSP firmware */
case RT5677_DSP_INB1_SRC_CTRL4:
case RT5677_DSP_INB2_SRC_CTRL4:
case RT5677_DSP_INB3_SRC_CTRL4:
@@ -686,10 +693,8 @@ static int rt5677_dsp_mode_i2c_read(
return ret;
}
-static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on)
+static void rt5677_set_dsp_mode(struct rt5677_priv *rt5677, bool on)
{
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
-
if (on) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
RT5677_PWR_DSP, RT5677_PWR_DSP);
@@ -701,86 +706,259 @@ static void rt5677_set_dsp_mode(struct snd_soc_component *component, bool on)
}
}
+static unsigned int rt5677_set_vad_source(struct rt5677_priv *rt5677)
+{
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(rt5677->component);
+ /* Force dapm to sync before we enable the
+ * DSP to prevent write corruption
+ */
+ snd_soc_dapm_sync(dapm);
+
+ /* DMIC1 power = enabled
+ * DMIC CLK = 256 * fs / 12
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_DMIC_CTRL1,
+ RT5677_DMIC_CLK_MASK, 5 << RT5677_DMIC_CLK_SFT);
+
+ /* I2S pre divide 2 = /6 (clk_sys2) */
+ regmap_update_bits(rt5677->regmap, RT5677_CLK_TREE_CTRL1,
+ RT5677_I2S_PD2_MASK, RT5677_I2S_PD2_6);
+
+ /* DSP Clock = MCLK1 (bypassed PLL2) */
+ regmap_write(rt5677->regmap, RT5677_GLB_CLK2,
+ RT5677_DSP_CLK_SRC_BYPASS);
+
+ /* SAD Threshold1 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL2, 0x013f);
+ /* SAD Threshold2 */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL3, 0x0ae5);
+ /* SAD Sample Rate Converter = Up 6 (8K to 48K)
+ * SAD Output Sample Rate = Same as I2S
+ * SAD Threshold3
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_VAD_CTRL4,
+ RT5677_VAD_OUT_SRC_RATE_MASK | RT5677_VAD_OUT_SRC_MASK |
+ RT5677_VAD_LV_DIFF_MASK, 0x7f << RT5677_VAD_LV_DIFF_SFT);
+ /* Minimum frame level within a pre-determined duration = 32 frames
+ * Bypass ADPCM Encoder/Decoder = Bypass ADPCM
+ * Automatic Push Data to SAD Buffer Once SAD Flag is triggered = enable
+ * SAD Buffer Over-Writing = enable
+ * SAD Buffer Pop Mode Control = disable
+ * SAD Buffer Push Mode Control = enable
+ * SAD Detector Control = enable
+ * SAD Function Control = enable
+ * SAD Function Reset = normal
+ */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1,
+ RT5677_VAD_FUNC_RESET | RT5677_VAD_FUNC_ENABLE |
+ RT5677_VAD_DET_ENABLE | RT5677_VAD_BUF_PUSH |
+ RT5677_VAD_BUF_OW | RT5677_VAD_FG2ENC |
+ RT5677_VAD_ADPCM_BYPASS | 1 << RT5677_VAD_MIN_DUR_SFT);
+
+ /* VAD/SAD is not routed to the IRQ output (i.e. MX-BE[14] = 0), but it
+ * is routed to DSP_IRQ_0, so DSP firmware may use it to sleep and save
+ * power. See ALC5677 datasheet section 9.17 "GPIO, Interrupt and Jack
+ * Detection" for more info.
+ */
+
+ /* Private register, no doc */
+ regmap_update_bits(rt5677->regmap, RT5677_PR_BASE + RT5677_BIAS_CUR4,
+ 0x0f00, 0x0100);
+
+ /* LDO2 output = 1.2V
+ * LDO1 output = 1.2V (LDO_IN = 1.8V)
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+ RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
+ 5 << RT5677_LDO1_SEL_SFT | 5 << RT5677_LDO2_SEL_SFT);
+
+ /* Codec core power = power on
+ * LDO1 power = power on
+ */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1,
+ RT5677_PWR_CORE | RT5677_PWR_LDO1);
+
+ /* Isolation for DCVDD4 = normal (set during probe)
+ * Isolation for DCVDD2 = normal (set during probe)
+ * Isolation for DSP = normal
+ * Isolation for Band 0~7 = disable
+ * Isolation for InBound 4~10 and OutBound 4~10 = disable
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP2,
+ RT5677_PWR_CORE_ISO | RT5677_PWR_DSP_ISO |
+ RT5677_PWR_SR7_ISO | RT5677_PWR_SR6_ISO |
+ RT5677_PWR_SR5_ISO | RT5677_PWR_SR4_ISO |
+ RT5677_PWR_SR3_ISO | RT5677_PWR_SR2_ISO |
+ RT5677_PWR_SR1_ISO | RT5677_PWR_SR0_ISO |
+ RT5677_PWR_MLT_ISO);
+
+ /* System Band 0~7 = power on
+ * InBound 4~10 and OutBound 4~10 = power on
+ * DSP = power on
+ * DSP CPU = stop (will be set to "run" after firmware loaded)
+ */
+ regmap_write(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_SR7 | RT5677_PWR_SR6 |
+ RT5677_PWR_SR5 | RT5677_PWR_SR4 |
+ RT5677_PWR_SR3 | RT5677_PWR_SR2 |
+ RT5677_PWR_SR1 | RT5677_PWR_SR0 |
+ RT5677_PWR_MLT | RT5677_PWR_DSP |
+ RT5677_PWR_DSP_CPU);
+
+ return 0;
+}
+
+static int rt5677_parse_and_load_dsp(struct rt5677_priv *rt5677, const u8 *buf,
+ unsigned int len)
+{
+ struct snd_soc_component *component = rt5677->component;
+ Elf32_Ehdr *elf_hdr;
+ Elf32_Phdr *pr_hdr;
+ Elf32_Half i;
+ int ret = 0;
+
+ if (!buf || (len < sizeof(Elf32_Ehdr)))
+ return -ENOMEM;
+
+ elf_hdr = (Elf32_Ehdr *)buf;
+#ifndef EM_XTENSA
+#define EM_XTENSA 94
+#endif
+ if (strncmp(elf_hdr->e_ident, ELFMAG, sizeof(ELFMAG) - 1))
+ dev_err(component->dev, "Wrong ELF header prefix\n");
+ if (elf_hdr->e_ehsize != sizeof(Elf32_Ehdr))
+ dev_err(component->dev, "Wrong Elf header size\n");
+ if (elf_hdr->e_machine != EM_XTENSA)
+ dev_err(component->dev, "Wrong DSP code file\n");
+
+ if (len < elf_hdr->e_phoff)
+ return -ENOMEM;
+ pr_hdr = (Elf32_Phdr *)(buf + elf_hdr->e_phoff);
+ for (i = 0; i < elf_hdr->e_phnum; i++) {
+ /* TODO: handle p_memsz != p_filesz */
+ if (pr_hdr->p_paddr && pr_hdr->p_filesz) {
+ dev_info(component->dev, "Load 0x%x bytes to 0x%x\n",
+ pr_hdr->p_filesz, pr_hdr->p_paddr);
+
+ ret = rt5677_spi_write(pr_hdr->p_paddr,
+ buf + pr_hdr->p_offset,
+ pr_hdr->p_filesz);
+ if (ret)
+ dev_err(component->dev, "Load firmware failed %d\n",
+ ret);
+ }
+ pr_hdr++;
+ }
+ return ret;
+}
+
+static int rt5677_load_dsp_from_file(struct rt5677_priv *rt5677)
+{
+ const struct firmware *fwp;
+ struct device *dev = rt5677->component->dev;
+ int ret = 0;
+
+ /* Load dsp firmware from rt5677_elf_vad file */
+ ret = request_firmware(&fwp, "rt5677_elf_vad", dev);
+ if (ret) {
+ dev_err(dev, "Request rt5677_elf_vad failed %d\n", ret);
+ return ret;
+ }
+ dev_info(dev, "Requested rt5677_elf_vad (%zu)\n", fwp->size);
+
+ ret = rt5677_parse_and_load_dsp(rt5677, fwp->data, fwp->size);
+ release_firmware(fwp);
+ return ret;
+}
+
static int rt5677_set_dsp_vad(struct snd_soc_component *component, bool on)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- static bool activity;
- int ret;
+ rt5677->dsp_vad_en_request = on;
+ rt5677->dsp_vad_en = on;
if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
return -ENXIO;
- if (on && !activity) {
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ return 0;
+}
+
+static void rt5677_dsp_work(struct work_struct *work)
+{
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, dsp_work.work);
+ static bool activity;
+ bool enable = rt5677->dsp_vad_en;
+ int i, val;
+
+
+ dev_info(rt5677->component->dev, "DSP VAD: enable=%d, activity=%d\n",
+ enable, activity);
+
+ if (enable && !activity) {
activity = true;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Before a hotword is detected, GPIO1 pin is configured as IRQ
+ * output so that jack detect works. When a hotword is detected,
+ * the DSP firmware configures the GPIO1 pin as GPIO1 and
+ * drives a 1. rt5677_irq() is called after a rising edge on
+ * the GPIO1 pin, due to either jack detect event or hotword
+ * event, or both. All possible events are checked and handled
+ * in rt5677_irq() where GPIO1 pin is configured back to IRQ
+ * output if a hotword is detected.
+ */
- regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
- regmap_update_bits(rt5677->regmap,
- RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
- RT5677_LDO1_SEL_MASK, 0x0);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
- RT5677_PWR_LDO1, RT5677_PWR_LDO1);
- switch (rt5677->type) {
- case RT5677:
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
- RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_PLL2_PR_SRC_MASK |
- RT5677_DSP_CLK_SRC_MASK,
- RT5677_PLL2_PR_SRC_MCLK2 |
- RT5677_DSP_CLK_SRC_BYPASS);
- break;
- case RT5676:
- regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
- RT5677_DSP_CLK_SRC_MASK,
- RT5677_DSP_CLK_SRC_BYPASS);
- break;
- default:
- break;
+ rt5677_set_vad_source(rt5677);
+ rt5677_set_dsp_mode(rt5677, true);
+
+#define RT5677_BOOT_RETRY 20
+ for (i = 0; i < RT5677_BOOT_RETRY; i++) {
+ regmap_read(rt5677->regmap, RT5677_PWR_DSP_ST, &val);
+ if (val == 0x3ff)
+ break;
+ udelay(500);
}
- regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
- rt5677_set_dsp_mode(component, true);
-
- ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
- component->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x50000000, rt5677->fw1);
- release_firmware(rt5677->fw1);
+ if (i == RT5677_BOOT_RETRY && val != 0x3ff) {
+ dev_err(rt5677->component->dev, "DSP Boot Timed Out!");
+ return;
}
- ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
- component->dev);
- if (ret == 0) {
- rt5677_spi_write_firmware(0x60000000, rt5677->fw2);
- release_firmware(rt5677->fw2);
- }
+ /* Boot the firmware from IRAM instead of SRAM0. */
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0019, 0x0003);
+ rt5677_dsp_mode_i2c_write_addr(rt5677, RT5677_DSP_BOOT_VECTOR,
+ 0x0009, 0x0003);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+ rt5677_load_dsp_from_file(rt5677);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_cache_only(rt5677->regmap, true);
- } else if (!on && activity) {
+ /* Set DSP CPU to Run */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, 0x0);
+ } else if (!enable && activity) {
activity = false;
- regcache_cache_only(rt5677->regmap, false);
- regcache_cache_bypass(rt5677->regmap, true);
+ /* Don't turn off the DSP while handling irqs */
+ mutex_lock(&rt5677->irq_lock);
+ /* Set DSP CPU to Stop */
+ regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1,
+ RT5677_PWR_DSP_CPU, RT5677_PWR_DSP_CPU);
- regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
- rt5677_set_dsp_mode(component, false);
- regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+ rt5677_set_dsp_mode(rt5677, false);
- regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+ /* Disable and clear VAD interrupt */
+ regmap_write(rt5677->regmap, RT5677_VAD_CTRL1, 0x2184);
- regcache_cache_bypass(rt5677->regmap, false);
- regcache_mark_dirty(rt5677->regmap);
- regcache_sync(rt5677->regmap);
- }
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
- return 0;
+ mutex_unlock(&rt5677->irq_lock);
+ }
}
static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
@@ -805,7 +983,7 @@ static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
- ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+ ucontrol->value.integer.value[0] = rt5677->dsp_vad_en_request;
return 0;
}
@@ -814,12 +992,8 @@ static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
-
- rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
- if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
- rt5677_set_dsp_vad(component, rt5677->dsp_vad_en);
+ rt5677_set_dsp_vad(component, !!ucontrol->value.integer.value[0]);
return 0;
}
@@ -3010,6 +3184,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AIF4TX", "AIF4 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("SLBRX", "SLIMBus Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("SLBTX", "SLIMBus Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DSPTX", "DSP Buffer", 0, SND_SOC_NOPM, 0, 0),
/* Sidetone Mux */
SND_SOC_DAPM_MUX("Sidetone Mux", SND_SOC_NOPM, 0, 0,
@@ -3544,11 +3719,24 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
{ "SLBTX", NULL, "SLB ADC3 Mux" },
{ "SLBTX", NULL, "SLB ADC4 Mux" },
+ { "DSPTX", NULL, "IB01 Bypass Mux" },
+
{ "IB01 Mux", "IF1 DAC 01", "IF1 DAC01" },
{ "IB01 Mux", "IF2 DAC 01", "IF2 DAC01" },
{ "IB01 Mux", "SLB DAC 01", "SLB DAC01" },
{ "IB01 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
- { "IB01 Mux", "VAD ADC/DAC1 FS", "DAC1 FS" },
+ /* The IB01 Mux controls the source for InBound0 and InBound1.
+ * When the mux option "VAD ADC/DAC1 FS" is selected, "VAD ADC" goes to
+ * InBound0 and "DAC1 FS" goes to InBound1. "VAD ADC" is used for
+ * hotwording. "DAC1 FS" is not used currently.
+ *
+ * Creating a common widget node for "VAD ADC" + "DAC1 FS" and
+ * connecting the common widget to IB01 Mux causes the issue where
+ * there is an active path going from system playback -> "DAC1 FS" ->
+ * IB01 Mux -> DSP Buffer -> hotword stream. This wrong path confuses
+ * DAPM. Therefore "DAC1 FS" is ignored for now.
+ */
+ { "IB01 Mux", "VAD ADC/DAC1 FS", "VAD ADC Mux" },
{ "IB01 Bypass Mux", "Bypass", "IB01 Mux" },
{ "IB01 Bypass Mux", "Pass SRC", "IB01 Mux" },
@@ -4457,14 +4645,15 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ enum snd_soc_bias_level prev_bias =
+ snd_soc_component_get_bias_level(component);
switch (level) {
case SND_SOC_BIAS_ON:
break;
case SND_SOC_BIAS_PREPARE:
- if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_STANDBY) {
- rt5677_set_dsp_vad(component, false);
+ if (prev_bias == SND_SOC_BIAS_STANDBY) {
regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
@@ -4488,9 +4677,25 @@ static int rt5677_set_bias_level(struct snd_soc_component *component,
break;
case SND_SOC_BIAS_STANDBY:
+ if (prev_bias == SND_SOC_BIAS_OFF &&
+ rt5677->dsp_vad_en_request) {
+ /* Re-enable the DSP if it was turned off at suspend */
+ rt5677->dsp_vad_en = true;
+ /* The delay is to wait for MCLK */
+ schedule_delayed_work(&rt5677->dsp_work,
+ msecs_to_jiffies(1000));
+ }
break;
case SND_SOC_BIAS_OFF:
+ flush_delayed_work(&rt5677->dsp_work);
+ if (rt5677->is_dsp_mode) {
+ /* Turn off the DSP before suspend */
+ rt5677->dsp_vad_en = false;
+ schedule_delayed_work(&rt5677->dsp_work, 0);
+ flush_delayed_work(&rt5677->dsp_work);
+ }
+
regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x0);
regmap_write(rt5677->regmap, RT5677_PWR_DIG1, 0x0000);
regmap_write(rt5677->regmap, RT5677_PWR_ANLG1,
@@ -4740,6 +4945,8 @@ static void rt5677_remove(struct snd_soc_component *component)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ cancel_delayed_work_sync(&rt5677->dsp_work);
+
regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
gpiod_set_value_cansleep(rt5677->pow_ldo2, 0);
gpiod_set_value_cansleep(rt5677->reset_pin, 1);
@@ -4750,6 +4957,11 @@ static int rt5677_suspend(struct snd_soc_component *component)
{
struct rt5677_priv *rt5677 = snd_soc_component_get_drvdata(component);
+ if (rt5677->irq) {
+ cancel_delayed_work_sync(&rt5677->resume_irq_check);
+ disable_irq(rt5677->irq);
+ }
+
if (!rt5677->dsp_vad_en) {
regcache_cache_only(rt5677->regmap, true);
regcache_mark_dirty(rt5677->regmap);
@@ -4778,6 +4990,11 @@ static int rt5677_resume(struct snd_soc_component *component)
regcache_sync(rt5677->regmap);
}
+ if (rt5677->irq) {
+ enable_irq(rt5677->irq);
+ schedule_delayed_work(&rt5677->resume_irq_check, 0);
+ }
+
return 0;
}
#else
@@ -4842,6 +5059,11 @@ static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.set_tdm_slot = rt5677_set_tdm_slot,
};
+static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = {
+ .set_sysclk = rt5677_set_dai_sysclk,
+ .set_pll = rt5677_set_dai_pll,
+};
+
static struct snd_soc_dai_driver rt5677_dai[] = {
{
.name = "rt5677-aif1",
@@ -4938,6 +5160,18 @@ static struct snd_soc_dai_driver rt5677_dai[] = {
},
.ops = &rt5677_aif_dai_ops,
},
+ {
+ .name = "rt5677-dspbuffer",
+ .id = RT5677_DSPBUFF,
+ .capture = {
+ .stream_name = "DSP Buffer",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &rt5677_dsp_dai_ops,
+ },
};
static const struct snd_soc_component_driver soc_component_dev_rt5677 = {
@@ -5073,6 +5307,28 @@ static const struct rt5677_irq_desc rt5677_irq_descs[] = {
},
};
+static bool rt5677_check_hotword(struct rt5677_priv *rt5677)
+{
+ int reg_gpio;
+
+ if (!rt5677->is_dsp_mode)
+ return false;
+
+ if (regmap_read(rt5677->regmap, RT5677_GPIO_CTRL1, &reg_gpio))
+ return false;
+
+ /* Firmware sets GPIO1 pin to be GPIO1 after hotword is detected */
+ if ((reg_gpio & RT5677_GPIO1_PIN_MASK) == RT5677_GPIO1_PIN_IRQ)
+ return false;
+
+ /* Set GPIO1 pin back to be IRQ output for jack detect */
+ regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1,
+ RT5677_GPIO1_PIN_MASK, RT5677_GPIO1_PIN_IRQ);
+
+ rt5677_spi_hotword_detected();
+ return true;
+}
+
static irqreturn_t rt5677_irq(int unused, void *data)
{
struct rt5677_priv *rt5677 = data;
@@ -5118,7 +5374,13 @@ static irqreturn_t rt5677_irq(int unused, void *data)
reg_irq ^= rt5677_irq_descs[i].polarity_mask;
}
}
- if (!irq_fired)
+
+ /* Exit the loop only when we know for sure that GPIO1 pin
+ * was low at some point since irq_lock was acquired. Any event
+ * after that point creates a rising edge that triggers another
+ * call to rt5677_irq().
+ */
+ if (!irq_fired && !rt5677_check_hotword(rt5677))
goto exit;
ret = regmap_write(rt5677->regmap, RT5677_IRQ_CTRL1, reg_irq);
@@ -5129,6 +5391,7 @@ static irqreturn_t rt5677_irq(int unused, void *data)
}
}
exit:
+ WARN_ON_ONCE(loop == 20);
mutex_unlock(&rt5677->irq_lock);
if (irq_fired)
return IRQ_HANDLED;
@@ -5136,6 +5399,39 @@ exit:
return IRQ_NONE;
}
+static void rt5677_resume_irq_check(struct work_struct *work)
+{
+ int i, virq;
+ struct rt5677_priv *rt5677 =
+ container_of(work, struct rt5677_priv, resume_irq_check.work);
+
+ /* This is needed to check and clear the interrupt status register
+ * at resume. If the headset is plugged/unplugged when the device is
+ * fully suspended, there won't be a rising edge at resume to trigger
+ * the interrupt. Without this, we miss the next unplug/plug event.
+ */
+ rt5677_irq(0, rt5677);
+
+ /* Call all enabled jack detect irq handlers again. This is needed in
+ * addition to the above check for a corner case caused by jack gpio
+ * debounce. After codec irq is disabled at suspend, the delayed work
+ * scheduled by soc-jack may run and read wrong jack gpio values, since
+ * the regmap is in cache only mode. At resume, there is no irq because
+ * rt5677_irq has already ran and cleared the irq status at suspend.
+ * Without this explicit check, unplug the headset right after suspend
+ * starts, then after resume the headset is still shown as plugged in.
+ */
+ mutex_lock(&rt5677->irq_lock);
+ for (i = 0; i < RT5677_IRQ_NUM; i++) {
+ if (rt5677->irq_en & rt5677_irq_descs[i].enable_mask) {
+ virq = irq_find_mapping(rt5677->domain, i);
+ if (virq)
+ handle_nested_irq(virq);
+ }
+ }
+ mutex_unlock(&rt5677->irq_lock);
+}
+
static void rt5677_irq_bus_lock(struct irq_data *data)
{
struct rt5677_priv *rt5677 = irq_data_get_irq_chip_data(data);
@@ -5211,6 +5507,7 @@ static int rt5677_init_irq(struct i2c_client *i2c)
}
mutex_init(&rt5677->irq_lock);
+ INIT_DELAYED_WORK(&rt5677->resume_irq_check, rt5677_resume_irq_check);
/*
* Select RC as the debounce clock so that GPIO works even when
@@ -5256,6 +5553,8 @@ static int rt5677_init_irq(struct i2c_client *i2c)
if (ret)
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
+ rt5677->irq = i2c->irq;
+
return ret;
}
@@ -5271,6 +5570,8 @@ static int rt5677_i2c_probe(struct i2c_client *i2c)
return -ENOMEM;
rt5677->dev = &i2c->dev;
+ rt5677->set_dsp_vad = rt5677_set_dsp_vad;
+ INIT_DELAYED_WORK(&rt5677->dsp_work, rt5677_dsp_work);
i2c_set_clientdata(i2c, rt5677);
if (i2c->dev.of_node) {
diff --git a/sound/soc/codecs/rt5677.h b/sound/soc/codecs/rt5677.h
index 213f4b8ca269..944ae02aafc2 100644
--- a/sound/soc/codecs/rt5677.h
+++ b/sound/soc/codecs/rt5677.h
@@ -1336,6 +1336,8 @@
#define RT5677_PLL_M_SFT 12
#define RT5677_PLL_M_BP (0x1 << 11)
#define RT5677_PLL_M_BP_SFT 11
+#define RT5677_PLL_UPDATE_PLL1 (0x1 << 1)
+#define RT5677_PLL_UPDATE_PLL1_SFT 1
/* Global Clock Control 1 (0x80) */
#define RT5677_SCLK_SRC_MASK (0x3 << 14)
@@ -1730,6 +1732,7 @@ enum {
RT5677_AIF4,
RT5677_AIF5,
RT5677_AIFS,
+ RT5677_DSPBUFF,
};
enum {
@@ -1845,14 +1848,20 @@ struct rt5677_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
- bool dsp_vad_en;
+ bool dsp_vad_en_request; /* DSP VAD enable/disable request */
+ bool dsp_vad_en; /* dsp_work parameter */
bool is_dsp_mode;
bool is_vref_slow;
+ struct delayed_work dsp_work;
/* Interrupt handling */
struct irq_domain *domain;
struct mutex irq_lock;
unsigned int irq_en;
+ struct delayed_work resume_irq_check;
+ int irq;
+
+ int (*set_dsp_vad)(struct snd_soc_component *component, bool on);
};
int rt5677_sel_asrc_clk_src(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 35dcec135c8a..b1713fffa3eb 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -1003,6 +1003,7 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
regmap_update_bits(rt5682->regmap, RT5682_RC_CLK_CTRL,
RT5682_POW_JDH | RT5682_POW_JDL, 0);
+ cancel_delayed_work_sync(&rt5682->jack_detect_work);
return 0;
}
@@ -1463,28 +1464,6 @@ static const struct snd_kcontrol_new hpor_switch =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1,
RT5682_R_MUTE_SFT, 1, 1);
-static int rt5682_charge_pump_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
-{
- struct snd_soc_component *component =
- snd_soc_dapm_to_component(w->dapm);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- snd_soc_component_update_bits(component,
- RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
- break;
- case SND_SOC_DAPM_POST_PMD:
- snd_soc_component_update_bits(component,
- RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_LV);
- break;
- default:
- return 0;
- }
-
- return 0;
-}
-
static int rt5682_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -1768,8 +1747,7 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1,
RT5682_PWR_HA_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1,
- RT5682_PUMP_EN_SFT, 0, rt5682_charge_pump_event,
- SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ RT5682_PUMP_EN_SFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1,
RT5682_CAPLESS_EN_SFT, 0, NULL, 0),
@@ -2669,6 +2647,8 @@ static int rt5682_i2c_probe(struct i2c_client *i2c,
RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA);
regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1,
RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ);
+ regmap_update_bits(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1,
+ RT5682_PM_HP_MASK, RT5682_PM_HP_HV);
INIT_DELAYED_WORK(&rt5682->jack_detect_work,
rt5682_jack_detect_handler);
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index ad76f22fcfac..54c8135fe43c 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -761,12 +761,12 @@ static int tas2770_i2c_probe(struct i2c_client *client,
tas2770->reset_gpio = devm_gpiod_get_optional(tas2770->dev,
"reset-gpio",
GPIOD_OUT_HIGH);
- if (IS_ERR(tas2770->reset_gpio)) {
- if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) {
- tas2770->reset_gpio = NULL;
- return -EPROBE_DEFER;
- }
+ if (IS_ERR(tas2770->reset_gpio)) {
+ if (PTR_ERR(tas2770->reset_gpio) == -EPROBE_DEFER) {
+ tas2770->reset_gpio = NULL;
+ return -EPROBE_DEFER;
}
+ }
tas2770->channel_size = 0;
tas2770->slot_width = 0;
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index df627a08def9..f6f19fdc72f5 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -171,6 +171,7 @@ struct aic31xx_priv {
int rate_div_line;
bool master_dapm_route_applied;
int irq;
+ u8 ocmv; /* output common-mode voltage */
};
struct aic31xx_rate_divs {
@@ -1312,6 +1313,11 @@ static int aic31xx_codec_probe(struct snd_soc_component *component)
if (ret)
return ret;
+ /* set output common-mode voltage */
+ snd_soc_component_update_bits(component, AIC31XX_HPDRIVER,
+ AIC31XX_HPD_OCMV_MASK,
+ aic31xx->ocmv << AIC31XX_HPD_OCMV_SHIFT);
+
return 0;
}
@@ -1501,6 +1507,43 @@ exit:
return IRQ_NONE;
}
+static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
+{
+ struct device *dev = priv->dev;
+ int dvdd, avdd;
+ u32 value;
+
+ if (dev->fwnode &&
+ fwnode_property_read_u32(dev->fwnode, "ai31xx-ocmv", &value)) {
+ /* OCMV setting is forced by DT */
+ if (value <= 3) {
+ priv->ocmv = value;
+ return;
+ }
+ }
+
+ avdd = regulator_get_voltage(priv->supplies[3].consumer);
+ dvdd = regulator_get_voltage(priv->supplies[5].consumer);
+
+ if (avdd > 3600000 || dvdd > 1950000) {
+ dev_warn(dev,
+ "Too high supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ } else if (avdd == 3600000 && dvdd == 1950000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_8V;
+ } else if (avdd >= 3300000 && dvdd >= 1800000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_65V;
+ } else if (avdd >= 3000000 && dvdd >= 1650000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_5V;
+ } else if (avdd >= 2700000 && dvdd >= 1525000) {
+ priv->ocmv = AIC31XX_HPD_OCMV_1_35V;
+ } else {
+ dev_warn(dev,
+ "Invalid supply voltage(s) AVDD: %d, DVDD: %d\n",
+ avdd, dvdd);
+ }
+}
+
static int aic31xx_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -1570,6 +1613,8 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ aic31xx_configure_ocmv(aic31xx);
+
if (aic31xx->irq > 0) {
regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1,
AIC31XX_GPIO1_FUNC_MASK,
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index cb024955c978..83a8c7604cc3 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -232,6 +232,14 @@ struct aic31xx_pdata {
#define AIC31XX_HSD_HP 0x01
#define AIC31XX_HSD_HS 0x03
+/* AIC31XX_HPDRIVER */
+#define AIC31XX_HPD_OCMV_MASK GENMASK(4, 3)
+#define AIC31XX_HPD_OCMV_SHIFT 3
+#define AIC31XX_HPD_OCMV_1_35V 0x0
+#define AIC31XX_HPD_OCMV_1_5V 0x1
+#define AIC31XX_HPD_OCMV_1_65V 0x2
+#define AIC31XX_HPD_OCMV_1_8V 0x3
+
/* AIC31XX_MICBIAS */
#define AIC31XX_MICBIAS_MASK GENMASK(1, 0)
#define AIC31XX_MICBIAS_SHIFT 0
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index cf64e109c658..7b087d94141b 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2410,6 +2410,8 @@ static int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
+ if (i2c->irq)
+ free_irq(i2c->irq, wm2200);
err_reset:
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
@@ -2426,12 +2428,15 @@ static int wm2200_i2c_remove(struct i2c_client *i2c)
{
struct wm2200_priv *wm2200 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
+ regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
return 0;
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 4af0e519e623..91cc63c5a51f 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2617,6 +2617,7 @@ static int wm5100_i2c_probe(struct i2c_client *i2c,
return ret;
err_reset:
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
@@ -2640,6 +2641,7 @@ static int wm5100_i2c_remove(struct i2c_client *i2c)
{
struct wm5100_priv *wm5100 = i2c_get_clientdata(i2c);
+ pm_runtime_disable(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm5100);
wm5100_free_gpio(i2c);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index bcb3c9d5abf0..7d7ea15d73e0 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -1410,34 +1410,6 @@ static int wm8904_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-
-static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
- unsigned int freq, int dir)
-{
- struct snd_soc_component *component = dai->component;
- struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
-
- switch (clk_id) {
- case WM8904_CLK_MCLK:
- priv->sysclk_src = clk_id;
- priv->mclk_rate = freq;
- break;
-
- case WM8904_CLK_FLL:
- priv->sysclk_src = clk_id;
- break;
-
- default:
- return -EINVAL;
- }
-
- dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
-
- wm8904_configure_clocking(component);
-
- return 0;
-}
-
static int wm8904_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_component *component = dai->component;
@@ -1824,6 +1796,50 @@ out:
return 0;
}
+static int wm8904_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct wm8904_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned long mclk_freq;
+ int ret;
+
+ switch (clk_id) {
+ case WM8904_CLK_AUTO:
+ mclk_freq = clk_get_rate(priv->mclk);
+ /* enable FLL if a different sysclk is desired */
+ if (mclk_freq != freq) {
+ priv->sysclk_src = WM8904_CLK_FLL;
+ ret = wm8904_set_fll(dai, WM8904_FLL_MCLK,
+ WM8904_FLL_MCLK,
+ mclk_freq, freq);
+ if (ret)
+ return ret;
+ break;
+ }
+ clk_id = WM8904_CLK_MCLK;
+ /* fallthrough */
+
+ case WM8904_CLK_MCLK:
+ priv->sysclk_src = clk_id;
+ priv->mclk_rate = freq;
+ break;
+
+ case WM8904_CLK_FLL:
+ priv->sysclk_src = clk_id;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(dai->dev, "Clock source is %d at %uHz\n", clk_id, freq);
+
+ wm8904_configure_clocking(component);
+
+ return 0;
+}
+
static int wm8904_digital_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_component *component = codec_dai->component;
@@ -1917,6 +1933,7 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
snd_soc_component_update_bits(component, WM8904_BIAS_CONTROL_0,
WM8904_BIAS_ENA, 0);
+ snd_soc_component_write(component, WM8904_SW_RESET_AND_ID, 0);
regcache_cache_only(wm8904->regmap, true);
regcache_mark_dirty(wm8904->regmap);
diff --git a/sound/soc/codecs/wm8904.h b/sound/soc/codecs/wm8904.h
index c1bca52f9927..de6340446b1f 100644
--- a/sound/soc/codecs/wm8904.h
+++ b/sound/soc/codecs/wm8904.h
@@ -10,6 +10,7 @@
#ifndef _WM8904_H
#define _WM8904_H
+#define WM8904_CLK_AUTO 0
#define WM8904_CLK_MCLK 1
#define WM8904_CLK_FLL 2
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 9b8bb7bbe945..2a9b610f6d43 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -599,6 +599,9 @@ struct wm_coeff_ctl_ops {
struct wm_coeff_ctl {
const char *name;
const char *fw_name;
+ /* Subname is needed to match with firmware */
+ const char *subname;
+ unsigned int subname_len;
struct wm_adsp_alg_region alg_region;
struct wm_coeff_ctl_ops ops;
struct wm_adsp *dsp;
@@ -1399,6 +1402,7 @@ static void wm_adsp_free_ctl_blk(struct wm_coeff_ctl *ctl)
{
kfree(ctl->cache);
kfree(ctl->name);
+ kfree(ctl->subname);
kfree(ctl);
}
@@ -1472,6 +1476,15 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ret = -ENOMEM;
goto err_ctl;
}
+ if (subname) {
+ ctl->subname_len = subname_len;
+ ctl->subname = kmemdup(subname,
+ strlen(subname) + 1, GFP_KERNEL);
+ if (!ctl->subname) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+ }
ctl->enabled = 1;
ctl->set = 0;
ctl->ops.xget = wm_coeff_get;
@@ -1485,7 +1498,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
if (!ctl->cache) {
ret = -ENOMEM;
- goto err_ctl_name;
+ goto err_ctl_subname;
}
list_add(&ctl->list, &dsp->ctl_list);
@@ -1508,6 +1521,8 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
err_ctl_cache:
kfree(ctl->cache);
+err_ctl_subname:
+ kfree(ctl->subname);
err_ctl_name:
kfree(ctl->name);
err_ctl:
@@ -1995,6 +2010,70 @@ out:
return ret;
}
+/*
+ * Find wm_coeff_ctl with input name as its subname
+ * If not found, return NULL
+ */
+static struct wm_coeff_ctl *wm_adsp_get_ctl(struct wm_adsp *dsp,
+ const char *name, int type,
+ unsigned int alg)
+{
+ struct wm_coeff_ctl *pos, *rslt = NULL;
+
+ list_for_each_entry(pos, &dsp->ctl_list, list) {
+ if (!pos->subname)
+ continue;
+ if (strncmp(pos->subname, name, pos->subname_len) == 0 &&
+ pos->alg_region.alg == alg &&
+ pos->alg_region.type == type) {
+ rslt = pos;
+ break;
+ }
+ }
+
+ return rslt;
+}
+
+int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl;
+ struct snd_kcontrol *kcontrol;
+ int ret;
+
+ ctl = wm_adsp_get_ctl(dsp, name, type, alg);
+ if (!ctl)
+ return -EINVAL;
+
+ if (len > ctl->len)
+ return -EINVAL;
+
+ ret = wm_coeff_write_control(ctl, buf, len);
+
+ kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl->name);
+ snd_ctl_notify(dsp->component->card->snd_card,
+ SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_write_ctl);
+
+int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len)
+{
+ struct wm_coeff_ctl *ctl;
+
+ ctl = wm_adsp_get_ctl(dsp, name, type, alg);
+ if (!ctl)
+ return -EINVAL;
+
+ if (len > ctl->len)
+ return -EINVAL;
+
+ return wm_coeff_read_control(ctl, buf, len);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_read_ctl);
+
static void wm_adsp_ctl_fixup_base(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region)
{
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index aa634ef6c9f5..4c481cf20275 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -201,5 +201,9 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp);
int wm_adsp_compr_copy(struct snd_compr_stream *stream,
char __user *buf, size_t count);
+int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
+int wm_adsp_read_ctl(struct wm_adsp *dsp, const char *name, int type,
+ unsigned int alg, void *buf, size_t len);
#endif