summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/atmel/Kconfig1
-rw-r--r--sound/soc/atmel/atmel-classd.c5
-rw-r--r--sound/soc/atmel/atmel-pdmic.c5
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c2
-rw-r--r--sound/soc/codecs/Kconfig23
-rw-r--r--sound/soc/codecs/Makefile6
-rw-r--r--sound/soc/codecs/adau-utils.c61
-rw-r--r--sound/soc/codecs/adau-utils.h7
-rw-r--r--sound/soc/codecs/adau1373.c38
-rw-r--r--sound/soc/codecs/adau1761-i2c.c2
-rw-r--r--sound/soc/codecs/adau1761-spi.c2
-rw-r--r--sound/soc/codecs/adau1781-i2c.c2
-rw-r--r--sound/soc/codecs/adau1781-spi.c2
-rw-r--r--sound/soc/codecs/adau17x1.c251
-rw-r--r--sound/soc/codecs/adau17x1.h6
-rw-r--r--sound/soc/codecs/adau7002.c80
-rw-r--r--sound/soc/codecs/ak4613.c14
-rw-r--r--sound/soc/codecs/ak4642.c13
-rw-r--r--sound/soc/codecs/arizona.c91
-rw-r--r--sound/soc/codecs/arizona.h22
-rw-r--r--sound/soc/codecs/bt-sco.c52
-rw-r--r--sound/soc/codecs/cs35l33.c1303
-rw-r--r--sound/soc/codecs/cs35l33.h221
-rw-r--r--sound/soc/codecs/cs47l24.c19
-rw-r--r--sound/soc/codecs/cx20442.c1
-rw-r--r--sound/soc/codecs/hdac_hdmi.c27
-rw-r--r--sound/soc/codecs/pcm1681.c2
-rw-r--r--sound/soc/codecs/pcm179x.c2
-rw-r--r--sound/soc/codecs/pcm5102a.c1
-rw-r--r--sound/soc/codecs/rt286.c7
-rw-r--r--sound/soc/codecs/rt5645.c26
-rw-r--r--sound/soc/codecs/rt5645.h3
-rw-r--r--sound/soc/codecs/rt5670.c2
-rw-r--r--sound/soc/codecs/wm5102.c3
-rw-r--r--sound/soc/codecs/wm5110.c20
-rw-r--r--sound/soc/codecs/wm8940.c1
-rw-r--r--sound/soc/codecs/wm8998.c1
-rw-r--r--sound/soc/codecs/wm_adsp.c34
-rw-r--r--sound/soc/codecs/wm_adsp.h4
-rw-r--r--sound/soc/davinci/davinci-mcasp.c56
-rw-r--r--sound/soc/davinci/davinci-mcasp.h4
-rw-r--r--sound/soc/fsl/fsl_ssi.c12
-rw-r--r--sound/soc/intel/Kconfig73
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c9
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c44
-rw-r--r--sound/soc/intel/boards/Makefile2
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c460
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c118
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c20
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c51
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c51
-rw-r--r--sound/soc/intel/boards/skl_rt286.c9
-rw-r--r--sound/soc/intel/common/Makefile4
-rw-r--r--sound/soc/intel/common/sst-acpi.h4
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h4
-rw-r--r--sound/soc/intel/common/sst-dsp.c69
-rw-r--r--sound/soc/intel/common/sst-dsp.h2
-rw-r--r--sound/soc/intel/common/sst-firmware.c68
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c1
-rw-r--r--sound/soc/intel/skylake/Makefile2
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c203
-rw-r--r--sound/soc/intel/skylake/skl-messages.c53
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c40
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.h22
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c93
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c260
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h102
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c4
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h18
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c256
-rw-r--r--sound/soc/intel/skylake/skl-sst.c124
-rw-r--r--sound/soc/intel/skylake/skl-topology.c230
-rw-r--r--sound/soc/intel/skylake/skl-topology.h7
-rw-r--r--sound/soc/intel/skylake/skl.c39
-rw-r--r--sound/soc/intel/skylake/skl.h10
-rw-r--r--sound/soc/sh/rcar/adg.c2
-rw-r--r--sound/soc/soc-dapm.c1
77 files changed, 4194 insertions, 695 deletions
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 06e099e802df..22aec9a1e9a4 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -10,6 +10,7 @@ if SND_ATMEL_SOC
config SND_ATMEL_SOC_PDC
tristate
+ depends on HAS_DMA
default m if SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=m
default y if SND_ATMEL_SOC_SSC_PDC=y || (SND_ATMEL_SOC_SSC_PDC=m && SND_ATMEL_SOC_SSC=y)
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 6107de9c538b..6d9b8b44e2da 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -593,11 +593,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no memory resource\n");
- return -ENXIO;
- }
-
io_base = devm_ioremap_resource(dev, res);
if (IS_ERR(io_base)) {
ret = PTR_ERR(io_base);
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index aee4787a0b89..5f56da60c92f 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -624,11 +624,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "no memory resource\n");
- return -ENXIO;
- }
-
io_base = devm_ioremap_resource(dev, res);
if (IS_ERR(io_base)) {
ret = PTR_ERR(io_base);
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 1267e1af0fae..54c09acd3fed 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -321,7 +321,7 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
return ret;
}
- dma_params = &ssc_dma_params[dai->id][dir];
+ dma_params = &ssc_dma_params[pdev->id][dir];
dma_params->ssc = ssc_p->ssc;
dma_params->substream = substream;
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4d82a58ff6b0..95c0e20f27c2 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -32,6 +32,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ADAU1977_SPI if SPI_MASTER
select SND_SOC_ADAU1977_I2C if I2C
select SND_SOC_ADAU1701 if I2C
+ select SND_SOC_ADAU7002
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@@ -46,6 +47,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_BT_SCO
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS35L32 if I2C
+ select SND_SOC_CS35L33 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
select SND_SOC_CS42L56 if I2C && INPUT
@@ -269,8 +271,12 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
+config SND_SOC_ADAU_UTILS
+ tristate
+
config SND_SOC_ADAU1373
tristate
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
@@ -280,6 +286,7 @@ config SND_SOC_ADAU1701
config SND_SOC_ADAU17X1
tristate
select SND_SOC_SIGMADSP_REGMAP
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1761
tristate
@@ -322,6 +329,9 @@ config SND_SOC_ADAU1977_I2C
select SND_SOC_ADAU1977
select REGMAP_I2C
+config SND_SOC_ADAU7002
+ tristate "Analog Devices ADAU7002 Stereo PDM-to-I2S/TDM Converter"
+
config SND_SOC_ADAV80X
tristate
@@ -371,7 +381,7 @@ config SND_SOC_ALC5632
tristate
config SND_SOC_BT_SCO
- tristate
+ tristate "Dummy BT SCO codec driver"
config SND_SOC_CQ0093VC
tristate
@@ -380,6 +390,10 @@ config SND_SOC_CS35L32
tristate "Cirrus Logic CS35L32 CODEC"
depends on I2C
+config SND_SOC_CS35L33
+ tristate "Cirrus Logic CS35L33 CODEC"
+ depends on I2C
+
config SND_SOC_CS42L51
tristate
@@ -483,9 +497,10 @@ config SND_SOC_DMIC
tristate
config SND_SOC_HDMI_CODEC
- tristate
- select SND_PCM_ELD
- select SND_PCM_IEC958
+ tristate
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ select HDMI
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0f548fd34ca3..0cd6ea5d49cd 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -7,6 +7,7 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-adau-utils-objs := adau-utils.o
snd-soc-adau1373-objs := adau1373.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau17x1-objs := adau17x1.o
@@ -19,6 +20,7 @@ snd-soc-adau1781-spi-objs := adau1781-spi.o
snd-soc-adau1977-objs := adau1977.o
snd-soc-adau1977-spi-objs := adau1977-spi.o
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
+snd-soc-adau7002-objs := adau7002.o
snd-soc-adav80x-objs := adav80x.o
snd-soc-adav801-objs := adav801.o
snd-soc-adav803-objs := adav803.o
@@ -35,6 +37,7 @@ snd-soc-arizona-objs := arizona.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
+snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
@@ -220,6 +223,7 @@ obj-$(CONFIG_SND_SOC_AD193X_SPI) += snd-soc-ad193x-spi.o
obj-$(CONFIG_SND_SOC_AD193X_I2C) += snd-soc-ad193x-i2c.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
+obj-$(CONFIG_SND_SOC_ADAU_UTILS) += snd-soc-adau-utils.o
obj-$(CONFIG_SND_SOC_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
@@ -232,6 +236,7 @@ obj-$(CONFIG_SND_SOC_ADAU1781_SPI) += snd-soc-adau1781-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977) += snd-soc-adau1977.o
obj-$(CONFIG_SND_SOC_ADAU1977_SPI) += snd-soc-adau1977-spi.o
obj-$(CONFIG_SND_SOC_ADAU1977_I2C) += snd-soc-adau1977-i2c.o
+obj-$(CONFIG_SND_SOC_ADAU7002) += snd-soc-adau7002.o
obj-$(CONFIG_SND_SOC_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
@@ -250,6 +255,7 @@ obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
+obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644
index 000000000000..19d6a6f41b12
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.c
@@ -0,0 +1,61 @@
+/*
+ * Shared helper functions for devices from the ADAU family
+ *
+ * Copyright 2011-2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "adau-utils.h"
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+ uint8_t regs[5])
+{
+ unsigned int r, n, m, i, j;
+ unsigned int div;
+
+ if (!freq_out) {
+ r = 0;
+ n = 0;
+ m = 0;
+ div = 0;
+ } else {
+ if (freq_out % freq_in != 0) {
+ div = DIV_ROUND_UP(freq_in, 13500000);
+ freq_in /= div;
+ r = freq_out / freq_in;
+ i = freq_out % freq_in;
+ j = gcd(i, freq_in);
+ n = i / j;
+ m = freq_in / j;
+ div--;
+ } else {
+ r = freq_out / freq_in;
+ n = 0;
+ m = 0;
+ div = 0;
+ }
+ if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
+ return -EINVAL;
+ }
+
+ regs[0] = m >> 8;
+ regs[1] = m & 0xff;
+ regs[2] = n >> 8;
+ regs[3] = n & 0xff;
+ regs[4] = (r << 3) | (div << 1);
+ if (m != 0)
+ regs[4] |= 1; /* Fractional mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adau_calc_pll_cfg);
+
+MODULE_DESCRIPTION("ASoC ADAU audio CODECs shared helper functions");
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/adau-utils.h b/sound/soc/codecs/adau-utils.h
new file mode 100644
index 000000000000..939b5f37762f
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.h
@@ -0,0 +1,7 @@
+#ifndef SOUND_SOC_CODECS_ADAU_PLL_H
+#define SOUND_SOC_CODECS_ADAU_PLL_H
+
+int adau_calc_pll_cfg(unsigned int freq_in, unsigned int freq_out,
+ uint8_t regs[5]);
+
+#endif
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index fe1353a797b9..1556b360fa15 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -23,6 +23,7 @@
#include <sound/adau1373.h>
#include "adau1373.h"
+#include "adau-utils.h"
struct adau1373_dai {
unsigned int clk_src;
@@ -1254,7 +1255,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
{
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int dpll_div = 0;
- unsigned int x, r, n, m, i, j, mode;
+ uint8_t pll_regs[5];
+ int ret;
switch (pll_id) {
case ADAU1373_PLL1:
@@ -1295,27 +1297,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
dpll_div++;
}
- if (freq_out % freq_in != 0) {
- /* fout = fin * (r + (n/m)) / x */
- x = DIV_ROUND_UP(freq_in, 13500000);
- freq_in /= x;
- r = freq_out / freq_in;
- i = freq_out % freq_in;
- j = gcd(i, freq_in);
- n = i / j;
- m = freq_in / j;
- x--;
- mode = 1;
- } else {
- /* fout = fin / r */
- r = freq_out / freq_in;
- n = 0;
- m = 0;
- x = 0;
- mode = 0;
- }
-
- if (r < 2 || r > 8 || x > 3 || m > 0xffff || n > 0xffff)
+ ret = adau_calc_pll_cfg(freq_in, freq_out, pll_regs);
+ if (ret)
return -EINVAL;
if (dpll_div) {
@@ -1330,12 +1313,11 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
regmap_write(adau1373->regmap, ADAU1373_DPLL_CTRL(pll_id),
(source << 4) | dpll_div);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), (m >> 8) & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), m & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), (n >> 8) & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), n & 0xff);
- regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id),
- (r << 3) | (x << 1) | mode);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL1(pll_id), pll_regs[0]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL2(pll_id), pll_regs[1]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL3(pll_id), pll_regs[2]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL4(pll_id), pll_regs[3]);
+ regmap_write(adau1373->regmap, ADAU1373_PLL_CTRL5(pll_id), pll_regs[4]);
/* Set sysclk to pll_rate / 4 */
regmap_update_bits(adau1373->regmap, ADAU1373_CLK_SRC_DIV(pll_id), 0x3f, 0x09);
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 8de010f758cd..9e7f257f17f8 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -31,7 +31,7 @@ static int adau1761_i2c_probe(struct i2c_client *client,
static int adau1761_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ adau17x1_remove(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index d9171245bd9f..a0b214be759a 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -48,7 +48,7 @@ static int adau1761_spi_probe(struct spi_device *spi)
static int adau1761_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
+ adau17x1_remove(&spi->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 06cbca84cf02..7b9d1802d159 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -31,7 +31,7 @@ static int adau1781_i2c_probe(struct i2c_client *client,
static int adau1781_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ adau17x1_remove(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 3d965a01b99c..9b233544d2e8 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -48,7 +48,7 @@ static int adau1781_spi_probe(struct spi_device *spi)
static int adau1781_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
+ adau17x1_remove(&spi->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fcf05b254ecd..439aa3ff1f99 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -23,6 +24,7 @@
#include "sigmadsp.h"
#include "adau17x1.h"
+#include "adau-utils.h"
static const char * const adau17x1_capture_mixer_boost_text[] = {
"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
@@ -302,6 +304,116 @@ bool adau17x1_has_dsp(struct adau *adau)
}
EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
+ int source, unsigned int freq_in, unsigned int freq_out)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ if (freq_in < 8000000 || freq_in > 27000000)
+ return -EINVAL;
+
+ ret = adau_calc_pll_cfg(freq_in, freq_out, adau->pll_regs);
+ if (ret < 0)
+ return ret;
+
+ /* The PLL register is 6 bytes long and can only be written at once. */
+ ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
+ adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
+ if (ret)
+ return ret;
+
+ adau->pll_freq = freq_out;
+
+ return 0;
+}
+
+static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
+ struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ bool is_pll;
+ bool was_pll;
+
+ switch (clk_id) {
+ case ADAU17X1_CLK_SRC_MCLK:
+ is_pll = false;
+ break;
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ if (!adau->mclk)
+ return -EINVAL;
+ /* Fall-through */
+ case ADAU17X1_CLK_SRC_PLL:
+ is_pll = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (adau->clk_src) {
+ case ADAU17X1_CLK_SRC_MCLK:
+ was_pll = false;
+ break;
+ case ADAU17X1_CLK_SRC_PLL:
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ was_pll = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adau->sysclk = freq;
+
+ if (is_pll != was_pll) {
+ if (is_pll) {
+ snd_soc_dapm_add_routes(dapm,
+ &adau17x1_dapm_pll_route, 1);
+ } else {
+ snd_soc_dapm_del_routes(dapm,
+ &adau17x1_dapm_pll_route, 1);
+ }
+ }
+
+ adau->clk_src = clk_id;
+
+ return 0;
+}
+
+static int adau17x1_auto_pll(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct adau *adau = snd_soc_dai_get_drvdata(dai);
+ unsigned int pll_rate;
+
+ switch (params_rate(params)) {
+ case 48000:
+ case 8000:
+ case 12000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 96000:
+ pll_rate = 48000 * 1024;
+ break;
+ case 44100:
+ case 7350:
+ case 11025:
+ case 14700:
+ case 22050:
+ case 29400:
+ case 88200:
+ pll_rate = 44100 * 1024;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adau17x1_set_dai_pll(dai, ADAU17X1_PLL, ADAU17X1_PLL_SRC_MCLK,
+ clk_get_rate(adau->mclk), pll_rate);
+}
+
static int adau17x1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
@@ -311,10 +423,19 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
unsigned int freq;
int ret;
- if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
+ switch (adau->clk_src) {
+ case ADAU17X1_CLK_SRC_PLL_AUTO:
+ ret = adau17x1_auto_pll(dai, params);
+ if (ret)
+ return ret;
+ /* Fall-through */
+ case ADAU17X1_CLK_SRC_PLL:
freq = adau->pll_freq;
- else
+ break;
+ default:
freq = adau->sysclk;
+ break;
+ }
if (freq % params_rate(params) != 0)
return -EINVAL;
@@ -386,93 +507,6 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
ADAU17X1_SERIAL_PORT1_DELAY_MASK, val);
}
-static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id,
- int source, unsigned int freq_in, unsigned int freq_out)
-{
- struct snd_soc_codec *codec = dai->codec;
- struct adau *adau = snd_soc_codec_get_drvdata(codec);
- unsigned int r, n, m, i, j;
- unsigned int div;
- int ret;
-
- if (freq_in < 8000000 || freq_in > 27000000)
- return -EINVAL;
-
- if (!freq_out) {
- r = 0;
- n = 0;
- m = 0;
- div = 0;
- } else {
- if (freq_out % freq_in != 0) {
- div = DIV_ROUND_UP(freq_in, 13500000);
- freq_in /= div;
- r = freq_out / freq_in;
- i = freq_out % freq_in;
- j = gcd(i, freq_in);
- n = i / j;
- m = freq_in / j;
- div--;
- } else {
- r = freq_out / freq_in;
- n = 0;
- m = 0;
- div = 0;
- }
- if (n > 0xffff || m > 0xffff || div > 3 || r > 8 || r < 2)
- return -EINVAL;
- }
-
- adau->pll_regs[0] = m >> 8;
- adau->pll_regs[1] = m & 0xff;
- adau->pll_regs[2] = n >> 8;
- adau->pll_regs[3] = n & 0xff;
- adau->pll_regs[4] = (r << 3) | (div << 1);
- if (m != 0)
- adau->pll_regs[4] |= 1; /* Fractional mode */
-
- /* The PLL register is 6 bytes long and can only be written at once. */
- ret = regmap_raw_write(adau->regmap, ADAU17X1_PLL_CONTROL,
- adau->pll_regs, ARRAY_SIZE(adau->pll_regs));
- if (ret)
- return ret;
-
- adau->pll_freq = freq_out;
-
- return 0;
-}
-
-static int adau17x1_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(dai->codec);
- struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
-
- switch (clk_id) {
- case ADAU17X1_CLK_SRC_MCLK:
- case ADAU17X1_CLK_SRC_PLL:
- break;
- default:
- return -EINVAL;
- }
-
- adau->sysclk = freq;
-
- if (adau->clk_src != clk_id) {
- if (clk_id == ADAU17X1_CLK_SRC_PLL) {
- snd_soc_dapm_add_routes(dapm,
- &adau17x1_dapm_pll_route, 1);
- } else {
- snd_soc_dapm_del_routes(dapm,
- &adau17x1_dapm_pll_route, 1);
- }
- }
-
- adau->clk_src = clk_id;
-
- return 0;
-}
-
static int adau17x1_set_dai_fmt(struct snd_soc_dai *dai,
unsigned int fmt)
{
@@ -857,6 +891,10 @@ int adau17x1_add_routes(struct snd_soc_codec *codec)
ret = snd_soc_dapm_add_routes(dapm, adau17x1_no_dsp_dapm_routes,
ARRAY_SIZE(adau17x1_no_dsp_dapm_routes));
}
+
+ if (adau->clk_src != ADAU17X1_CLK_SRC_MCLK)
+ snd_soc_dapm_add_routes(dapm, &adau17x1_dapm_pll_route, 1);
+
return ret;
}
EXPORT_SYMBOL_GPL(adau17x1_add_routes);
@@ -879,6 +917,7 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
const char *firmware_name)
{
struct adau *adau;
+ int ret;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -887,6 +926,30 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
if (!adau)
return -ENOMEM;
+ adau->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(adau->mclk)) {
+ if (PTR_ERR(adau->mclk) != -ENOENT)
+ return PTR_ERR(adau->mclk);
+ /* Clock is optional (for the driver) */
+ adau->mclk = NULL;
+ } else if (adau->mclk) {
+ adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO;
+
+ /*
+ * Any valid PLL output rate will work at this point, use one
+ * that is likely to be chosen later as well. The register will
+ * be written when the PLL is powered up for the first time.
+ */
+ ret = adau_calc_pll_cfg(clk_get_rate(adau->mclk), 48000 * 1024,
+ adau->pll_regs);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(adau->mclk);
+ if (ret)
+ return ret;
+ }
+
adau->regmap = regmap;
adau->switch_mode = switch_mode;
adau->type = type;
@@ -910,6 +973,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
}
EXPORT_SYMBOL_GPL(adau17x1_probe);
+void adau17x1_remove(struct device *dev)
+{
+ struct adau *adau = dev_get_drvdata(dev);
+
+ snd_soc_unregister_codec(dev);
+ if (adau->mclk)
+ clk_disable_unprepare(adau->mclk);
+}
+EXPORT_SYMBOL_GPL(adau17x1_remove);
+
MODULE_DESCRIPTION("ASoC ADAU1X61/ADAU1X81 common code");
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h
index 5ae87a084d97..bf04b7efee40 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -22,13 +22,18 @@ enum adau17x1_pll_src {
};
enum adau17x1_clk_src {
+ /* Automatically configure PLL based on the sample rate */
+ ADAU17X1_CLK_SRC_PLL_AUTO,
ADAU17X1_CLK_SRC_MCLK,
ADAU17X1_CLK_SRC_PLL,
};
+struct clk;
+
struct adau {
unsigned int sysclk;
unsigned int pll_freq;
+ struct clk *mclk;
enum adau17x1_clk_src clk_src;
enum adau17x1_type type;
@@ -52,6 +57,7 @@ int adau17x1_add_routes(struct snd_soc_codec *codec);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev),
const char *firmware_name);
+void adau17x1_remove(struct device *dev);
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias);
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644
index 000000000000..9df72c6adcca
--- /dev/null
+++ b/sound/soc/codecs/adau7002.c
@@ -0,0 +1,80 @@
+/*
+ * ADAU7002 Stereo PDM-to-I2S/TDM converter driver
+ *
+ * Copyright 2014-2016 Analog Devices
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget adau7002_widgets[] = {
+ SND_SOC_DAPM_INPUT("PDM_DAT"),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7002_routes[] = {
+ { "Capture", NULL, "PDM_DAT" },
+ { "Capture", NULL, "IOVDD" },
+};
+
+static struct snd_soc_dai_driver adau7002_dai = {
+ .name = "adau7002-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 20,
+ },
+};
+
+static const struct snd_soc_codec_driver adau7002_codec_driver = {
+ .dapm_widgets = adau7002_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
+ .dapm_routes = adau7002_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau7002_routes),
+};
+
+static int adau7002_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &adau7002_codec_driver,
+ &adau7002_dai, 1);
+}
+
+static int adau7002_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau7002_dt_ids[] = {
+ { .compatible = "adi,adau7002", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
+#endif
+
+static struct platform_driver adau7002_driver = {
+ .driver = {
+ .name = "adau7002",
+ .of_match_table = of_match_ptr(adau7002_dt_ids),
+ },
+ .probe = adau7002_probe,
+ .remove = adau7002_remove,
+};
+module_platform_driver(adau7002_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("ADAU7002 Stereo PDM-to-I2S/TDM Converter driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 647f69de6baa..97798d250f08 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -146,6 +146,7 @@ static const struct regmap_config ak4613_regmap_cfg = {
.max_register = 0x16,
.reg_defaults = ak4613_reg,
.num_reg_defaults = ARRAY_SIZE(ak4613_reg),
+ .cache_type = REGCACHE_RBTREE,
};
static const struct of_device_id ak4613_of_match[] = {
@@ -436,15 +437,25 @@ static struct snd_soc_dai_driver ak4613_dai = {
.symmetric_rates = 1,
};
-static int ak4613_resume(struct snd_soc_codec *codec)
+static int ak4613_suspend(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
+ return 0;
+}
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+ .suspend = ak4613_suspend,
.resume = ak4613_resume,
.set_bias_level = ak4613_set_bias_level,
.controls = ak4613_snd_controls,
@@ -530,7 +541,6 @@ static int ak4613_i2c_remove(struct i2c_client *client)
static struct i2c_driver ak4613_i2c_driver = {
.driver = {
.name = "ak4613-codec",
- .owner = THIS_MODULE,
.of_match_table = ak4613_of_match,
},
.probe = ak4613_i2c_probe,
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 4d8b9e49e8d6..cc941d66ec3d 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -523,15 +523,23 @@ static struct snd_soc_dai_driver ak4642_dai = {
.symmetric_rates = 1,
};
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
- regcache_sync(regmap);
return 0;
}
+static int ak4642_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+ return 0;
+}
static int ak4642_probe(struct snd_soc_codec *codec)
{
struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -544,6 +552,7 @@ static int ak4642_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.probe = ak4642_probe,
+ .suspend = ak4642_suspend,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 664a8c044ffb..ecfdbfcae366 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -85,30 +85,9 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- bool manual_ena = false;
int val;
- switch (arizona->type) {
- case WM5102:
- switch (arizona->rev) {
- case 0:
- break;
- default:
- manual_ena = true;
- break;
- }
- default:
- break;
- }
-
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- if (!priv->spk_ena && manual_ena) {
- regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
- priv->spk_ena_pending = true;
- }
- break;
case SND_SOC_DAPM_POST_PMU:
val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
if (val & ARIZONA_SPK_OVERHEAT_STS) {
@@ -120,33 +99,12 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
regmap_update_bits_async(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
1 << w->shift, 1 << w->shift);
-
- if (priv->spk_ena_pending) {
- msleep(75);
- regmap_write_async(arizona->regmap, 0x4f5, 0xda);
- priv->spk_ena_pending = false;
- priv->spk_ena++;
- }
break;
case SND_SOC_DAPM_PRE_PMD:
- if (manual_ena) {
- priv->spk_ena--;
- if (!priv->spk_ena)
- regmap_write_async(arizona->regmap,
- 0x4f5, 0x25a);
- }
-
regmap_update_bits_async(arizona->regmap,
ARIZONA_OUTPUT_ENABLES_1,
1 << w->shift, 0);
break;
- case SND_SOC_DAPM_POST_PMD:
- if (manual_ena) {
- if (!priv->spk_ena)
- regmap_write_async(arizona->regmap,
- 0x4f5, 0x0da);
- }
- break;
default:
break;
}
@@ -324,6 +282,17 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
+int arizona_init_notifiers(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_notifiers);
+
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
@@ -619,7 +588,7 @@ const struct soc_enum arizona_asrc_rate1 =
arizona_rate_text, arizona_rate_val);
EXPORT_SYMBOL_GPL(arizona_asrc_rate1);
-static const char *arizona_vol_ramp_text[] = {
+static const char * const arizona_vol_ramp_text[] = {
"0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
"15ms/6dB", "30ms/6dB",
};
@@ -648,7 +617,7 @@ SOC_ENUM_SINGLE_DECL(arizona_out_vi_ramp,
arizona_vol_ramp_text);
EXPORT_SYMBOL_GPL(arizona_out_vi_ramp);
-static const char *arizona_lhpf_mode_text[] = {
+static const char * const arizona_lhpf_mode_text[] = {
"Low-pass", "High-pass"
};
@@ -676,7 +645,7 @@ SOC_ENUM_SINGLE_DECL(arizona_lhpf4_mode,
arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
-static const char *arizona_ng_hold_text[] = {
+static const char * const arizona_ng_hold_text[] = {
"30ms", "120ms", "250ms", "500ms",
};
@@ -810,6 +779,14 @@ const struct soc_enum arizona_output_anc_src[] = {
};
EXPORT_SYMBOL_GPL(arizona_output_anc_src);
+const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0),
+};
+EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
+
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -2573,6 +2550,30 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
+int arizona_register_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb,
+ int (*notify)(struct notifier_block *nb,
+ unsigned long action, void *data))
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ nb->notifier_call = notify;
+
+ return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_register_notifier);
+
+int arizona_unregister_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
+
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index ce0531b8c632..69da1ef3a17c 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -63,6 +63,9 @@
#define ARIZONA_DVFS_SR1_RQ 0x001
#define ARIZONA_DVFS_ADSP1_RQ 0x100
+/* Notifier events */
+#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
+
struct arizona;
struct wm_adsp;
@@ -87,14 +90,15 @@ struct arizona_priv {
unsigned int out_down_pending;
unsigned int out_down_delay;
- unsigned int spk_ena:2;
- unsigned int spk_ena_pending:1;
-
unsigned int dvfs_reqs;
struct mutex dvfs_lock;
bool dvfs_cached;
};
+struct arizona_voice_trigger_info {
+ int core;
+};
+
#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
@@ -248,6 +252,8 @@ extern const struct soc_enum arizona_anc_input_src[];
extern const struct soc_enum arizona_anc_ng_enum;
extern const struct soc_enum arizona_output_anc_src[];
+extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
+
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
@@ -306,6 +312,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
extern int arizona_init_spk(struct snd_soc_codec *codec);
extern int arizona_init_gpio(struct snd_soc_codec *codec);
extern int arizona_init_mono(struct snd_soc_codec *codec);
+extern int arizona_init_notifiers(struct snd_soc_codec *codec);
extern int arizona_free_spk(struct snd_soc_codec *codec);
@@ -317,4 +324,13 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+extern int arizona_register_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb,
+ int (*notify)(struct notifier_block *nb,
+ unsigned long action,
+ void *data));
+extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb);
+
#endif
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad113e96..2a8d0ee141d4 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -25,22 +25,41 @@ static const struct snd_soc_dapm_route bt_sco_routes[] = {
{ "TX", NULL, "Playback" },
};
-static struct snd_soc_dai_driver bt_sco_dai = {
- .name = "bt-sco-pcm",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+static struct snd_soc_dai_driver bt_sco_dai[] = {
+ {
+ .name = "bt-sco-pcm",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
},
+ {
+ .name = "bt-sco-pcm-wb",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ }
};
static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
@@ -53,7 +72,7 @@ static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
static int bt_sco_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
- &bt_sco_dai, 1);
+ bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
}
static int bt_sco_remove(struct platform_device *pdev)
@@ -77,6 +96,7 @@ MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
#if defined(CONFIG_OF)
static const struct of_device_id bt_sco_codec_of_match[] = {
{ .compatible = "delta,dfbmcs320", },
+ { .compatible = "linux,bt-sco", },
{},
};
MODULE_DEVICE_TABLE(of, bt_sco_codec_of_match);
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644
index 000000000000..6f9c1addcd7f
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.c
@@ -0,0 +1,1303 @@
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/cs35l33.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include "cs35l33.h"
+
+#define CS35L33_BOOT_DELAY 50
+
+struct cs35l33_private {
+ struct snd_soc_codec *codec;
+ struct cs35l33_pdata pdata;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ bool amp_cal;
+ int mclk_int;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ bool is_tdm_mode;
+ bool enable_soft_ramp;
+};
+
+static const struct reg_default cs35l33_reg[] = {
+ {CS35L33_PWRCTL1, 0x85},
+ {CS35L33_PWRCTL2, 0xFE},
+ {CS35L33_CLK_CTL, 0x0C},
+ {CS35L33_BST_PEAK_CTL, 0x90},
+ {CS35L33_PROTECT_CTL, 0x55},
+ {CS35L33_BST_CTL1, 0x00},
+ {CS35L33_BST_CTL2, 0x01},
+ {CS35L33_ADSP_CTL, 0x00},
+ {CS35L33_ADC_CTL, 0xC8},
+ {CS35L33_DAC_CTL, 0x14},
+ {CS35L33_DIG_VOL_CTL, 0x00},
+ {CS35L33_CLASSD_CTL, 0x04},
+ {CS35L33_AMP_CTL, 0x90},
+ {CS35L33_INT_MASK_1, 0xFF},
+ {CS35L33_INT_MASK_2, 0xFF},
+ {CS35L33_DIAG_LOCK, 0x00},
+ {CS35L33_DIAG_CTRL_1, 0x40},
+ {CS35L33_DIAG_CTRL_2, 0x00},
+ {CS35L33_HG_MEMLDO_CTL, 0x62},
+ {CS35L33_HG_REL_RATE, 0x03},
+ {CS35L33_LDO_DEL, 0x12},
+ {CS35L33_HG_HEAD, 0x0A},
+ {CS35L33_HG_EN, 0x05},
+ {CS35L33_TX_VMON, 0x00},
+ {CS35L33_TX_IMON, 0x03},
+ {CS35L33_TX_VPMON, 0x02},
+ {CS35L33_TX_VBSTMON, 0x05},
+ {CS35L33_TX_FLAG, 0x06},
+ {CS35L33_TX_EN1, 0x00},
+ {CS35L33_TX_EN2, 0x00},
+ {CS35L33_TX_EN3, 0x00},
+ {CS35L33_TX_EN4, 0x00},
+ {CS35L33_RX_AUD, 0x40},
+ {CS35L33_RX_SPLY, 0x03},
+ {CS35L33_RX_ALIVE, 0x04},
+ {CS35L33_BST_CTL4, 0x63},
+};
+
+static const struct reg_sequence cs35l33_patch[] = {
+ { 0x00, 0x99, 0 },
+ { 0x59, 0x02, 0 },
+ { 0x52, 0x30, 0 },
+ { 0x39, 0x45, 0 },
+ { 0x57, 0x30, 0 },
+ { 0x2C, 0x68, 0 },
+ { 0x00, 0x00, 0 },
+};
+
+static bool cs35l33_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_HG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l33_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ /* these are read only registers */
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_HG_STATUS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool cs35l33_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L33_DEVID_AB:
+ case CS35L33_DEVID_CD:
+ case CS35L33_DEVID_E:
+ case CS35L33_REV_ID:
+ case CS35L33_PWRCTL1:
+ case CS35L33_PWRCTL2:
+ case CS35L33_CLK_CTL:
+ case CS35L33_BST_PEAK_CTL:
+ case CS35L33_PROTECT_CTL:
+ case CS35L33_BST_CTL1:
+ case CS35L33_BST_CTL2:
+ case CS35L33_ADSP_CTL:
+ case CS35L33_ADC_CTL:
+ case CS35L33_DAC_CTL:
+ case CS35L33_DIG_VOL_CTL:
+ case CS35L33_CLASSD_CTL:
+ case CS35L33_AMP_CTL:
+ case CS35L33_INT_MASK_1:
+ case CS35L33_INT_MASK_2:
+ case CS35L33_INT_STATUS_1:
+ case CS35L33_INT_STATUS_2:
+ case CS35L33_DIAG_LOCK:
+ case CS35L33_DIAG_CTRL_1:
+ case CS35L33_DIAG_CTRL_2:
+ case CS35L33_HG_MEMLDO_CTL:
+ case CS35L33_HG_REL_RATE:
+ case CS35L33_LDO_DEL:
+ case CS35L33_HG_HEAD:
+ case CS35L33_HG_EN:
+ case CS35L33_TX_VMON:
+ case CS35L33_TX_IMON:
+ case CS35L33_TX_VPMON:
+ case CS35L33_TX_VBSTMON:
+ case CS35L33_TX_FLAG:
+ case CS35L33_TX_EN1:
+ case CS35L33_TX_EN2:
+ case CS35L33_TX_EN3:
+ case CS35L33_TX_EN4:
+ case CS35L33_RX_AUD:
+ case CS35L33_RX_SPLY:
+ case CS35L33_RX_ALIVE:
+ case CS35L33_BST_CTL4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(classd_ctl_tlv, 900, 100, 0);
+static DECLARE_TLV_DB_SCALE(dac_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l33_snd_controls[] = {
+
+ SOC_SINGLE_TLV("SPK Amp Volume", CS35L33_AMP_CTL,
+ 4, 0x09, 0, classd_ctl_tlv),
+ SOC_SINGLE_SX_TLV("DAC Volume", CS35L33_DIG_VOL_CTL,
+ 0, 0x34, 0xE4, dac_tlv),
+};
+
+static int cs35l33_spkrdrv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (!priv->amp_cal) {
+ usleep_range(8000, 9000);
+ priv->amp_cal = true;
+ regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_CAL, 0);
+ dev_dbg(codec->dev, "Amp calibration done\n");
+ }
+ dev_dbg(codec->dev, "Amp turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(codec->dev, "Amp turned off\n");
+ break;
+ default:
+ dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l33_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_BST, 0);
+ val = priv->is_tdm_mode ? 0 : CS35L33_PDN_TDM;
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_PDN_TDM, val);
+ dev_dbg(codec->dev, "BST turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(codec->dev, "SDIN turned on\n");
+ if (!priv->amp_cal) {
+ regmap_update_bits(priv->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_CAL, CS35L33_AMP_CAL);
+ dev_dbg(codec->dev, "Amp calibration started\n");
+ usleep_range(10000, 11000);
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_PDN_TDM, CS35L33_PDN_TDM);
+ usleep_range(4000, 4100);
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_BST, CS35L33_PDN_BST);
+ dev_dbg(codec->dev, "BST and SDIN turned off\n");
+ break;
+ default:
+ dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+
+ }
+
+ return 0;
+}
+
+static int cs35l33_sdout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int mask = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+ unsigned int mask2 = CS35L33_SDOUT_3ST_TDM;
+ unsigned int val, val2;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->is_tdm_mode) {
+ /* set sdout_3st_i2s and reset pdn_tdm */
+ val = CS35L33_SDOUT_3ST_I2S;
+ /* reset sdout_3st_tdm */
+ val2 = 0;
+ } else {
+ /* reset sdout_3st_i2s and set pdn_tdm */
+ val = CS35L33_PDN_TDM;
+ /* set sdout_3st_tdm */
+ val2 = CS35L33_SDOUT_3ST_TDM;
+ }
+ dev_dbg(codec->dev, "SDOUT turned on\n");
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = CS35L33_SDOUT_3ST_I2S | CS35L33_PDN_TDM;
+ val2 = CS35L33_SDOUT_3ST_TDM;
+ dev_dbg(codec->dev, "SDOUT turned off\n");
+ break;
+ default:
+ dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ return 0;
+ }
+
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ mask, val);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ mask2, val2);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l33_dapm_widgets[] = {
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("SPKDRV", CS35L33_PWRCTL1, 7, 1, NULL, 0,
+ cs35l33_spkrdrv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L33_PWRCTL2,
+ 2, 1, cs35l33_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("MON"),
+
+ SND_SOC_DAPM_ADC("VMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("IMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_IMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("VPMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VPMON_SHIFT, 1),
+ SND_SOC_DAPM_ADC("VBSTMON", NULL,
+ CS35L33_PWRCTL2, CS35L33_PDN_VBSTMON_SHIFT, 1),
+
+ SND_SOC_DAPM_AIF_OUT_E("SDOUT", NULL, 0, SND_SOC_NOPM, 0, 0,
+ cs35l33_sdout_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l33_audio_map[] = {
+ {"SDIN", NULL, "CS35L33 Playback"},
+ {"SPKDRV", NULL, "SDIN"},
+ {"SPK", NULL, "SPKDRV"},
+
+ {"VMON", NULL, "MON"},
+ {"IMON", NULL, "MON"},
+
+ {"SDOUT", NULL, "VMON"},
+ {"SDOUT", NULL, "IMON"},
+ {"CS35L33 Capture", NULL, "SDOUT"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vphg_auto_route[] = {
+ {"SPKDRV", NULL, "VPMON"},
+ {"VPMON", NULL, "CS35L33 Playback"},
+};
+
+static const struct snd_soc_dapm_route cs35l33_vp_vbst_mon_route[] = {
+ {"SDOUT", NULL, "VPMON"},
+ {"VPMON", NULL, "MON"},
+ {"SDOUT", NULL, "VBSTMON"},
+ {"VBSTMON", NULL, "MON"},
+};
+
+static int cs35l33_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ unsigned int val;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_ALL, 0);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL1,
+ CS35L33_PDN_ALL, CS35L33_PDN_ALL);
+ regmap_read(priv->regmap, CS35L33_INT_STATUS_2, &val);
+ usleep_range(1000, 1100);
+ if (val & CS35L33_PDN_DONE)
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS, CS35L33_MCLKDIS);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l33_mclk_div {
+ int mclk;
+ int srate;
+ u8 adsp_rate;
+ u8 int_fs_ratio;
+};
+
+static const struct cs35l33_mclk_div cs35l33_mclk_coeffs[] = {
+ /* MCLK, Sample Rate, adsp_rate, int_fs_ratio */
+ {5644800, 11025, 0x4, CS35L33_INT_FS_RATE},
+ {5644800, 22050, 0x8, CS35L33_INT_FS_RATE},
+ {5644800, 44100, 0xC, CS35L33_INT_FS_RATE},
+
+ {6000000, 8000, 0x1, 0},
+ {6000000, 11025, 0x2, 0},
+ {6000000, 11029, 0x3, 0},
+ {6000000, 12000, 0x4, 0},
+ {6000000, 16000, 0x5, 0},
+ {6000000, 22050, 0x6, 0},
+ {6000000, 22059, 0x7, 0},
+ {6000000, 24000, 0x8, 0},
+ {6000000, 32000, 0x9, 0},
+ {6000000, 44100, 0xA, 0},
+ {6000000, 44118, 0xB, 0},
+ {6000000, 48000, 0xC, 0},
+
+ {6144000, 8000, 0x1, CS35L33_INT_FS_RATE},
+ {6144000, 12000, 0x4, CS35L33_INT_FS_RATE},
+ {6144000, 16000, 0x5, CS35L33_INT_FS_RATE},
+ {6144000, 24000, 0x8, CS35L33_INT_FS_RATE},
+ {6144000, 32000, 0x9, CS35L33_INT_FS_RATE},
+ {6144000, 48000, 0xC, CS35L33_INT_FS_RATE},
+};
+
+static int cs35l33_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l33_mclk_coeffs); i++) {
+ if (cs35l33_mclk_coeffs[i].mclk == mclk &&
+ cs35l33_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs35l33_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, CS35L33_MS_MASK);
+ dev_dbg(codec->dev, "Audio port in master mode\n");
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, 0);
+ dev_dbg(codec->dev, "Audio port in slave mode\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * tdm mode in cs35l33 resembles dsp-a mode very
+ * closely, it is dsp-a with fsync shifted left by half bclk
+ */
+ priv->is_tdm_mode = true;
+ dev_dbg(codec->dev, "Audio port in TDM mode\n");
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ priv->is_tdm_mode = false;
+ dev_dbg(codec->dev, "Audio port in I2S mode\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs35l33_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ int sample_size = params_width(params);
+ int coeff = cs35l33_get_mclk_coeff(priv->mclk_int, params_rate(params));
+
+ if (coeff < 0)
+ return coeff;
+
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_ADSP_FS | CS35L33_INT_FS_RATE,
+ cs35l33_mclk_coeffs[coeff].int_fs_ratio
+ | cs35l33_mclk_coeffs[coeff].adsp_rate);
+
+ if (priv->is_tdm_mode) {
+ sample_size = (sample_size / 8) - 1;
+ if (sample_size > 2)
+ sample_size = 2;
+ regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+ CS35L33_AUDIN_RX_DEPTH,
+ sample_size << CS35L33_AUDIN_RX_DEPTH_SHIFT);
+ }
+
+ dev_dbg(codec->dev, "sample rate=%d, bits per sample=%d\n",
+ params_rate(params), params_width(params));
+
+ return 0;
+}
+
+static const unsigned int cs35l33_src_rates[] = {
+ 8000, 11025, 11029, 12000, 16000, 22050,
+ 22059, 24000, 32000, 44100, 44118, 48000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l33_constraints = {
+ .count = ARRAY_SIZE(cs35l33_src_rates),
+ .list = cs35l33_src_rates,
+};
+
+static int cs35l33_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l33_constraints);
+ return 0;
+}
+
+static int cs35l33_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (tristate) {
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_SDOUT_3ST_I2S, CS35L33_SDOUT_3ST_I2S);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_SDOUT_3ST_TDM, CS35L33_SDOUT_3ST_TDM);
+ } else {
+ regmap_update_bits(priv->regmap, CS35L33_PWRCTL2,
+ CS35L33_SDOUT_3ST_I2S, 0);
+ regmap_update_bits(priv->regmap, CS35L33_CLK_CTL,
+ CS35L33_SDOUT_3ST_TDM, 0);
+ }
+
+ return 0;
+}
+
+static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg, bit_pos, i;
+ int slot, slot_num;
+
+ if (slot_width != 8)
+ return -EINVAL;
+
+ /* scan rx_mask for aud slot */
+ slot = ffs(rx_mask) - 1;
+ if (slot >= 0) {
+ regmap_update_bits(priv->regmap, CS35L33_RX_AUD,
+ CS35L33_X_LOC, slot);
+ dev_dbg(codec->dev, "Audio starts from slots %d", slot);
+ }
+
+ /*
+ * scan tx_mask: vmon(2 slots); imon (2 slots);
+ * vpmon (1 slot) vbstmon (1 slot)
+ */
+ slot = ffs(tx_mask) - 1;
+ slot_num = 0;
+
+ for (i = 0; i < 2 ; i++) {
+ /* disable vpmon/vbstmon: enable later if set in tx_mask */
+ regmap_update_bits(priv->regmap, CS35L33_TX_VPMON + i,
+ CS35L33_X_STATE | CS35L33_X_LOC, CS35L33_X_STATE
+ | CS35L33_X_LOC);
+ }
+
+ /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route,
+ ARRAY_SIZE(cs35l33_vp_vbst_mon_route));
+
+ while (slot >= 0) {
+ /* configure VMON_TX_LOC */
+ if (slot_num == 0) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ dev_dbg(codec->dev, "VMON enabled in slots %d-%d",
+ slot, slot + 1);
+ }
+
+ /* configure IMON_TX_LOC */
+ if (slot_num == 3) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_IMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ dev_dbg(codec->dev, "IMON enabled in slots %d-%d",
+ slot, slot + 1);
+ }
+
+ /* configure VPMON_TX_LOC */
+ if (slot_num == 4) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VPMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ snd_soc_dapm_add_routes(dapm,
+ &cs35l33_vp_vbst_mon_route[0], 2);
+ dev_dbg(codec->dev, "VPMON enabled in slots %d", slot);
+ }
+
+ /* configure VBSTMON_TX_LOC */
+ if (slot_num == 5) {
+ regmap_update_bits(priv->regmap, CS35L33_TX_VBSTMON,
+ CS35L33_X_STATE | CS35L33_X_LOC, slot);
+ snd_soc_dapm_add_routes(dapm,
+ &cs35l33_vp_vbst_mon_route[2], 2);
+ dev_dbg(codec->dev,
+ "VBSTMON enabled in slots %d", slot);
+ }
+
+ /* Enable the relevant tx slot */
+ reg = CS35L33_TX_EN4 - (slot/8);
+ bit_pos = slot - ((slot / 8) * (8));
+ regmap_update_bits(priv->regmap, reg,
+ 1 << bit_pos, 1 << bit_pos);
+
+ tx_mask &= ~(1 << slot);
+ slot = ffs(tx_mask) - 1;
+ slot_num++;
+ }
+
+ return 0;
+}
+
+static int cs35l33_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+ switch (freq) {
+ case CS35L33_MCLK_5644:
+ case CS35L33_MCLK_6:
+ case CS35L33_MCLK_6144:
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIV2, 0);
+ cs35l33->mclk_int = freq;
+ break;
+ case CS35L33_MCLK_11289:
+ case CS35L33_MCLK_12:
+ case CS35L33_MCLK_12288:
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIV2, CS35L33_MCLKDIV2);
+ cs35l33->mclk_int = freq/2;
+ break;
+ default:
+ cs35l33->mclk_int = 0;
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "external mclk freq=%d, internal mclk freq=%d\n",
+ freq, cs35l33->mclk_int);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l33_ops = {
+ .startup = cs35l33_pcm_startup,
+ .set_tristate = cs35l33_set_tristate,
+ .set_fmt = cs35l33_set_dai_fmt,
+ .hw_params = cs35l33_pcm_hw_params,
+ .set_tdm_slot = cs35l33_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l33_dai = {
+ .name = "cs35l33-dai",
+ .id = 0,
+ .playback = {
+ .stream_name = "CS35L33 Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = CS35L33_RATES,
+ .formats = CS35L33_FORMATS,
+ },
+ .capture = {
+ .stream_name = "CS35L33 Capture",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CS35L33_RATES,
+ .formats = CS35L33_FORMATS,
+ },
+ .ops = &cs35l33_ops,
+ .symmetric_rates = 1,
+};
+
+static int cs35l33_set_hg_data(struct snd_soc_codec *codec,
+ struct cs35l33_pdata *pdata)
+{
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ if (hg_config->enable_hg_algo) {
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_MEM_DEPTH_MASK,
+ hg_config->mem_depth << CS35L33_MEM_DEPTH_SHIFT);
+ regmap_write(priv->regmap, CS35L33_HG_REL_RATE,
+ hg_config->release_rate);
+ regmap_update_bits(priv->regmap, CS35L33_HG_HEAD,
+ CS35L33_HD_RM_MASK,
+ hg_config->hd_rm << CS35L33_HD_RM_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_LDO_THLD_MASK,
+ hg_config->ldo_thld << CS35L33_LDO_THLD_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_MEMLDO_CTL,
+ CS35L33_LDO_DISABLE_MASK,
+ hg_config->ldo_path_disable <<
+ CS35L33_LDO_DISABLE_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_LDO_ENTRY_DELAY_MASK,
+ hg_config->ldo_entry_delay <<
+ CS35L33_LDO_ENTRY_DELAY_SHIFT);
+ if (hg_config->vp_hg_auto) {
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_VP_HG_AUTO_MASK,
+ CS35L33_VP_HG_AUTO_MASK);
+ snd_soc_dapm_add_routes(dapm, cs35l33_vphg_auto_route,
+ ARRAY_SIZE(cs35l33_vphg_auto_route));
+ }
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_VP_HG_MASK,
+ hg_config->vp_hg << CS35L33_VP_HG_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_VP_HG_RATE_MASK,
+ hg_config->vp_hg_rate << CS35L33_VP_HG_RATE_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_LDO_DEL,
+ CS35L33_VP_HG_VA_MASK,
+ hg_config->vp_hg_va << CS35L33_VP_HG_VA_SHIFT);
+ regmap_update_bits(priv->regmap, CS35L33_HG_EN,
+ CS35L33_CLASS_HG_EN_MASK, CS35L33_CLASS_HG_EN_MASK);
+ }
+ return 0;
+}
+
+static int cs35l33_set_bst_ipk(struct snd_soc_codec *codec, unsigned int bst)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0, steps = 0;
+
+ /* Boost current in uA */
+ if (bst > 3600000 || bst < 1850000) {
+ dev_err(codec->dev, "Invalid boost current %d\n", bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (bst % 15625) {
+ dev_err(codec->dev, "Current not a multiple of 15625uA (%d)\n",
+ bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ while (bst > 1850000) {
+ bst -= 15625;
+ steps++;
+ }
+
+ regmap_write(cs35l33->regmap, CS35L33_BST_PEAK_CTL,
+ steps+0x70);
+
+err:
+ return ret;
+}
+
+static int cs35l33_probe(struct snd_soc_codec *codec)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+ cs35l33->codec = codec;
+ pm_runtime_get_sync(codec->dev);
+
+ regmap_update_bits(cs35l33->regmap, CS35L33_PROTECT_CTL,
+ CS35L33_ALIVE_WD_DIS, 0x8);
+ regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL2,
+ CS35L33_ALIVE_WD_DIS2,
+ CS35L33_ALIVE_WD_DIS2);
+
+ /* Set Platform Data */
+ regmap_update_bits(cs35l33->regmap, CS35L33_BST_CTL1,
+ CS35L33_BST_CTL_MASK, cs35l33->pdata.boost_ctl);
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLASSD_CTL,
+ CS35L33_AMP_DRV_SEL_MASK,
+ cs35l33->pdata.amp_drv_sel << CS35L33_AMP_DRV_SEL_SHIFT);
+
+ if (cs35l33->pdata.boost_ipk)
+ cs35l33_set_bst_ipk(codec, cs35l33->pdata.boost_ipk);
+
+ if (cs35l33->enable_soft_ramp) {
+ snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, CS35L33_DIGSFT);
+ snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
+ } else {
+ snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, 0);
+ }
+
+ /* update IMON scaling rate if different from default of 0x8 */
+ if (cs35l33->pdata.imon_adc_scale != 0x8)
+ snd_soc_update_bits(codec, CS35L33_ADC_CTL,
+ CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
+
+ cs35l33_set_hg_data(codec, &(cs35l33->pdata));
+
+ /*
+ * unmask important interrupts that causes the chip to enter
+ * speaker safe mode and hence deserves user attention
+ */
+ regmap_update_bits(cs35l33->regmap, CS35L33_INT_MASK_1,
+ CS35L33_M_OTE | CS35L33_M_OTW | CS35L33_M_AMP_SHORT |
+ CS35L33_M_CAL_ERR, 0);
+
+ pm_runtime_put_sync(codec->dev);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l33 = {
+ .probe = cs35l33_probe,
+
+ .set_bias_level = cs35l33_set_bias_level,
+ .set_sysclk = cs35l33_codec_set_sysclk,
+
+ .dapm_widgets = cs35l33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l33_dapm_widgets),
+ .dapm_routes = cs35l33_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l33_audio_map),
+ .controls = cs35l33_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l33_snd_controls),
+
+ .idle_bias_off = true,
+};
+
+static const struct regmap_config cs35l33_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L33_MAX_REGISTER,
+ .reg_defaults = cs35l33_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l33_reg),
+ .volatile_reg = cs35l33_volatile_register,
+ .readable_reg = cs35l33_readable_register,
+ .writeable_reg = cs35l33_writeable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_rw = true,
+};
+
+static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l33->regmap, false);
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+
+ ret = regcache_sync(cs35l33->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs35l33->regmap, true);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ regcache_cache_only(cs35l33->regmap, true);
+ regcache_mark_dirty(cs35l33->regmap);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l33_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend,
+ cs35l33_runtime_resume,
+ NULL)
+};
+
+static int cs35l33_get_hg_data(const struct device_node *np,
+ struct cs35l33_pdata *pdata)
+{
+ struct device_node *hg;
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ u32 val32;
+
+ hg = of_get_child_by_name(np, "cirrus,hg-algo");
+ hg_config->enable_hg_algo = hg ? true : false;
+
+ if (hg_config->enable_hg_algo) {
+ if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0)
+ hg_config->mem_depth = val32;
+ if (of_property_read_u32(hg, "cirrus,release-rate",
+ &val32) >= 0)
+ hg_config->release_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0)
+ hg_config->ldo_thld = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-path-disable",
+ &val32) >= 0)
+ hg_config->ldo_path_disable = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-entry-delay",
+ &val32) >= 0)
+ hg_config->ldo_entry_delay = val32;
+
+ hg_config->vp_hg_auto = of_property_read_bool(hg,
+ "cirrus,vp-hg-auto");
+
+ if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0)
+ hg_config->vp_hg = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0)
+ hg_config->vp_hg_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0)
+ hg_config->vp_hg_va = val32;
+ }
+
+ of_node_put(hg);
+
+ return 0;
+}
+
+static irqreturn_t cs35l33_irq_thread(int irq, void *data)
+{
+ struct cs35l33_private *cs35l33 = data;
+ struct snd_soc_codec *codec = cs35l33->codec;
+ unsigned int sticky_val1, sticky_val2, current_val, mask1, mask2;
+
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_2,
+ &sticky_val2);
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+ &sticky_val1);
+ regmap_read(cs35l33->regmap, CS35L33_INT_MASK_2, &mask2);
+ regmap_read(cs35l33->regmap, CS35L33_INT_MASK_1, &mask1);
+
+ /* Check to see if the unmasked bits are active,
+ * if not then exit.
+ */
+ if (!(sticky_val1 & ~mask1) && !(sticky_val2 & ~mask2))
+ return IRQ_NONE;
+
+ regmap_read(cs35l33->regmap, CS35L33_INT_STATUS_1,
+ &current_val);
+
+ /* handle the interrupts */
+
+ if (sticky_val1 & CS35L33_AMP_SHORT) {
+ dev_crit(codec->dev, "Amp short error\n");
+ if (!(current_val & CS35L33_AMP_SHORT)) {
+ dev_dbg(codec->dev,
+ "Amp short error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL,
+ CS35L33_AMP_SHORT_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL,
+ CS35L33_AMP_SHORT_RLS,
+ CS35L33_AMP_SHORT_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_AMP_SHORT_RLS,
+ 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_CAL_ERR) {
+ dev_err(codec->dev, "Cal error\n");
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ if (!(current_val & CS35L33_CAL_ERR)) {
+ dev_dbg(codec->dev, "Cal error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ CS35L33_CAL_ERR_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_CAL_ERR_RLS,
+ 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_OTE) {
+ dev_crit(codec->dev, "Over temperature error\n");
+ if (!(current_val & CS35L33_OTE)) {
+ dev_dbg(codec->dev,
+ "Over temperature error release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS,
+ CS35L33_OTE_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky_val1 & CS35L33_OTW) {
+ dev_err(codec->dev, "Over temperature warning\n");
+ if (!(current_val & CS35L33_OTW)) {
+ dev_dbg(codec->dev,
+ "Over temperature warning release\n");
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS,
+ CS35L33_OTW_RLS);
+ regmap_update_bits(cs35l33->regmap,
+ CS35L33_AMP_CTL, CS35L33_OTW_RLS, 0);
+ }
+ }
+ if (CS35L33_ALIVE_ERR & sticky_val1)
+ dev_err(codec->dev, "ERROR: ADSPCLK Interrupt\n");
+
+ if (CS35L33_MCLK_ERR & sticky_val1)
+ dev_err(codec->dev, "ERROR: MCLK Interrupt\n");
+
+ if (CS35L33_VMON_OVFL & sticky_val2)
+ dev_err(codec->dev,
+ "ERROR: VMON Overflow Interrupt\n");
+
+ if (CS35L33_IMON_OVFL & sticky_val2)
+ dev_err(codec->dev,
+ "ERROR: IMON Overflow Interrupt\n");
+
+ if (CS35L33_VPMON_OVFL & sticky_val2)
+ dev_err(codec->dev,
+ "ERROR: VPMON Overflow Interrupt\n");
+
+ return IRQ_HANDLED;
+}
+
+static const char * const cs35l33_core_supplies[] = {
+ "VA",
+ "VP",
+};
+
+static int cs35l33_of_get_pdata(struct device *dev,
+ struct cs35l33_private *cs35l33)
+{
+ struct device_node *np = dev->of_node;
+ struct cs35l33_pdata *pdata = &cs35l33->pdata;
+ u32 val32;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "cirrus,boost-ctl", &val32) >= 0) {
+ pdata->boost_ctl = val32;
+ pdata->amp_drv_sel = 1;
+ }
+
+ if (of_property_read_u32(np, "cirrus,ramp-rate", &val32) >= 0) {
+ pdata->ramp_rate = val32;
+ cs35l33->enable_soft_ramp = true;
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-ipk", &val32) >= 0)
+ pdata->boost_ipk = val32;
+
+ if (of_property_read_u32(np, "cirrus,imon-adc-scale", &val32) >= 0) {
+ if ((val32 == 0x0) || (val32 == 0x7) || (val32 == 0x6))
+ pdata->imon_adc_scale = val32;
+ else
+ /* use default value */
+ pdata->imon_adc_scale = 0x8;
+ } else {
+ /* use default value */
+ pdata->imon_adc_scale = 0x8;
+ }
+
+ cs35l33_get_hg_data(np, pdata);
+
+ return 0;
+}
+
+static int cs35l33_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l33_private *cs35l33;
+ struct cs35l33_pdata *pdata = dev_get_platdata(&i2c_client->dev);
+ int ret, devid, i;
+ unsigned int reg;
+
+ cs35l33 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs35l33_private),
+ GFP_KERNEL);
+ if (!cs35l33)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs35l33);
+ cs35l33->regmap = devm_regmap_init_i2c(i2c_client, &cs35l33_regmap);
+ if (IS_ERR(cs35l33->regmap)) {
+ ret = PTR_ERR(cs35l33->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l33->regmap, true);
+
+ for (i = 0; i < ARRAY_SIZE(cs35l33_core_supplies); i++)
+ cs35l33->core_supplies[i].supply
+ = cs35l33_core_supplies[i];
+ cs35l33->num_core_supplies = ARRAY_SIZE(cs35l33_core_supplies);
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l33->pdata = *pdata;
+ } else {
+ cs35l33_of_get_pdata(&i2c_client->dev, cs35l33);
+ pdata = &cs35l33->pdata;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l33_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l33", cs35l33);
+ if (ret != 0)
+ dev_warn(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+ /* We could issue !RST or skip it based on AMP topology */
+ cs35l33->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset-gpios", GPIOD_OUT_HIGH);
+ if (IS_ERR(cs35l33->reset_gpio)) {
+ dev_err(&i2c_client->dev, "%s ERROR: Can't get reset GPIO\n",
+ __func__);
+ return PTR_ERR(cs35l33->reset_gpio);
+ }
+
+ ret = regulator_bulk_enable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+ regcache_cache_only(cs35l33->regmap, false);
+
+ /* initialize codec */
+ ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_AB, &reg);
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_CD, &reg);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L33_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35L33 Device ID (%X). Expected ID %X\n",
+ devid, CS35L33_CHIP_ID);
+ goto err_enable;
+ }
+
+ ret = regmap_read(cs35l33->regmap, CS35L33_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_enable;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35L33, Revision: %02X\n", reg & 0xFF);
+
+ ret = regmap_register_patch(cs35l33->regmap,
+ cs35l33_patch, ARRAY_SIZE(cs35l33_patch));
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "Error in applying regmap patch: %d\n", ret);
+ goto err_enable;
+ }
+
+ /* disable mclk and tdm */
+ regmap_update_bits(cs35l33->regmap, CS35L33_CLK_CTL,
+ CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM,
+ CS35L33_MCLKDIS | CS35L33_SDOUT_3ST_TDM);
+
+ pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+ pm_runtime_use_autosuspend(&i2c_client->dev);
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs35l33, &cs35l33_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "%s: Register codec failed\n",
+ __func__);
+ goto err_enable;
+ }
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static int cs35l33_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return 0;
+}
+
+static const struct of_device_id cs35l33_of_match[] = {
+ { .compatible = "cirrus,cs35l33", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l33_of_match);
+
+static const struct i2c_device_id cs35l33_id[] = {
+ {"cs35l33", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+ .driver = {
+ .name = "cs35l33",
+ .pm = &cs35l33_pm_ops,
+ .of_match_table = cs35l33_of_match,
+
+ },
+ .id_table = cs35l33_id,
+ .probe = cs35l33_i2c_probe,
+ .remove = cs35l33_i2c_remove,
+
+};
+module_i2c_driver(cs35l33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L33 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l33.h b/sound/soc/codecs/cs35l33.h
new file mode 100644
index 000000000000..c045737d1a5f
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.h
@@ -0,0 +1,221 @@
+/*
+ * cs35l33.h -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L33_H__
+#define __CS35L33_H__
+
+#define CS35L33_CHIP_ID 0x00035A33
+#define CS35L33_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L33_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L33_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L33_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L33_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L33_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L33_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L33_CLK_CTL 0x08 /* Clock Ctl */
+#define CS35L33_BST_PEAK_CTL 0x09 /* Max Current for Boost */
+#define CS35L33_PROTECT_CTL 0x0A /* Amp Protection Parameters */
+#define CS35L33_BST_CTL1 0x0B /* Boost Converter CTL1 */
+#define CS35L33_BST_CTL2 0x0C /* Boost Converter CTL2 */
+#define CS35L33_ADSP_CTL 0x0D /* Serial Port Control */
+#define CS35L33_ADC_CTL 0x0E /* ADC Control */
+#define CS35L33_DAC_CTL 0x0F /* DAC Control */
+#define CS35L33_DIG_VOL_CTL 0x10 /* Digital Volume CTL */
+#define CS35L33_CLASSD_CTL 0x11 /* Class D Amp CTL */
+#define CS35L33_AMP_CTL 0x12 /* Amp Gain/Protecton Release CTL */
+#define CS35L33_INT_MASK_1 0x13 /* Interrupt Mask 1 */
+#define CS35L33_INT_MASK_2 0x14 /* Interrupt Mask 2 */
+#define CS35L33_INT_STATUS_1 0x15 /* Interrupt Status 1 [RO] */
+#define CS35L33_INT_STATUS_2 0x16 /* Interrupt Status 2 [RO] */
+#define CS35L33_DIAG_LOCK 0x17 /* Diagnostic Mode Register Lock */
+#define CS35L33_DIAG_CTRL_1 0x18 /* Diagnostic Mode Register Control */
+#define CS35L33_DIAG_CTRL_2 0x19 /* Diagnostic Mode Register Control 2 */
+#define CS35L33_HG_MEMLDO_CTL 0x23 /* H/G Memory/LDO CTL */
+#define CS35L33_HG_REL_RATE 0x24 /* H/G Release Rate */
+#define CS35L33_LDO_DEL 0x25 /* LDO Entry Delay/VPhg Control 1 */
+#define CS35L33_HG_HEAD 0x29 /* H/G Headroom */
+#define CS35L33_HG_EN 0x2A /* H/G Enable/VPhg CNT2 */
+#define CS35L33_TX_VMON 0x2D /* TDM TX Control 1 (VMON) */
+#define CS35L33_TX_IMON 0x2E /* TDM TX Control 2 (IMON) */
+#define CS35L33_TX_VPMON 0x2F /* TDM TX Control 3 (VPMON) */
+#define CS35L33_TX_VBSTMON 0x30 /* TDM TX Control 4 (VBSTMON) */
+#define CS35L33_TX_FLAG 0x31 /* TDM TX Control 5 (FLAG) */
+#define CS35L33_TX_EN1 0x32 /* TDM TX Enable 1 */
+#define CS35L33_TX_EN2 0x33 /* TDM TX Enable 2 */
+#define CS35L33_TX_EN3 0x34 /* TDM TX Enable 3 */
+#define CS35L33_TX_EN4 0x35 /* TDM TX Enable 4 */
+#define CS35L33_RX_AUD 0x36 /* TDM RX Control 1 */
+#define CS35L33_RX_SPLY 0x37 /* TDM RX Control 2 */
+#define CS35L33_RX_ALIVE 0x38 /* TDM RX Control 3 */
+#define CS35L33_BST_CTL4 0x39 /* Boost Converter Control 4 */
+#define CS35L33_HG_STATUS 0x3F /* H/G Status */
+#define CS35L33_MAX_REGISTER 0x59
+
+#define CS35L33_MCLK_5644 5644800
+#define CS35L33_MCLK_6144 6144000
+#define CS35L33_MCLK_6 6000000
+#define CS35L33_MCLK_11289 11289600
+#define CS35L33_MCLK_12 12000000
+#define CS35L33_MCLK_12288 12288000
+
+/* CS35L33_PWRCTL1 */
+#define CS35L33_PDN_AMP (1 << 7)
+#define CS35L33_PDN_BST (1 << 2)
+#define CS35L33_PDN_ALL 1
+
+/* CS35L33_PWRCTL2 */
+#define CS35L33_PDN_VMON_SHIFT 7
+#define CS35L33_PDN_VMON (1 << CS35L33_PDN_VMON_SHIFT)
+#define CS35L33_PDN_IMON_SHIFT 6
+#define CS35L33_PDN_IMON (1 << CS35L33_PDN_IMON_SHIFT)
+#define CS35L33_PDN_VPMON_SHIFT 5
+#define CS35L33_PDN_VPMON (1 << CS35L33_PDN_VPMON_SHIFT)
+#define CS35L33_PDN_VBSTMON_SHIFT 4
+#define CS35L33_PDN_VBSTMON (1 << CS35L33_PDN_VBSTMON_SHIFT)
+#define CS35L33_SDOUT_3ST_I2S_SHIFT 3
+#define CS35L33_SDOUT_3ST_I2S (1 << CS35L33_SDOUT_3ST_I2S_SHIFT)
+#define CS35L33_PDN_SDIN_SHIFT 2
+#define CS35L33_PDN_SDIN (1 << CS35L33_PDN_SDIN_SHIFT)
+#define CS35L33_PDN_TDM_SHIFT 1
+#define CS35L33_PDN_TDM (1 << CS35L33_PDN_TDM_SHIFT)
+
+/* CS35L33_CLK_CTL */
+#define CS35L33_MCLKDIS (1 << 7)
+#define CS35L33_MCLKDIV2 (1 << 6)
+#define CS35L33_SDOUT_3ST_TDM (1 << 5)
+#define CS35L33_INT_FS_RATE (1 << 4)
+#define CS35L33_ADSP_FS 0xF
+
+/* CS35L33_PROTECT_CTL */
+#define CS35L33_ALIVE_WD_DIS (3 << 2)
+
+/* CS35L33_BST_CTL1 */
+#define CS35L33_BST_CTL_SRC (1 << 6)
+#define CS35L33_BST_CTL_SHIFT (1 << 5)
+#define CS35L33_BST_CTL_MASK 0x3F
+
+/* CS35L33_BST_CTL2 */
+#define CS35L33_TDM_WD_SEL (1 << 4)
+#define CS35L33_ALIVE_WD_DIS2 (1 << 3)
+#define CS35L33_VBST_SR_STEP 0x3
+
+/* CS35L33_ADSP_CTL */
+#define CS35L33_ADSP_DRIVE (1 << 7)
+#define CS35L33_MS_MASK (1 << 6)
+#define CS35L33_SDIN_LOC (3 << 4)
+#define CS35L33_ALIVE_RATE 0x3
+
+/* CS35L33_ADC_CTL */
+#define CS35L33_INV_VMON (1 << 7)
+#define CS35L33_INV_IMON (1 << 6)
+#define CS35L33_ADC_NOTCH_DIS (1 << 5)
+#define CS35L33_IMON_SCALE 0xF
+
+/* CS35L33_DAC_CTL */
+#define CS35L33_INV_DAC (1 << 7)
+#define CS35L33_DAC_NOTCH_DIS (1 << 5)
+#define CS35L33_DIGSFT (1 << 4)
+#define CS35L33_DSR_RATE 0xF
+
+/* CS35L33_CLASSD_CTL */
+#define CS35L33_AMP_SD (1 << 6)
+#define CS35L33_AMP_DRV_SEL_SRC (1 << 5)
+#define CS35L33_AMP_DRV_SEL_MASK 0x10
+#define CS35L33_AMP_DRV_SEL_SHIFT 4
+#define CS35L33_AMP_CAL (1 << 3)
+#define CS35L33_GAIN_CHG_ZC_MASK 0x04
+#define CS35L33_GAIN_CHG_ZC_SHIFT 2
+#define CS35L33_CLASS_D_CTL_MASK 0x3F
+
+/* CS35L33_AMP_CTL */
+#define CS35L33_AMP_GAIN 0xF0
+#define CS35L33_CAL_ERR_RLS (1 << 3)
+#define CS35L33_AMP_SHORT_RLS (1 << 2)
+#define CS35L33_OTW_RLS (1 << 1)
+#define CS35L33_OTE_RLS 1
+
+/* CS35L33_INT_MASK_1 */
+#define CS35L33_M_CAL_ERR_SHIFT 6
+#define CS35L33_M_CAL_ERR (1 << CS35L33_M_CAL_ERR_SHIFT)
+#define CS35L33_M_ALIVE_ERR_SHIFT 5
+#define CS35L33_M_ALIVE_ERR (1 << CS35L33_M_ALIVE_ERR_SHIFT)
+#define CS35L33_M_AMP_SHORT_SHIFT 2
+#define CS35L33_M_AMP_SHORT (1 << CS35L33_M_AMP_SHORT_SHIFT)
+#define CS35L33_M_OTW_SHIFT 1
+#define CS35L33_M_OTW (1 << CS35L33_M_OTW_SHIFT)
+#define CS35L33_M_OTE_SHIFT 0
+#define CS35L33_M_OTE (1 << CS35L33_M_OTE_SHIFT)
+
+/* CS35L33_INT_STATUS_1 */
+#define CS35L33_CAL_ERR (1 << 6)
+#define CS35L33_ALIVE_ERR (1 << 5)
+#define CS35L33_ADSPCLK_ERR (1 << 4)
+#define CS35L33_MCLK_ERR (1 << 3)
+#define CS35L33_AMP_SHORT (1 << 2)
+#define CS35L33_OTW (1 << 1)
+#define CS35L33_OTE (1 << 0)
+
+/* CS35L33_INT_STATUS_2 */
+#define CS35L33_VMON_OVFL (1 << 7)
+#define CS35L33_IMON_OVFL (1 << 6)
+#define CS35L33_VPMON_OVFL (1 << 5)
+#define CS35L33_VBSTMON_OVFL (1 << 4)
+#define CS35L33_PDN_DONE 1
+
+/* CS35L33_BST_CTL4 */
+#define CS35L33_BST_RGS 0x70
+#define CS35L33_BST_COEFF3 0xF
+
+/* CS35L33_HG_MEMLDO_CTL */
+#define CS35L33_MEM_DEPTH_SHIFT 5
+#define CS35L33_MEM_DEPTH_MASK (0x3 << CS35L33_MEM_DEPTH_SHIFT)
+#define CS35L33_LDO_THLD_SHIFT 1
+#define CS35L33_LDO_THLD_MASK (0xF << CS35L33_LDO_THLD_SHIFT)
+#define CS35L33_LDO_DISABLE_SHIFT 0
+#define CS35L33_LDO_DISABLE_MASK (0x1 << CS35L33_LDO_DISABLE_SHIFT)
+
+/* CS35L33_LDO_DEL */
+#define CS35L33_VP_HG_VA_SHIFT 5
+#define CS35L33_VP_HG_VA_MASK (0x7 << CS35L33_VP_HG_VA_SHIFT)
+#define CS35L33_LDO_ENTRY_DELAY_SHIFT 2
+#define CS35L33_LDO_ENTRY_DELAY_MASK (0x7 << CS35L33_LDO_ENTRY_DELAY_SHIFT)
+#define CS35L33_VP_HG_RATE_SHIFT 0
+#define CS35L33_VP_HG_RATE_MASK (0x3 << CS35L33_VP_HG_RATE_SHIFT)
+
+/* CS35L33_HG_HEAD */
+#define CS35L33_HD_RM_SHIFT 0
+#define CS35L33_HD_RM_MASK (0x7F << CS35L33_HD_RM_SHIFT)
+
+/* CS35L33_HG_EN */
+#define CS35L33_CLASS_HG_ENA_SHIFT 7
+#define CS35L33_CLASS_HG_EN_MASK (0x1 << CS35L33_CLASS_HG_ENA_SHIFT)
+#define CS35L33_VP_HG_AUTO_SHIFT 6
+#define CS35L33_VP_HG_AUTO_MASK (0x1 << 6)
+#define CS35L33_VP_HG_SHIFT 0
+#define CS35L33_VP_HG_MASK (0x1F << CS35L33_VP_HG_SHIFT)
+
+#define CS35L33_RATES (SNDRV_PCM_RATE_8000_48000)
+#define CS35L33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+/* CS35L33_{RX,TX}_X */
+#define CS35L33_X_STATE_SHIFT 7
+#define CS35L33_X_STATE (1 << CS35L33_X_STATE_SHIFT)
+#define CS35L33_X_LOC_SHIFT 0
+#define CS35L33_X_LOC (0x1F << CS35L33_X_LOC_SHIFT)
+
+/* CS35L33_RX_AUD */
+#define CS35L33_AUDIN_RX_DEPTH_SHIFT 5
+#define CS35L33_AUDIN_RX_DEPTH (0x7 << CS35L33_AUDIN_RX_DEPTH_SHIFT)
+
+#endif
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index 5ec5a682d186..954a4f5d3338 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -359,6 +359,11 @@ SND_SOC_DAPM_INPUT("IN2R"),
SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, arizona_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -899,10 +904,16 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
{ "DRC2 Signal Activity", NULL, "DRC2L" },
{ "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
};
static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1067,6 +1078,7 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{
struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
@@ -1074,6 +1086,12 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
}
if (!serviced) {
@@ -1096,6 +1114,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index d6f4abbbf8a7..fb3885fe0afb 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -226,6 +226,7 @@ static int v253_open(struct tty_struct *tty)
if (!tty->disc_data)
return -ENODEV;
+ tty->receive_room = 16;
if (tty->ops->write(tty, v253_init, len) != len) {
ret = -EIO;
goto err;
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 181cd3bf0b92..4e181b270d95 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1124,8 +1124,10 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
}
hdac_hdmi_parse_eld(edev, pin);
- print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
- pin->eld.eld_buffer, pin->eld.eld_size);
+ print_hex_dump_debug("ELD: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ pin->eld.eld_buffer, pin->eld.eld_size,
+ true);
} else {
pin->eld.monitor_present = false;
pin->eld.eld_valid = false;
@@ -1474,6 +1476,11 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
* exit, we call pm_runtime_suspend() so that will do for us
*/
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+ if (!hlink) {
+ dev_err(&edev->hdac.dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
ret = create_fill_widget_route_map(dapm);
@@ -1634,6 +1641,11 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
/* hold the ref while we probe */
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+ if (!hlink) {
+ dev_err(&edev->hdac.dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
@@ -1744,6 +1756,11 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
}
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_put(ebus, hlink);
return 0;
@@ -1765,6 +1782,11 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
return 0;
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(ebus, hlink);
err = snd_hdac_display_power(bus, true);
@@ -1796,6 +1818,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
static const struct hda_device_id hdmi_list[] = {
HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
+ HDA_CODEC_EXT_ENTRY(0x8086280b, 0x100000, "Kabylake HDMI", 0),
{}
};
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 58325234285c..33e1fc2d1598 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -73,7 +73,7 @@ static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
return !((reg == 0x00) || (reg == 0x0f));
}
-static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm1681_writeable_reg(struct device *dev, unsigned int reg)
{
return pcm1681_accessible_reg(dev, reg) &&
(reg != PCM1681_ZERO_DETECT_STATUS);
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index 06a66579ca6d..88fbdd184aa0 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -59,7 +59,7 @@ static bool pcm179x_accessible_reg(struct device *dev, unsigned int reg)
return reg >= 0x10 && reg <= 0x17;
}
-static bool pcm179x_writeable_reg(struct device *dev, unsigned register reg)
+static bool pcm179x_writeable_reg(struct device *dev, unsigned int reg)
{
bool accessible;
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
index ed515677409b..8ba322a00363 100644
--- a/sound/soc/codecs/pcm5102a.c
+++ b/sound/soc/codecs/pcm5102a.c
@@ -57,7 +57,6 @@ static struct platform_driver pcm5102a_codec_driver = {
.remove = pcm5102a_remove,
.driver = {
.name = "pcm5102a-codec",
- .owner = THIS_MODULE,
.of_match_table = pcm5102a_of_match,
},
};
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 1bd31644a782..74c0e4eb3788 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1100,6 +1100,13 @@ static const struct dmi_system_id force_combo_jack_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Skylake Client platform")
}
},
+ {
+ .ident = "Intel Kabylake RVP",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Kabylake Client platform")
+ }
+ },
+
{ }
};
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 3c6594da6c9c..490bfe661346 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -63,6 +63,7 @@ static const struct reg_sequence init_list[] = {
{RT5645_PR_BASE + 0x20, 0x611f},
{RT5645_PR_BASE + 0x21, 0x4040},
{RT5645_PR_BASE + 0x23, 0x0004},
+ {RT5645_ASRC_4, 0x0120},
};
static const struct reg_sequence rt5650_init_list[] = {
@@ -157,7 +158,7 @@ static const struct reg_default rt5645_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -253,7 +254,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x2b, 0x5454 },
{ 0x2c, 0xaaa0 },
{ 0x2d, 0x0000 },
- { 0x2f, 0x1002 },
+ { 0x2f, 0x5002 },
{ 0x31, 0x5000 },
{ 0x32, 0x0000 },
{ 0x33, 0x0000 },
@@ -314,7 +315,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -440,6 +441,7 @@ static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case RT5645_RESET:
+ case RT5645_PRIV_INDEX:
case RT5645_PRIV_DATA:
case RT5645_IN1_CTRL1:
case RT5645_IN1_CTRL2:
@@ -740,6 +742,14 @@ static int rt5645_spk_put_volsw(struct snd_kcontrol *kcontrol,
return ret;
}
+static const char * const rt5645_dac1_vol_ctrl_mode_text[] = {
+ "immediately", "zero crossing", "soft ramp"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt5645_dac1_vol_ctrl_mode, RT5645_PR_BASE,
+ RT5645_DA1_ZDET_SFT, rt5645_dac1_vol_ctrl_mode_text);
+
static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* Speaker Output Volume */
SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -806,6 +816,9 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
1, 1),
RT5645_HWEQ("Speaker HWEQ"),
+
+ /* Digital Soft Volume Control */
+ SOC_ENUM("DAC1 Digital Volume Control Func", rt5645_dac1_vol_ctrl_mode),
};
/**
@@ -3531,6 +3544,7 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
static const struct acpi_device_id rt5645_acpi_match[] = {
{ "10EC5645", 0 },
{ "10EC5650", 0 },
+ { "10EC5640", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
@@ -3561,6 +3575,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
},
},
+ {
+ .ident = "Microsoft Surface 3",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ },
+ },
{ }
};
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 205e0715c99a..cfc5f97549eb 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -2018,6 +2018,9 @@
/* Codec Private Register definition */
+/* DAC ADC Digital Volume (0x00) */
+#define RT5645_DA1_ZDET_SFT 6
+
/* 3D Speaker Control (0x63) */
#define RT5645_3D_SPK_MASK (0x1 << 15)
#define RT5645_3D_SPK_SFT 15
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 49a9e7049e2b..0af5ddbef1da 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -619,7 +619,7 @@ static const struct snd_kcontrol_new rt5670_snd_controls[] = {
RT5670_L_MUTE_SFT, RT5670_R_MUTE_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5670_HP_VOL,
RT5670_L_VOL_SFT, RT5670_R_VOL_SFT,
- 39, 0, out_vol_tlv),
+ 39, 1, out_vol_tlv),
/* OUTPUT Control */
SOC_DOUBLE("OUT Channel Switch", RT5670_LOUT1,
RT5670_VOL_L_SFT, RT5670_VOL_R_SFT, 1, 1),
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index da60e3fe5ee7..846deed6af41 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1713,6 +1713,7 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
@@ -1872,7 +1873,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.capture = {
.stream_name = "Audio Trace CPU",
.channels_min = 1,
- .channels_max = 6,
+ .channels_max = 4,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index b5820e4d5471..156547026a40 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1104,6 +1104,11 @@ SND_SOC_DAPM_INPUT("IN4R"),
SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DSP Voice Trigger"),
+
+SND_SOC_DAPM_SWITCH("DSP3 Voice Trigger", SND_SOC_NOPM, 2, 0,
+ &arizona_voice_trigger_switch[2]),
+
SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
0, NULL, 0, wm5110_in_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -1723,6 +1728,7 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "OUT2L", NULL, "SYSCLK" },
{ "OUT2R", NULL, "SYSCLK" },
{ "OUT3L", NULL, "SYSCLK" },
+ { "OUT3R", NULL, "SYSCLK" },
{ "OUT4L", NULL, "SYSCLK" },
{ "OUT4R", NULL, "SYSCLK" },
{ "OUT5L", NULL, "SYSCLK" },
@@ -1997,10 +2003,16 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
+ { "DRC2 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
{ "DRC2 Signal Activity", NULL, "DRC2L" },
{ "DRC2 Signal Activity", NULL, "DRC2R" },
+
+ { "DSP Voice Trigger", NULL, "SYSCLK" },
+ { "DSP Voice Trigger", NULL, "DSP3 Voice Trigger" },
+ { "DSP3 Voice Trigger", "Switch", "DSP3" },
};
static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -2222,6 +2234,7 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
{
struct wm5110_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
@@ -2229,6 +2242,12 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
if (ret != -ENODEV)
serviced++;
+ if (ret == WM_ADSP_COMPR_VOICE_TRIGGER) {
+ info.core = i;
+ arizona_call_notifiers(arizona,
+ ARIZONA_NOTIFY_VOICE_TRIGGER,
+ &info);
+ }
}
if (!serviced) {
@@ -2251,6 +2270,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index f6f9395ea38e..1c600819f768 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -743,6 +743,7 @@ static const struct regmap_config wm8940_regmap = {
.max_register = WM8940_MONOMIX,
.reg_defaults = wm8940_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm8940_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
.readable_reg = wm8940_readable_register,
.volatile_reg = wm8940_volatile_register,
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 449f66636205..3a5c896a2d13 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -1166,6 +1166,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
{ "MICSUPP", NULL, "SYSCLK" },
+ { "DRC1 Signal Activity", NULL, "SYSCLK" },
{ "DRC1 Signal Activity", NULL, "DRC1L" },
{ "DRC1 Signal Activity", NULL, "DRC1R" },
};
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index a07bd7c2c587..21fbe7d07063 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -394,6 +394,7 @@ static const struct {
int compr_direction;
int num_caps;
const struct wm_adsp_fw_caps *caps;
+ bool voice_trigger;
} wm_adsp_fw[WM_ADSP_NUM_FW] = {
[WM_ADSP_FW_MBC_VSS] = { .file = "mbc-vss" },
[WM_ADSP_FW_HIFI] = { .file = "hifi" },
@@ -406,6 +407,7 @@ static const struct {
.compr_direction = SND_COMPRESS_CAPTURE,
.num_caps = ARRAY_SIZE(ctrl_caps),
.caps = ctrl_caps,
+ .voice_trigger = true,
},
[WM_ADSP_FW_ASR] = { .file = "asr" },
[WM_ADSP_FW_TRACE] = {
@@ -2366,13 +2368,15 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA |
- ADSP2_START, 0);
+ ADSP2_CORE_ENA | ADSP2_START, 0);
/* Make sure DMAs are quiesced */
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
- regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
list_for_each_entry(ctl, &dsp->ctl_list, list)
ctl->enabled = 0;
@@ -2998,6 +3002,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
goto out;
}
+ if (wm_adsp_fw[dsp->fw].voice_trigger && buf->irq_count == 2)
+ ret = WM_ADSP_COMPR_VOICE_TRIGGER;
+
out_notify:
if (compr && compr->stream)
snd_compr_fragment_elapsed(compr->stream);
@@ -3037,12 +3044,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
buf = compr->buf;
- if (!compr->buf) {
- ret = -ENXIO;
- goto out;
- }
-
- if (compr->buf->error) {
+ if (!compr->buf || compr->buf->error) {
+ snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
ret = -EIO;
goto out;
}
@@ -3060,8 +3063,12 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
*/
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
ret = wm_adsp_buffer_get_error(buf);
- if (ret < 0)
+ if (ret < 0) {
+ if (compr->buf->error)
+ snd_compr_stop_error(stream,
+ SNDRV_PCM_STATE_XRUN);
goto out;
+ }
ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) {
@@ -3156,11 +3163,10 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
- if (!compr->buf)
- return -ENXIO;
-
- if (compr->buf->error)
+ if (!compr->buf || compr->buf->error) {
+ snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
return -EIO;
+ }
count /= WM_ADSP_DATA_WORD_SIZE;
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index feb61e2c4bb4..be3b5bcb7f17 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -19,6 +19,10 @@
#include "wmfw.h"
+/* Return values for wm_adsp_compr_handle_irq */
+#define WM_ADSP_COMPR_OK 0
+#define WM_ADSP_COMPR_VOICE_TRIGGER 1
+
struct wm_adsp_region {
int type;
unsigned int base;
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 0f66fda2c772..237dc67002ef 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -1513,8 +1513,9 @@ static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
};
static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
- .tx_dma_offset = 0x200,
- .rx_dma_offset = 0x284,
+ /* The CFG port offset will be calculated if it is needed */
+ .tx_dma_offset = 0,
+ .rx_dma_offset = 0,
.version = MCASP_VERSION_4,
};
@@ -1734,6 +1735,52 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
return PCM_EDMA;
}
+static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->tx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == TX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_TXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
+static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->rx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == RX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_RXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1862,7 +1909,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (dat)
dma_data->addr = dat->start;
else
- dma_data->addr = mem->start + pdata->tx_dma_offset;
+ dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata);
dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -1883,7 +1930,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (dat)
dma_data->addr = dat->start;
else
- dma_data->addr = mem->start + pdata->rx_dma_offset;
+ dma_data->addr =
+ mem->start + davinci_mcasp_rxdma_offset(pdata);
dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 1e8787fb3fb7..afddc8010c54 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -85,9 +85,9 @@
(n << 2))
/* Transmit Buffer for Serializer n */
-#define DAVINCI_MCASP_TXBUF_REG 0x200
+#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2))
/* Receive Buffer for Serializer n */
-#define DAVINCI_MCASP_RXBUF_REG 0x280
+#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2))
/* McASP FIFO Registers */
#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 632ecc0e3956..bedec4a32581 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -952,16 +952,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
+ regmap_update_bits(regs, CCSR_SSI_STCCR,
+ CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(2));
+ regmap_update_bits(regs, CCSR_SSI_SRCCR,
+ CCSR_SSI_SxCCR_DC_MASK,
+ CCSR_SSI_SxCCR_DC(2));
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFS:
case SND_SOC_DAIFMT_CBS_CFS:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_MASTER;
- regmap_update_bits(regs, CCSR_SSI_STCCR,
- CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
- regmap_update_bits(regs, CCSR_SSI_SRCCR,
- CCSR_SSI_SxCCR_DC_MASK,
- CCSR_SSI_SxCCR_DC(2));
break;
case SND_SOC_DAIFMT_CBM_CFM:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 91c15abb625e..a20c3dfbcb5d 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -7,7 +7,7 @@ config SND_MFLD_MACHINE
help
This adds support for ASoC machine driver for Intel(R) MID Medfield platform
used as alsa device in audio substem in Intel(R) MID devices
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SST_MFLD_PLATFORM
@@ -25,7 +25,6 @@ config SND_SST_IPC_ACPI
tristate
select SND_SST_IPC
select SND_SOC_INTEL_SST
- depends on ACPI
config SND_SOC_INTEL_SST
tristate
@@ -33,6 +32,12 @@ config SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_MATCH if ACPI
depends on (X86 || COMPILE_TEST)
+# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from
+# the reverse selection, each machine driver needs to select
+# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE
+config SND_SOC_INTEL_SST_FIRMWARE
+ tristate
+
config SND_SOC_INTEL_SST_ACPI
tristate
@@ -48,16 +53,33 @@ config SND_SOC_INTEL_BAYTRAIL
config SND_SOC_INTEL_HASWELL_MACH
tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
depends on X86_INTEL_LPSS && I2C && I2C_DESIGNWARE_PLATFORM
- depends on DW_DMAC_CORE=y
+ depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT5640
help
This adds support for the Lynxpoint Audio DSP on Intel(R) Haswell
Ultrabook platforms.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
+config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
+ tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode"
+ depends on X86 && ACPI && I2C
+ select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SKYLAKE
+ select SND_SOC_DA7219
+ select SND_SOC_MAX98357A
+ select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
+ select SND_HDA_DSP_LOADER
+ help
+ This adds support for ASoC machine driver for Broxton-P platforms
+ with DA7219 + MAX98357A I2S audio codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
config SND_SOC_INTEL_BXT_RT298_MACH
tristate "ASoC Audio driver for Broxton with RT298 I2S mode"
depends on X86 && ACPI && I2C
@@ -70,26 +92,28 @@ config SND_SOC_INTEL_BXT_RT298_MACH
help
This adds support for ASoC machine driver for Broxton platforms
with RT286 I2S audio codec.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BYT_RT5640_MACH
tristate "ASoC Audio driver for Intel Baytrail with RT5640 codec"
depends on X86_INTEL_LPSS && I2C
- depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n)
+ depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_RT5640
help
This adds audio driver for Intel Baytrail platform based boards
with the RT5640 audio codec. This driver is deprecated, use
- SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality
+ SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality.
config SND_SOC_INTEL_BYT_MAX98090_MACH
tristate "ASoC Audio driver for Intel Baytrail with MAX98090 codec"
depends on X86_INTEL_LPSS && I2C
- depends on DW_DMAC_CORE=y && (SND_SST_IPC_ACPI = n)
+ depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_BAYTRAIL
select SND_SOC_MAX98090
help
@@ -100,19 +124,20 @@ config SND_SOC_INTEL_BROADWELL_MACH
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
depends on X86_INTEL_LPSS && I2C && DW_DMAC && \
I2C_DESIGNWARE_PLATFORM
- depends on DW_DMAC_CORE=y
+ depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT286
help
This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
Ultrabook platforms.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BYTCR_RT5640_MACH
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5640 codec"
- depends on X86 && I2C
+ depends on X86 && I2C && ACPI
select SND_SOC_RT5640
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -120,12 +145,12 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH
help
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
platforms with RT5640 audio codec.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_BYTCR_RT5651_MACH
tristate "ASoC Audio driver for Intel Baytrail and Baytrail-CR with RT5651 codec"
- depends on X86 && I2C
+ depends on X86 && I2C && ACPI
select SND_SOC_RT5651
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -133,12 +158,12 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
help
This adds support for ASoC machine driver for Intel(R) Baytrail and Baytrail-CR
platforms with RT5651 audio codec.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
- depends on X86_INTEL_LPSS && I2C
+ depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_RT5670
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -146,12 +171,12 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
help
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
platforms with RT5672 audio codec.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645/5650 codec"
- depends on X86_INTEL_LPSS && I2C
+ depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_RT5645
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -163,16 +188,16 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with MAX98090 & TI codec"
- depends on X86_INTEL_LPSS && I2C
+ depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
select SND_SOC_INTEL_SST_MATCH if ACPI
help
- This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
- platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
- If unsure select "N".
+ This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+ platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
+ If unsure select "N".
config SND_SOC_INTEL_SKYLAKE
tristate
@@ -192,7 +217,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
help
This adds support for ASoC machine driver for Skylake platforms
with RT286 I2S audio codec.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
@@ -207,7 +232,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + SSM4567.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
@@ -222,5 +247,5 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
help
This adds support for ASoC Onboard Codec I2S machine driver. This will
create an alsa sound card for NAU88L25 + MAX98357A.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c
index 395168986462..1bead81bb510 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c
@@ -182,24 +182,29 @@ static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
case SNDRV_PCM_TRIGGER_START:
if (stream->compr_ops->stream_start)
return stream->compr_ops->stream_start(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_STOP:
if (stream->compr_ops->stream_drop)
return stream->compr_ops->stream_drop(sst->dev, stream->id);
+ break;
case SND_COMPR_TRIGGER_DRAIN:
if (stream->compr_ops->stream_drain)
return stream->compr_ops->stream_drain(sst->dev, stream->id);
+ break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
if (stream->compr_ops->stream_partial_drain)
return stream->compr_ops->stream_partial_drain(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (stream->compr_ops->stream_pause)
return stream->compr_ops->stream_pause(sst->dev, stream->id);
+ break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (stream->compr_ops->stream_pause_release)
return stream->compr_ops->stream_pause_release(sst->dev, stream->id);
- default:
- return -EINVAL;
+ break;
}
+ return -EINVAL;
}
static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 3bc4b63b2f9d..4d3184971227 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -28,6 +28,7 @@
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
+#include <linux/dmi.h>
#include <linux/acpi.h>
#include <asm/platform_sst_audio.h>
#include <sound/core.h>
@@ -237,6 +238,9 @@ static int sst_acpi_probe(struct platform_device *pdev)
dev_err(dev, "No matching machine driver found\n");
return -ENODEV;
}
+ if (mach->machine_quirk)
+ mach = mach->machine_quirk(mach);
+
pdata = mach->pdata;
ret = kstrtouint(id->id, 16, &dev_id);
@@ -320,6 +324,44 @@ static int sst_acpi_remove(struct platform_device *pdev)
return 0;
}
+static unsigned long cht_machine_id;
+
+#define CHT_SURFACE_MACH 1
+
+static int cht_surface_quirk_cb(const struct dmi_system_id *id)
+{
+ cht_machine_id = CHT_SURFACE_MACH;
+ return 1;
+}
+
+
+static const struct dmi_system_id cht_table[] = {
+ {
+ .callback = cht_surface_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ },
+ },
+};
+
+
+static struct sst_acpi_mach cht_surface_mach = {
+ "10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ &chv_platform_data };
+
+static struct sst_acpi_mach *cht_quirk(void *arg)
+{
+ struct sst_acpi_mach *mach = arg;
+
+ dmi_check_system(cht_table);
+
+ if (cht_machine_id == CHT_SURFACE_MACH)
+ return &cht_surface_mach;
+ else
+ return mach;
+}
+
static struct sst_acpi_mach sst_acpi_bytcr[] = {
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
&byt_rvp_platform_data },
@@ -343,7 +385,7 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
- {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
+ {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
&chv_platform_data },
{},
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index a8506774f510..dac03a06bfd8 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -2,6 +2,7 @@ snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
@@ -15,6 +16,7 @@ snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
+obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
new file mode 100644
index 000000000000..3774b117d365
--- /dev/null
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -0,0 +1,460 @@
+/*
+ * Intel Broxton-P I2S Machine Driver
+ *
+ * Copyright (C) 2016, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ * Intel Skylake I2S Machine driver
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../codecs/hdac_hdmi.h"
+#include "../../codecs/da7219.h"
+#include "../../codecs/da7219-aad.h"
+
+#define BXT_DIALOG_CODEC_DAI "da7219-hifi"
+#define BXT_MAXIM_CODEC_DAI "HiFi"
+#define DUAL_CHANNEL 2
+
+static struct snd_soc_jack broxton_headset;
+
+enum {
+ BXT_DPCM_AUDIO_PB = 0,
+ BXT_DPCM_AUDIO_CP,
+ BXT_DPCM_AUDIO_REF_CP,
+ BXT_DPCM_AUDIO_HDMI1_PB,
+ BXT_DPCM_AUDIO_HDMI2_PB,
+ BXT_DPCM_AUDIO_HDMI3_PB,
+};
+
+static const struct snd_kcontrol_new broxton_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Spk"),
+};
+
+static const struct snd_soc_dapm_widget broxton_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_SPK("Spk", NULL),
+ SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+ SND_SOC_DAPM_SPK("HDMI1", NULL),
+ SND_SOC_DAPM_SPK("HDMI2", NULL),
+ SND_SOC_DAPM_SPK("HDMI3", NULL),
+};
+
+static const struct snd_soc_dapm_route broxton_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ /* speaker */
+ {"Spk", NULL, "Speaker"},
+
+ /* other jacks */
+ {"MIC", NULL, "Headset Mic"},
+
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+
+ /* CODEC BE connections */
+ {"HiFi Playback", NULL, "ssp5 Tx"},
+ {"ssp5 Tx", NULL, "codec0_out"},
+
+ {"Playback", NULL, "ssp1 Tx"},
+ {"ssp1 Tx", NULL, "codec1_out"},
+
+ {"codec0_in", NULL, "ssp1 Rx"},
+ {"ssp1 Rx", NULL, "Capture"},
+
+ {"HDMI1", NULL, "hif5 Output"},
+ {"HDMI2", NULL, "hif6 Output"},
+ {"HDMI3", NULL, "hif7 Output"},
+
+ {"hifi3", NULL, "iDisp3 Tx"},
+ {"iDisp3 Tx", NULL, "iDisp3_out"},
+ {"hifi2", NULL, "iDisp2 Tx"},
+ {"iDisp2 Tx", NULL, "iDisp2_out"},
+ {"hifi1", NULL, "iDisp1 Tx"},
+ {"iDisp1 Tx", NULL, "iDisp1_out"},
+
+ /* DMIC */
+ {"dmic01_hifi", NULL, "DMIC01 Rx"},
+ {"DMIC01 Rx", NULL, "DMIC AIF"},
+};
+
+static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* The ADSP will convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = DUAL_CHANNEL;
+
+ /* set SSP to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int broxton_da7219_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int ret;
+ struct snd_soc_codec *codec = rtd->codec;
+
+ /*
+ * Headset buttons map to the google Reference headset.
+ * These can be configured by userspace.
+ */
+ ret = snd_soc_card_jack_new(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset,
+ NULL, 0);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ da7219_aad_jack_det(codec, &broxton_headset);
+
+ snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
+
+ return ret;
+}
+
+static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
+}
+
+static int broxton_da7219_fe_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_component *component = rtd->cpu_dai->component;
+
+ dapm = snd_soc_component_get_dapm(component);
+ snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
+
+ return 0;
+}
+
+static unsigned int rates[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static unsigned int channels[] = {
+ DUAL_CHANNEL,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int bxt_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /*
+ * On this platform for PCM device we support,
+ * 48Khz
+ * stereo
+ * 16 bit audio
+ */
+
+ runtime->hw.channels_max = DUAL_CHANNEL;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops broxton_da7219_fe_ops = {
+ .startup = bxt_fe_startup,
+};
+
+static int broxton_da7219_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static int broxton_da7219_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops broxton_da7219_ops = {
+ .hw_params = broxton_da7219_hw_params,
+ .hw_free = broxton_da7219_hw_free,
+};
+
+/* broxton digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broxton_dais[] = {
+ /* Front End DAI links */
+ [BXT_DPCM_AUDIO_PB]
+ {
+ .name = "Bxt Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:0e.0",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .nonatomic = 1,
+ .init = broxton_da7219_fe_init,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ .ops = &broxton_da7219_fe_ops,
+ },
+ [BXT_DPCM_AUDIO_CP]
+ {
+ .name = "Bxt Audio Capture Port",
+ .stream_name = "Audio Record",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:0e.0",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .nonatomic = 1,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_capture = 1,
+ .ops = &broxton_da7219_fe_ops,
+ },
+ [BXT_DPCM_AUDIO_REF_CP]
+ {
+ .name = "Bxt Audio Reference cap",
+ .stream_name = "Refcap",
+ .cpu_dai_name = "Reference Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [BXT_DPCM_AUDIO_HDMI1_PB]
+ {
+ .name = "Bxt HDMI Port1",
+ .stream_name = "Hdmi1",
+ .cpu_dai_name = "HDMI1 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [BXT_DPCM_AUDIO_HDMI2_PB]
+ {
+ .name = "Bxt HDMI Port2",
+ .stream_name = "Hdmi2",
+ .cpu_dai_name = "HDMI2 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [BXT_DPCM_AUDIO_HDMI3_PB]
+ {
+ .name = "Bxt HDMI Port3",
+ .stream_name = "Hdmi3",
+ .cpu_dai_name = "HDMI3 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ /* Back End DAI links */
+ {
+ /* SSP5 - Codec */
+ .name = "SSP5-Codec",
+ .id = 0,
+ .cpu_dai_name = "SSP5 Pin",
+ .platform_name = "0000:00:0e.0",
+ .no_pcm = 1,
+ .codec_name = "MX98357A:00",
+ .codec_dai_name = BXT_MAXIM_CODEC_DAI,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = broxton_ssp_fixup,
+ .dpcm_playback = 1,
+ },
+ {
+ /* SSP1 - Codec */
+ .name = "SSP1-Codec",
+ .id = 1,
+ .cpu_dai_name = "SSP1 Pin",
+ .platform_name = "0000:00:0e.0",
+ .no_pcm = 1,
+ .codec_name = "i2c-DLGS7219:00",
+ .codec_dai_name = BXT_DIALOG_CODEC_DAI,
+ .init = broxton_da7219_codec_init,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = broxton_ssp_fixup,
+ .ops = &broxton_da7219_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "dmic01",
+ .id = 2,
+ .cpu_dai_name = "DMIC01 Pin",
+ .codec_name = "dmic-codec",
+ .codec_dai_name = "dmic-hifi",
+ .platform_name = "0000:00:0e.0",
+ .ignore_suspend = 1,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp1",
+ .id = 3,
+ .cpu_dai_name = "iDisp1 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi1",
+ .platform_name = "0000:00:0e.0",
+ .init = broxton_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp2",
+ .id = 4,
+ .cpu_dai_name = "iDisp2 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi2",
+ .platform_name = "0000:00:0e.0",
+ .init = broxton_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp3",
+ .id = 5,
+ .cpu_dai_name = "iDisp3 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi3",
+ .platform_name = "0000:00:0e.0",
+ .init = broxton_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+};
+
+/* broxton audio machine driver for SPT + da7219 */
+static struct snd_soc_card broxton_audio_card = {
+ .name = "bxtda7219max",
+ .owner = THIS_MODULE,
+ .dai_link = broxton_dais,
+ .num_links = ARRAY_SIZE(broxton_dais),
+ .controls = broxton_controls,
+ .num_controls = ARRAY_SIZE(broxton_controls),
+ .dapm_widgets = broxton_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
+ .dapm_routes = broxton_map,
+ .num_dapm_routes = ARRAY_SIZE(broxton_map),
+ .fully_routed = true,
+};
+
+static int broxton_audio_probe(struct platform_device *pdev)
+{
+ broxton_audio_card.dev = &pdev->dev;
+ return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);
+}
+
+static struct platform_driver broxton_audio = {
+ .probe = broxton_audio_probe,
+ .driver = {
+ .name = "bxt_da7219_max98357a_i2s",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(broxton_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("Audio Machine driver-DA7219 & MAX98357A in I2S mode");
+MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>");
+MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>");
+MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
+MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s");
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index f4787515c0ed..253d7bfbf511 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -33,6 +33,7 @@ enum {
BXT_DPCM_AUDIO_PB = 0,
BXT_DPCM_AUDIO_CP,
BXT_DPCM_AUDIO_REF_CP,
+ BXT_DPCM_AUDIO_DMIC_CP,
BXT_DPCM_AUDIO_HDMI1_PB,
BXT_DPCM_AUDIO_HDMI2_PB,
BXT_DPCM_AUDIO_HDMI3_PB,
@@ -88,6 +89,7 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
/* CODEC BE connections */
{ "AIF1 Playback", NULL, "ssp5 Tx"},
{ "ssp5 Tx", NULL, "codec0_out"},
+ { "ssp5 Tx", NULL, "codec1_out"},
{ "codec0_in", NULL, "ssp5 Rx" },
{ "ssp5 Rx", NULL, "AIF1 Capture" },
@@ -104,6 +106,17 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
};
+static int broxton_rt298_fe_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_component *component = rtd->cpu_dai->component;
+
+ dapm = snd_soc_component_get_dapm(component);
+ snd_soc_dapm_ignore_suspend(dapm, "Reference Capture");
+
+ return 0;
+}
+
static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
@@ -118,6 +131,9 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret;
rt298_mic_detect(codec, &broxton_headset);
+
+ snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
+
return 0;
}
@@ -169,6 +185,89 @@ static struct snd_soc_ops broxton_rt298_ops = {
.hw_params = broxton_rt298_hw_params,
};
+static unsigned int rates[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int broxton_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ if (params_channels(params) == 2)
+ channels->min = channels->max = 2;
+ else
+ channels->min = channels->max = 4;
+
+ return 0;
+}
+
+static unsigned int channels_dmic[] = {
+ 2, 4,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
+ .count = ARRAY_SIZE(channels_dmic),
+ .list = channels_dmic,
+ .mask = 0,
+};
+
+static int broxton_dmic_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw.channels_max = 4;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_dmic_channels);
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+}
+
+static struct snd_soc_ops broxton_dmic_ops = {
+ .startup = broxton_dmic_startup,
+};
+
+static unsigned int channels[] = {
+ 2,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int bxt_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /*
+ * on this platform for PCM device we support:
+ * 48Khz
+ * stereo
+ */
+
+ runtime->hw.channels_max = 2;
+ snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_channels);
+
+ snd_pcm_hw_constraint_list(runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+
+ return 0;
+}
+
+static const struct snd_soc_ops broxton_rt286_fe_ops = {
+ .startup = bxt_fe_startup,
+};
+
/* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_rt298_dais[] = {
/* Front End DAI links */
@@ -182,8 +281,10 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
+ .init = broxton_rt298_fe_init,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
+ .ops = &broxton_rt286_fe_ops,
},
[BXT_DPCM_AUDIO_CP]
{
@@ -197,6 +298,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
+ .ops = &broxton_rt286_fe_ops,
},
[BXT_DPCM_AUDIO_REF_CP]
{
@@ -211,6 +313,20 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
+ [BXT_DPCM_AUDIO_DMIC_CP]
+ {
+ .name = "Bxt Audio DMIC cap",
+ .stream_name = "dmiccap",
+ .cpu_dai_name = "DMIC Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ .ops = &broxton_dmic_ops,
+ },
[BXT_DPCM_AUDIO_HDMI1_PB]
{
.name = "Bxt HDMI Port1",
@@ -276,6 +392,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
.platform_name = "0000:00:0e.0",
+ .be_hw_params_fixup = broxton_dmic_fixup,
.ignore_suspend = 1,
.dpcm_capture = 1,
.no_pcm = 1,
@@ -341,6 +458,7 @@ static struct platform_driver broxton_audio = {
.probe = broxton_audio_probe,
.driver = {
.name = "bxt_alc298s_i2s",
+ .pm = &snd_soc_pm_ops,
},
};
module_platform_driver(broxton_audio)
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index d7ef292c402d..56056ed7fcfd 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -30,6 +30,7 @@
#include <sound/jack.h>
#include "../../codecs/rt5645.h"
#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
#define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5645-aif1"
@@ -340,10 +341,13 @@ static struct snd_soc_card snd_soc_card_chtrt5650 = {
};
static struct cht_acpi_card snd_soc_cards[] = {
+ {"10EC5640", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
{"10EC5645", CODEC_TYPE_RT5645, &snd_soc_card_chtrt5645},
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
};
+static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
@@ -351,6 +355,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
char codec_name[16];
+ struct sst_acpi_mach *mach;
+ const char *i2c_name = NULL;
+ int dai_index = 0;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
@@ -366,12 +373,23 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
}
}
card->dev = &pdev->dev;
+ mach = card->dev->platform_data;
sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
- if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00"))
+ if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
+ dai_index = i;
+ }
+
+ /* fixup codec name based on HID */
+ i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ if (i2c_name != NULL) {
+ snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
+ "%s%s", "i2c-", i2c_name);
+ cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
+ }
snd_soc_card_set_drvdata(card, drv);
ret_val = devm_snd_soc_register_card(&pdev->dev, card);
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
index d2808652b974..25db5be7fdfa 100644
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
@@ -23,12 +23,15 @@
#include <sound/soc.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
+#include "../skylake/skl.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_MAXIM_CODEC_DAI "HiFi"
+#define DMIC_CH(p) p->list[p->count-1]
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
+static const struct snd_pcm_hw_constraint_list *dmic_constraints;
struct skl_hdmi_pcm {
struct list_head head;
@@ -339,7 +342,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- if (params_channels(params) == 2)
+ if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
@@ -357,13 +360,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.mask = 0,
};
+static const unsigned int dmic_2ch[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
+ .count = ARRAY_SIZE(dmic_2ch),
+ .list = dmic_2ch,
+ .mask = 0,
+};
+
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.channels_max = 4;
+ runtime->hw.channels_max = DMIC_CH(dmic_constraints);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
+ dmic_constraints);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -382,8 +395,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
.list = rates_16000,
};
+static const unsigned int ch_mono[] = {
+ 1,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_refcap = {
+ .count = ARRAY_SIZE(ch_mono),
+ .list = ch_mono,
+};
+
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
{
+ substream->runtime->hw.channels_max = 1;
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_refcap);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_16000);
@@ -610,6 +637,7 @@ static struct snd_soc_card skylake_audio_card = {
static int skylake_audio_probe(struct platform_device *pdev)
{
struct skl_nau8825_private *ctx;
+ struct skl_machine_pdata *pdata;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
@@ -620,15 +648,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
+ pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata)
+ dmic_constraints = pdata->dmic_num == 2 ?
+ &constraints_dmic_2ch : &constraints_dmic_channels;
+
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
}
+static const struct platform_device_id skl_board_ids[] = {
+ { .name = "skl_n88l25_m98357a" },
+ { .name = "kbl_n88l25_m98357a" },
+ { }
+};
+
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
- .name = "skl_nau88l25_max98357a_i2s",
+ .name = "skl_n88l25_m98357a",
.pm = &snd_soc_pm_ops,
},
+ .id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@@ -637,4 +677,5 @@ module_platform_driver(skylake_audio)
MODULE_DESCRIPTION("Audio Machine driver-NAU88L25 & MAX98357A in I2S mode");
MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_nau88l25_max98357a_i2s");
+MODULE_ALIAS("platform:skl_n88l25_m98357a");
+MODULE_ALIAS("platform:kbl_n88l25_m98357a");
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
index e19aa99c4f72..69c5d5da4e86 100644
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
@@ -27,12 +27,15 @@
#include <sound/pcm_params.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
+#include "../skylake/skl.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
+#define DMIC_CH(p) p->list[p->count-1]
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
+static const struct snd_pcm_hw_constraint_list *dmic_constraints;
struct skl_hdmi_pcm {
struct list_head head;
@@ -367,7 +370,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
{
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- if (params_channels(params) == 2)
+ if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
@@ -405,13 +408,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.mask = 0,
};
+static const unsigned int dmic_2ch[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
+ .count = ARRAY_SIZE(dmic_2ch),
+ .list = dmic_2ch,
+ .mask = 0,
+};
+
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.channels_max = 4;
+ runtime->hw.channels_max = DMIC_CH(dmic_constraints);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
+ dmic_constraints);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -430,8 +443,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
.list = rates_16000,
};
+static const unsigned int ch_mono[] = {
+ 1,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_refcap = {
+ .count = ARRAY_SIZE(ch_mono),
+ .list = ch_mono,
+};
+
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
{
+ substream->runtime->hw.channels_max = 1;
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_refcap);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_16000);
@@ -662,6 +689,7 @@ static struct snd_soc_card skylake_audio_card = {
static int skylake_audio_probe(struct platform_device *pdev)
{
struct skl_nau88125_private *ctx;
+ struct skl_machine_pdata *pdata;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
@@ -672,15 +700,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
+ pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata)
+ dmic_constraints = pdata->dmic_num == 2 ?
+ &constraints_dmic_2ch : &constraints_dmic_channels;
+
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
}
+static const struct platform_device_id skl_board_ids[] = {
+ { .name = "skl_n88l25_s4567" },
+ { .name = "kbl_n88l25_s4567" },
+ { }
+};
+
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
- .name = "skl_nau88l25_ssm4567_i2s",
+ .name = "skl_n88l25_s4567",
.pm = &snd_soc_pm_ops,
},
+ .id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@@ -693,4 +733,5 @@ MODULE_AUTHOR("Sathya Prakash M R <sathya.prakash.m.r@intel.com>");
MODULE_AUTHOR("Yong Zhi <yong.zhi@intel.com>");
MODULE_DESCRIPTION("Intel Audio Machine driver for SKL with NAU88L25 and SSM4567 in I2S Mode");
MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:skl_nau88l25_ssm4567_i2s");
+MODULE_ALIAS("platform:skl_n88l25_s4567");
+MODULE_ALIAS("platform:kbl_n88l25_s4567");
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
index 426b48233fdb..88c61e8cb87f 100644
--- a/sound/soc/intel/boards/skl_rt286.c
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -505,12 +505,20 @@ static int skylake_audio_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
}
+static const struct platform_device_id skl_board_ids[] = {
+ { .name = "skl_alc286s_i2s" },
+ { .name = "kbl_alc286s_i2s" },
+ { }
+};
+
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
.name = "skl_alc286s_i2s",
.pm = &snd_soc_pm_ops,
},
+ .id_table = skl_board_ids,
+
};
module_platform_driver(skylake_audio)
@@ -520,3 +528,4 @@ MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
MODULE_DESCRIPTION("Intel SST Audio for Skylake");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:skl_alc286s_i2s");
+MODULE_ALIAS("platform:kbl_alc286s_i2s");
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index fbbb25c2ceed..1a35149bcad7 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -2,9 +2,9 @@ snd-soc-sst-dsp-objs := sst-dsp.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-match-objs := sst-match-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
-
-snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
+snd-soc-sst-firmware-objs := sst-firmware.o
obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
obj-$(CONFIG_SND_SOC_INTEL_SST_MATCH) += snd-soc-sst-match.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o
diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
index 8398cb227ba9..5d2949324d0e 100644
--- a/sound/soc/intel/common/sst-acpi.h
+++ b/sound/soc/intel/common/sst-acpi.h
@@ -20,7 +20,7 @@
#if IS_ENABLED(CONFIG_ACPI)
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
#else
-inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
{
return NULL;
}
@@ -40,6 +40,6 @@ struct sst_acpi_mach {
/* board name */
const char *board;
- void (*machine_quirk)(void);
+ struct sst_acpi_mach * (*machine_quirk)(void *arg);
void *pdata;
};
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h
index 97dc1ae05e69..d13c84364c3c 100644
--- a/sound/soc/intel/common/sst-dsp-priv.h
+++ b/sound/soc/intel/common/sst-dsp-priv.h
@@ -383,10 +383,6 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
u32 index, void *private);
void sst_mem_block_unregister_all(struct sst_dsp *dsp);
-/* Create/Free DMA resources */
-int sst_dma_new(struct sst_dsp *sst);
-void sst_dma_free(struct sst_dma *dma);
-
u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
enum sst_mem_type type);
#endif
diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c
index b5bbdf4fe93a..c00ede4ea4d7 100644
--- a/sound/soc/intel/common/sst-dsp.c
+++ b/sound/soc/intel/common/sst-dsp.c
@@ -285,7 +285,7 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
}
reg = sst_dsp_shim_read_unlocked(ctx, offset);
- dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
+ dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
(time < timeout) ? "successful" : "timedout");
ret = time < timeout ? 0 : -ETIME;
@@ -420,73 +420,6 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
}
EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
-#ifdef CONFIG_DW_DMAC_CORE
-struct sst_dsp *sst_dsp_new(struct device *dev,
- struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
-{
- struct sst_dsp *sst;
- int err;
-
- dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
-
- sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
- if (sst == NULL)
- return NULL;
-
- spin_lock_init(&sst->spinlock);
- mutex_init(&sst->mutex);
- sst->dev = dev;
- sst->dma_dev = pdata->dma_dev;
- sst->thread_context = sst_dev->thread_context;
- sst->sst_dev = sst_dev;
- sst->id = pdata->id;
- sst->irq = pdata->irq;
- sst->ops = sst_dev->ops;
- sst->pdata = pdata;
- INIT_LIST_HEAD(&sst->used_block_list);
- INIT_LIST_HEAD(&sst->free_block_list);
- INIT_LIST_HEAD(&sst->module_list);
- INIT_LIST_HEAD(&sst->fw_list);
- INIT_LIST_HEAD(&sst->scratch_block_list);
-
- /* Initialise SST Audio DSP */
- if (sst->ops->init) {
- err = sst->ops->init(sst, pdata);
- if (err < 0)
- return NULL;
- }
-
- /* Register the ISR */
- err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
- sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
- if (err)
- goto irq_err;
-
- err = sst_dma_new(sst);
- if (err)
- dev_warn(dev, "sst_dma_new failed %d\n", err);
-
- return sst;
-
-irq_err:
- if (sst->ops->free)
- sst->ops->free(sst);
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(sst_dsp_new);
-
-void sst_dsp_free(struct sst_dsp *sst)
-{
- free_irq(sst->irq, sst);
- if (sst->ops->free)
- sst->ops->free(sst);
-
- sst_dma_free(sst->dma);
-}
-EXPORT_SYMBOL_GPL(sst_dsp_free);
-#endif
-
/* Module information */
MODULE_AUTHOR("Liam Girdwood");
MODULE_DESCRIPTION("Intel SST Core");
diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h
index 0b84c719ec48..859f0de00339 100644
--- a/sound/soc/intel/common/sst-dsp.h
+++ b/sound/soc/intel/common/sst-dsp.h
@@ -216,7 +216,7 @@ struct sst_pdata {
void *dsp;
};
-#ifdef CONFIG_DW_DMAC_CORE
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
/* Initialization */
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index 25993527370b..a086c35f91bb 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -1211,3 +1211,71 @@ u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
}
}
EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
+
+struct sst_dsp *sst_dsp_new(struct device *dev,
+ struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
+{
+ struct sst_dsp *sst;
+ int err;
+
+ dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id);
+
+ sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL);
+ if (sst == NULL)
+ return NULL;
+
+ spin_lock_init(&sst->spinlock);
+ mutex_init(&sst->mutex);
+ sst->dev = dev;
+ sst->dma_dev = pdata->dma_dev;
+ sst->thread_context = sst_dev->thread_context;
+ sst->sst_dev = sst_dev;
+ sst->id = pdata->id;
+ sst->irq = pdata->irq;
+ sst->ops = sst_dev->ops;
+ sst->pdata = pdata;
+ INIT_LIST_HEAD(&sst->used_block_list);
+ INIT_LIST_HEAD(&sst->free_block_list);
+ INIT_LIST_HEAD(&sst->module_list);
+ INIT_LIST_HEAD(&sst->fw_list);
+ INIT_LIST_HEAD(&sst->scratch_block_list);
+
+ /* Initialise SST Audio DSP */
+ if (sst->ops->init) {
+ err = sst->ops->init(sst, pdata);
+ if (err < 0)
+ return NULL;
+ }
+
+ /* Register the ISR */
+ err = request_threaded_irq(sst->irq, sst->ops->irq_handler,
+ sst_dev->thread, IRQF_SHARED, "AudioDSP", sst);
+ if (err)
+ goto irq_err;
+
+ err = sst_dma_new(sst);
+ if (err)
+ dev_warn(dev, "sst_dma_new failed %d\n", err);
+
+ return sst;
+
+irq_err:
+ if (sst->ops->free)
+ sst->ops->free(sst);
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_new);
+
+void sst_dsp_free(struct sst_dsp *sst)
+{
+ free_irq(sst->irq, sst);
+ if (sst->ops->free)
+ sst->ops->free(sst);
+
+ sst_dma_free(sst->dma);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_free);
+
+MODULE_DESCRIPTION("Intel SST Firmware Loader");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
index 994256b39b9c..3154525c2b83 100644
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/sound/soc/intel/haswell/sst-haswell-pcm.c
@@ -819,7 +819,6 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
mutex_lock(&pcm_data->mutex);
pm_runtime_get_sync(pdata->dev);
- snd_soc_pcm_set_drvdata(rtd, pcm_data);
pcm_data->substream = substream;
snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware);
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index c28f5d0e1d99..60fbc9bbe473 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -5,6 +5,6 @@ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
# Skylake IPC Support
snd-soc-skl-ipc-objs := skl-sst-ipc.o skl-sst-dsp.o skl-sst-cldma.o \
- skl-sst.o bxt-sst.o
+ skl-sst.o bxt-sst.o skl-sst-utils.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
index 965ce40ce752..2663781278aa 100644
--- a/sound/soc/intel/skylake/bxt-sst.c
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -37,11 +37,19 @@
#define BXT_ADSP_SRAM1_BASE 0xA0000
+#define BXT_INSTANCE_ID 0
+#define BXT_BASE_FW_MODULE_ID 0
+
static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
}
+/*
+ * First boot sequence has some extra steps. Core 0 waits for power
+ * status on core 1, so power up core 1 also momentarily, keep it in
+ * reset/stall and then turn it off
+ */
static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
const void *fwdata, u32 fwsize)
{
@@ -49,7 +57,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
u32 reg;
stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
- if (stream_tag < 0) {
+ if (stream_tag <= 0) {
dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
stream_tag);
return stream_tag;
@@ -58,17 +66,27 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
ctx->dsp_ops.stream_tag = stream_tag;
memcpy(ctx->dmab.area, fwdata, fwsize);
- /* Purge FW request */
+ /* Step 1: Power up core 0 and core1 */
+ ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK |
+ SKL_DSP_CORE_MASK(1));
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core0/1 power up failed\n");
+ goto base_fw_load_failed;
+ }
+
+ /* Step 2: Purge FW request */
sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
- BXT_IPC_PURGE_FW | (stream_tag - 1));
+ (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9)));
- ret = skl_dsp_enable_core(ctx);
+ /* Step 3: Unset core0 reset state & unstall/run core0 */
+ ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
+ dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret);
ret = -EIO;
goto base_fw_load_failed;
}
+ /* Step 4: Wait for DONE Bit */
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
@@ -88,10 +106,18 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
SKL_ADSP_REG_HIPCIE_DONE);
}
- /* enable Interrupt */
+ /* Step 5: power down core1 */
+ ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core1 power down failed\n");
+ goto base_fw_load_failed;
+ }
+
+ /* Step 6: Enable Interrupt */
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
+ /* Step 7: Wait for ROM init */
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
if (SKL_FW_INIT ==
(sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
@@ -112,7 +138,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
base_fw_load_failed:
ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
- skl_dsp_disable_core(ctx);
+ skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
return ret;
}
@@ -130,23 +157,41 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
return ret;
}
+#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+
static int bxt_load_base_firmware(struct sst_dsp *ctx)
{
- const struct firmware *fw = NULL;
+ struct firmware stripped_fw;
struct skl_sst *skl = ctx->thread_context;
int ret;
- ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
+ ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
if (ret < 0) {
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
goto sst_load_base_firmware_failed;
}
- ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+ /* check for extended manifest */
+ if (ctx->fw == NULL)
+ goto sst_load_base_firmware_failed;
+
+ ret = snd_skl_parse_uuids(ctx, BXT_ADSP_FW_BIN_HDR_OFFSET);
+ if (ret < 0)
+ goto sst_load_base_firmware_failed;
+
+ stripped_fw.data = ctx->fw->data;
+ stripped_fw.size = ctx->fw->size;
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
+ ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
/* Retry Enabling core and ROM load. Retry seemed to help */
if (ret < 0) {
- ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+ ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
if (ret < 0) {
+ dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+ sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+ sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+
dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
goto sst_load_base_firmware_failed;
}
@@ -159,83 +204,135 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
- skl_dsp_disable_core(ctx);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
} else {
dev_dbg(ctx->dev, "Firmware download successful\n");
ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
if (ret == 0) {
dev_err(ctx->dev, "DSP boot fail, FW Ready timeout\n");
- skl_dsp_disable_core(ctx);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
ret = -EIO;
} else {
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
ret = 0;
+ skl->fw_loaded = true;
}
}
sst_load_base_firmware_failed:
- release_firmware(fw);
+ release_firmware(ctx->fw);
return ret;
}
-static int bxt_set_dsp_D0(struct sst_dsp *ctx)
+static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
struct skl_sst *skl = ctx->thread_context;
int ret;
+ struct skl_ipc_dxstate_info dx;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- skl->boot_complete = false;
-
- ret = skl_dsp_enable_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
+ if (skl->fw_loaded == false) {
+ skl->boot_complete = false;
+ ret = bxt_load_base_firmware(ctx);
+ if (ret < 0)
+ dev_err(ctx->dev, "reload fw failed: %d\n", ret);
return ret;
}
- /* enable interrupt */
- skl_ipc_int_enable(ctx);
- skl_ipc_op_int_enable(ctx);
+ /* If core 0 is being turned on, turn on core 1 as well */
+ if (core_id == SKL_DSP_CORE0_ID)
+ ret = skl_dsp_core_power_up(ctx, core_mask |
+ SKL_DSP_CORE_MASK(1));
+ else
+ ret = skl_dsp_core_power_up(ctx, core_mask);
+
+ if (ret < 0)
+ goto err;
+
+ if (core_id == SKL_DSP_CORE0_ID) {
+
+ /*
+ * Enable interrupt after SPA is set and before
+ * DSP is unstalled
+ */
+ skl_ipc_int_enable(ctx);
+ skl_ipc_op_int_enable(ctx);
+ skl->boot_complete = false;
+ }
- ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
- msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
- if (ret == 0) {
- dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
- dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
- sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
- sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
- return -EIO;
+ ret = skl_dsp_start_core(ctx, core_mask);
+ if (ret < 0)
+ goto err;
+
+ if (core_id == SKL_DSP_CORE0_ID) {
+ ret = wait_event_timeout(skl->boot_wait,
+ skl->boot_complete,
+ msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+
+ /* If core 1 was turned on for booting core 0, turn it off */
+ skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+ if (ret == 0) {
+ dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__);
+ dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+ sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+ sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+ dev_err(ctx->dev, "Failed to set core0 to D0 state\n");
+ ret = -EIO;
+ goto err;
+ }
+ }
+
+ /* Tell FW if additional core in now On */
+
+ if (core_id != SKL_DSP_CORE0_ID) {
+ dx.core_mask = core_mask;
+ dx.dx_mask = core_mask;
+
+ ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
+ BXT_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n",
+ core_id, ret);
+ goto err;
+ }
}
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ skl->cores.state[core_id] = SKL_DSP_RUNNING;
return 0;
+err:
+ if (core_id == SKL_DSP_CORE0_ID)
+ core_mask |= SKL_DSP_CORE_MASK(1);
+ skl_dsp_disable_core(ctx, core_mask);
+
+ return ret;
}
-static int bxt_set_dsp_D3(struct sst_dsp *ctx)
+static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
{
+ int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
- int ret = 0;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- if (!is_skl_dsp_running(ctx))
- return ret;
-
- dx.core_mask = SKL_DSP_CORE0_MASK;
+ dx.core_mask = core_mask;
dx.dx_mask = SKL_IPC_D3_MASK;
- ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
- SKL_BASE_FW_MODULE_ID, &dx);
- if (ret < 0) {
- dev_err(ctx->dev, "Failed to set DSP to D3 state: %d\n", ret);
- return ret;
- }
+ dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n",
+ dx.core_mask, dx.dx_mask);
+
+ ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
+ BXT_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0)
+ dev_err(ctx->dev,
+ "Failed to set DSP to D3:core id = %d;Continue reset\n",
+ core_id);
- ret = skl_dsp_disable_core(ctx);
+ ret = skl_dsp_disable_core(ctx, core_mask);
if (ret < 0) {
- dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
- ret = -EIO;
+ dev_err(ctx->dev, "Failed to disable core %d", ret);
+ return ret;
}
-
- skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ skl->cores.state[core_id] = SKL_DSP_RESET;
return 0;
}
@@ -274,6 +371,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->dev = dev;
skl_dev.thread_context = skl;
+ INIT_LIST_HEAD(&skl->uuid_list);
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
@@ -291,10 +389,12 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
+ INIT_LIST_HEAD(&sst->module_list);
ret = skl_ipc_init(dev, skl);
if (ret)
return ret;
+ skl->cores.count = 2;
skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait);
@@ -304,6 +404,8 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
return ret;
}
+ skl_dsp_init_core_state(sst);
+
if (dsp)
*dsp = skl;
@@ -314,6 +416,7 @@ EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
+ skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 226db84ba20f..44ab595ce21a 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -206,6 +206,12 @@ static const struct skl_dsp_ops dsp_ops[] = {
.cleanup = skl_sst_dsp_cleanup
},
{
+ .id = 0x9d71,
+ .loader_ops = skl_get_loader_ops,
+ .init = skl_sst_dsp_init,
+ .cleanup = skl_sst_dsp_cleanup
+ },
+ {
.id = 0x5a98,
.loader_ops = bxt_get_loader_ops,
.init = bxt_sst_dsp_init,
@@ -730,7 +736,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
dev_dbg(ctx->dev, "Module type=%d config size: %d bytes\n",
module_config->id.module_id, param_size);
- print_hex_dump(KERN_DEBUG, "Module params:", DUMP_PREFIX_OFFSET, 8, 4,
+ print_hex_dump_debug("Module params:", DUMP_PREFIX_OFFSET, 8, 4,
*param_data, param_size, false);
return 0;
}
@@ -1046,7 +1052,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
dev_dbg(ctx->dev, "%s: pipe = %d\n", __func__, pipe->ppl_id);
- /* If pipe is not started, do not try to stop the pipe in FW. */
+ /* If pipe is started, do stop the pipe in FW. */
if (pipe->state > SKL_PIPE_STARTED) {
ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
if (ret < 0) {
@@ -1055,18 +1061,20 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
}
pipe->state = SKL_PIPE_PAUSED;
- } else {
- /* If pipe was not created in FW, do not try to delete it */
- if (pipe->state < SKL_PIPE_CREATED)
- return 0;
+ }
- ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
- if (ret < 0)
- dev_err(ctx->dev, "Failed to delete pipeline\n");
+ /* If pipe was not created in FW, do not try to delete it */
+ if (pipe->state < SKL_PIPE_CREATED)
+ return 0;
- pipe->state = SKL_PIPE_INVALID;
+ ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to delete pipeline\n");
+ return ret;
}
+ pipe->state = SKL_PIPE_INVALID;
+
return ret;
}
@@ -1125,7 +1133,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
return ret;
}
- pipe->state = SKL_PIPE_CREATED;
+ pipe->state = SKL_PIPE_PAUSED;
+
+ return 0;
+}
+
+/*
+ * Reset the pipeline by sending set pipe state IPC this will reset the DMA
+ * from the DSP side
+ */
+int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+ int ret;
+
+ /* If pipe was not created in FW, do not try to pause or delete */
+ if (pipe->state < SKL_PIPE_PAUSED)
+ return 0;
+
+ ret = skl_set_pipe_state(ctx, pipe, PPL_RESET);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "Failed to reset pipe ret=%d\n", ret);
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_RESET;
return 0;
}
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 7d73648e5f9a..3f8e6f0b7eb5 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -17,6 +17,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
+#include <linux/pci.h>
#include "skl.h"
/* Unique identification for getting NHLT blobs */
@@ -149,6 +150,45 @@ struct nhlt_specific_cfg
return NULL;
}
+int skl_get_dmic_geo(struct skl *skl)
+{
+ struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+ struct nhlt_endpoint *epnt;
+ struct nhlt_dmic_array_config *cfg;
+ struct device *dev = &skl->pci->dev;
+ unsigned int dmic_geo = 0;
+ u8 j;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ for (j = 0; j < nhlt->endpoint_count; j++) {
+ if (epnt->linktype == NHLT_LINK_DMIC) {
+ cfg = (struct nhlt_dmic_array_config *)
+ (epnt->config.caps);
+ switch (cfg->array_type) {
+ case NHLT_MIC_ARRAY_2CH_SMALL:
+ case NHLT_MIC_ARRAY_2CH_BIG:
+ dmic_geo |= MIC_ARRAY_2CH;
+ break;
+
+ case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+ case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+ case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+ dmic_geo |= MIC_ARRAY_4CH;
+ break;
+
+ default:
+ dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
+ cfg->array_type);
+
+ }
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return dmic_geo;
+}
+
static void skl_nhlt_trim_space(struct skl *skl)
{
char *s = skl->tplg_name;
diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h
index 3769f9fefe2b..116534e7b3c5 100644
--- a/sound/soc/intel/skylake/skl-nhlt.h
+++ b/sound/soc/intel/skylake/skl-nhlt.h
@@ -103,4 +103,26 @@ struct nhlt_resource_desc {
u64 length;
} __packed;
+#define MIC_ARRAY_2CH 2
+#define MIC_ARRAY_4CH 4
+
+struct nhlt_tdm_config {
+ u8 virtual_slot;
+ u8 config_type;
+} __packed;
+
+struct nhlt_dmic_array_config {
+ struct nhlt_tdm_config tdm_config;
+ u8 array_type;
+} __packed;
+
+enum {
+ NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
+ NHLT_MIC_ARRAY_2CH_BIG = 0xb,
+ NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc,
+ NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd,
+ NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe,
+ NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
+};
+
#endif
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 7c81b31748ff..6e05bf8622f7 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -227,16 +227,25 @@ static int skl_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
+ struct skl *skl = get_skl_ctx(dai->dev);
unsigned int format_val;
int err;
+ struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+
format_val = skl_get_format(substream, dai);
dev_dbg(dai->dev, "stream_tag=%d formatvalue=%d\n",
hdac_stream(stream)->stream_tag, format_val);
snd_hdac_stream_reset(hdac_stream(stream));
+ /* In case of XRUN recovery, reset the FW pipe to clean state */
+ if (mconfig && (substream->runtime->status->state ==
+ SNDRV_PCM_STATE_XRUN))
+ skl_reset_pipe(skl->skl_sst, mconfig->pipe);
+
err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
if (err < 0)
return err;
@@ -521,6 +530,8 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct hdac_ext_link *link;
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_module_cfg *mconfig = NULL;
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -535,6 +546,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
snd_hdac_ext_link_stream_reset(link_dev);
+ /* In case of XRUN recovery, reset the FW pipe to clean state */
+ mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
+ if (mconfig && (substream->runtime->status->state ==
+ SNDRV_PCM_STATE_XRUN))
+ skl_reset_pipe(skl->skl_sst, mconfig->pipe);
+
snd_hdac_ext_link_stream_setup(link_dev, format_val);
snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
@@ -1009,51 +1026,11 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
return 0;
}
-/* calculate runtime delay from LPIB */
-static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
- struct hdac_ext_stream *sstream,
- unsigned int pos)
-{
- struct hdac_bus *bus = ebus_to_hbus(ebus);
- struct hdac_stream *hstream = hdac_stream(sstream);
- struct snd_pcm_substream *substream = hstream->substream;
- int stream = substream->stream;
- unsigned int lpib_pos = snd_hdac_stream_get_pos_lpib(hstream);
- int delay;
-
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- delay = pos - lpib_pos;
- else
- delay = lpib_pos - pos;
-
- if (delay < 0) {
- if (delay >= hstream->delay_negative_threshold)
- delay = 0;
- else
- delay += hstream->bufsize;
- }
-
- if (hstream->bufsize == delay)
- delay = 0;
-
- if (delay >= hstream->period_bytes) {
- dev_info(bus->dev,
- "Unstable LPIB (%d >= %d); disabling LPIB delay counting\n",
- delay, hstream->period_bytes);
- delay = 0;
- }
-
- return bytes_to_frames(substream->runtime, delay);
-}
-
-static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
- int codec_delay)
+static snd_pcm_uframes_t skl_platform_pcm_pointer
+ (struct snd_pcm_substream *substream)
{
- struct hdac_stream *hstr = hdac_stream(hstream);
- struct snd_pcm_substream *substream = hstr->substream;
- struct hdac_ext_bus *ebus;
+ struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
unsigned int pos;
- int delay;
/* use the position buffer as default */
pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
@@ -1061,23 +1038,7 @@ static unsigned int skl_get_position(struct hdac_ext_stream *hstream,
if (pos >= hdac_stream(hstream)->bufsize)
pos = 0;
- if (substream->runtime) {
- ebus = get_bus_ctx(substream);
- delay = skl_get_delay_from_lpib(ebus, hstream, pos)
- + codec_delay;
- substream->runtime->delay += delay;
- }
-
- return pos;
-}
-
-static snd_pcm_uframes_t skl_platform_pcm_pointer
- (struct snd_pcm_substream *substream)
-{
- struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
-
- return bytes_to_frames(substream->runtime,
- skl_get_position(hstream, 0));
+ return bytes_to_frames(substream->runtime, pos);
}
static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
@@ -1180,9 +1141,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
static int skl_platform_soc_probe(struct snd_soc_platform *platform)
{
struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+ struct skl *skl = ebus_to_skl(ebus);
+ int ret;
- if (ebus->ppcap)
- return skl_tplg_init(platform, ebus);
+ if (ebus->ppcap) {
+ ret = skl_tplg_init(platform, ebus);
+ if (ret < 0) {
+ dev_err(platform->dev, "Failed to init topology!\n");
+ return ret;
+ }
+ skl->platform = platform;
+ }
return 0;
}
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index 13c19855ee1a..c3deefab65d6 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -34,33 +34,84 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
mutex_unlock(&ctx->mutex);
}
-static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
+/*
+ * Initialize core power state and usage count. To be called after
+ * successful first boot. Hence core 0 will be running and other cores
+ * will be reset
+ */
+void skl_dsp_init_core_state(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int i;
+
+ skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING;
+ skl->cores.usage_count[SKL_DSP_CORE0_ID] = 1;
+
+ for (i = SKL_DSP_CORE0_ID + 1; i < SKL_DSP_CORES_MAX; i++) {
+ skl->cores.state[i] = SKL_DSP_RESET;
+ skl->cores.usage_count[i] = 0;
+ }
+}
+
+/* Get the mask for all enabled cores */
+unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ unsigned int core_mask, en_cores_mask;
+ u32 val;
+
+ core_mask = SKL_DSP_CORES_MASK(skl->cores.count);
+
+ val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
+
+ /* Cores having CPA bit set */
+ en_cores_mask = (val & SKL_ADSPCS_CPA_MASK(core_mask)) >>
+ SKL_ADSPCS_CPA_SHIFT;
+
+ /* And cores having CRST bit cleared */
+ en_cores_mask &= (~val & SKL_ADSPCS_CRST_MASK(core_mask)) >>
+ SKL_ADSPCS_CRST_SHIFT;
+
+ /* And cores having CSTALL bit cleared */
+ en_cores_mask &= (~val & SKL_ADSPCS_CSTALL_MASK(core_mask)) >>
+ SKL_ADSPCS_CSTALL_SHIFT;
+ en_cores_mask &= core_mask;
+
+ dev_dbg(ctx->dev, "DSP enabled cores mask = %x\n", en_cores_mask);
+
+ return en_cores_mask;
+}
+
+static int
+skl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx,
- SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK,
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK));
+ SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK(core_mask),
+ SKL_ADSPCS_CRST_MASK(core_mask));
/* poll with timeout to check if operation successful */
ret = sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK,
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK),
+ SKL_ADSPCS_CRST_MASK(core_mask),
+ SKL_ADSPCS_CRST_MASK(core_mask),
SKL_DSP_RESET_TO,
"Set reset");
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) !=
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) {
- dev_err(ctx->dev, "Set reset state failed\n");
+ SKL_ADSPCS_CRST_MASK(core_mask)) !=
+ SKL_ADSPCS_CRST_MASK(core_mask)) {
+ dev_err(ctx->dev, "Set reset state failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
+int skl_dsp_core_unset_reset_state(
+ struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
@@ -68,152 +119,160 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK, 0);
+ SKL_ADSPCS_CRST_MASK(core_mask), 0);
/* poll with timeout to check if operation successful */
ret = sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK,
+ SKL_ADSPCS_CRST_MASK(core_mask),
0,
SKL_DSP_RESET_TO,
"Unset reset");
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) != 0) {
- dev_err(ctx->dev, "Unset reset state failed\n");
+ SKL_ADSPCS_CRST_MASK(core_mask)) != 0) {
+ dev_err(ctx->dev, "Unset reset state failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
+static bool
+is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask)
{
int val;
bool is_enable;
val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
- is_enable = ((val & SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) &&
- (val & SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK)) &&
- !(val & SKL_ADSPCS_CRST(SKL_DSP_CORES_MASK)) &&
- !(val & SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK)));
+ is_enable = ((val & SKL_ADSPCS_CPA_MASK(core_mask)) &&
+ (val & SKL_ADSPCS_SPA_MASK(core_mask)) &&
+ !(val & SKL_ADSPCS_CRST_MASK(core_mask)) &&
+ !(val & SKL_ADSPCS_CSTALL_MASK(core_mask)));
+
+ dev_dbg(ctx->dev, "DSP core(s) enabled? %d : core_mask %x\n",
+ is_enable, core_mask);
- dev_dbg(ctx->dev, "DSP core is enabled=%d\n", is_enable);
return is_enable;
}
-static int skl_dsp_reset_core(struct sst_dsp *ctx)
+static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask)
{
/* stall core */
- sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CSTALL_MASK(core_mask),
+ SKL_ADSPCS_CSTALL_MASK(core_mask));
/* set reset state */
- return skl_dsp_core_set_reset_state(ctx);
+ return skl_dsp_core_set_reset_state(ctx, core_mask);
}
-static int skl_dsp_start_core(struct sst_dsp *ctx)
+int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* unset reset state */
- ret = skl_dsp_core_unset_reset_state(ctx);
- if (ret < 0) {
- dev_dbg(ctx->dev, "dsp unset reset fails\n");
+ ret = skl_dsp_core_unset_reset_state(ctx, core_mask);
+ if (ret < 0)
return ret;
- }
/* run core */
- dev_dbg(ctx->dev, "run core...\n");
- sst_dsp_shim_write_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- ~SKL_ADSPCS_CSTALL(SKL_DSP_CORES_MASK));
-
- if (!is_skl_dsp_core_enable(ctx)) {
- skl_dsp_reset_core(ctx);
- dev_err(ctx->dev, "DSP core enable failed\n");
+ dev_dbg(ctx->dev, "unstall/run core: core_mask = %x\n", core_mask);
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CSTALL_MASK(core_mask), 0);
+
+ if (!is_skl_dsp_core_enable(ctx, core_mask)) {
+ skl_dsp_reset_core(ctx, core_mask);
+ dev_err(ctx->dev, "DSP start core failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static int skl_dsp_core_power_up(struct sst_dsp *ctx)
+int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK, SKL_ADSPCS_SPA(SKL_DSP_CORES_MASK));
+ SKL_ADSPCS_SPA_MASK(core_mask),
+ SKL_ADSPCS_SPA_MASK(core_mask));
/* poll with timeout to check if operation successful */
ret = sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CPA_MASK,
- SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK),
+ SKL_ADSPCS_CPA_MASK(core_mask),
+ SKL_ADSPCS_CPA_MASK(core_mask),
SKL_DSP_PU_TO,
"Power up");
if ((sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS) &
- SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) !=
- SKL_ADSPCS_CPA(SKL_DSP_CORES_MASK)) {
- dev_err(ctx->dev, "DSP core power up failed\n");
+ SKL_ADSPCS_CPA_MASK(core_mask)) !=
+ SKL_ADSPCS_CPA_MASK(core_mask)) {
+ dev_err(ctx->dev, "DSP core power up failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static int skl_dsp_core_power_down(struct sst_dsp *ctx)
+int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask)
{
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK, 0);
+ SKL_ADSPCS_SPA_MASK(core_mask), 0);
/* poll with timeout to check if operation successful */
return sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CPA_MASK,
+ SKL_ADSPCS_CPA_MASK(core_mask),
0,
SKL_DSP_PD_TO,
"Power down");
}
-int skl_dsp_enable_core(struct sst_dsp *ctx)
+int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* power up */
- ret = skl_dsp_core_power_up(ctx);
+ ret = skl_dsp_core_power_up(ctx, core_mask);
if (ret < 0) {
- dev_dbg(ctx->dev, "dsp core power up failed\n");
+ dev_err(ctx->dev, "dsp core power up failed: core_mask %x\n",
+ core_mask);
return ret;
}
- return skl_dsp_start_core(ctx);
+ return skl_dsp_start_core(ctx, core_mask);
}
-int skl_dsp_disable_core(struct sst_dsp *ctx)
+int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
- ret = skl_dsp_reset_core(ctx);
+ ret = skl_dsp_reset_core(ctx, core_mask);
if (ret < 0) {
- dev_err(ctx->dev, "dsp core reset failed\n");
+ dev_err(ctx->dev, "dsp core reset failed: core_mask %x\n",
+ core_mask);
return ret;
}
/* power down core*/
- ret = skl_dsp_core_power_down(ctx);
+ ret = skl_dsp_core_power_down(ctx, core_mask);
if (ret < 0) {
- dev_err(ctx->dev, "dsp core power down failed\n");
+ dev_err(ctx->dev, "dsp core power down fail mask %x: %d\n",
+ core_mask, ret);
return ret;
}
- if (is_skl_dsp_core_enable(ctx)) {
- dev_err(ctx->dev, "DSP core disable failed\n");
+ if (is_skl_dsp_core_enable(ctx, core_mask)) {
+ dev_err(ctx->dev, "dsp core disable fail mask %x: %d\n",
+ core_mask, ret);
ret = -EIO;
}
@@ -224,28 +283,25 @@ int skl_dsp_boot(struct sst_dsp *ctx)
{
int ret;
- if (is_skl_dsp_core_enable(ctx)) {
- dev_dbg(ctx->dev, "dsp core is already enabled, so reset the dap core\n");
- ret = skl_dsp_reset_core(ctx);
+ if (is_skl_dsp_core_enable(ctx, SKL_DSP_CORE0_MASK)) {
+ ret = skl_dsp_reset_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "dsp reset failed\n");
+ dev_err(ctx->dev, "dsp core0 reset fail: %d\n", ret);
return ret;
}
- ret = skl_dsp_start_core(ctx);
+ ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "dsp start failed\n");
+ dev_err(ctx->dev, "dsp core0 start fail: %d\n", ret);
return ret;
}
} else {
- dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
- ret = skl_dsp_disable_core(ctx);
-
+ ret = skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "dsp disable core failes\n");
+ dev_err(ctx->dev, "dsp core0 disable fail: %d\n", ret);
return ret;
}
- ret = skl_dsp_enable_core(ctx);
+ ret = skl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK);
}
return ret;
@@ -281,16 +337,74 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
return result;
}
+/*
+ * skl_dsp_get_core/skl_dsp_put_core will be called inside DAPM context
+ * within the dapm mutex. Hence no separate lock is used.
+ */
+int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int ret = 0;
+
+ if (core_id >= skl->cores.count) {
+ dev_err(ctx->dev, "invalid core id: %d\n", core_id);
+ return -EINVAL;
+ }
+
+ if (skl->cores.state[core_id] == SKL_DSP_RESET) {
+ ret = ctx->fw_ops.set_state_D0(ctx, core_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "unable to get core%d\n", core_id);
+ return ret;
+ }
+ }
+
+ skl->cores.usage_count[core_id]++;
+
+ dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
+ core_id, skl->cores.state[core_id],
+ skl->cores.usage_count[core_id]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_dsp_get_core);
+
+int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int ret = 0;
+
+ if (core_id >= skl->cores.count) {
+ dev_err(ctx->dev, "invalid core id: %d\n", core_id);
+ return -EINVAL;
+ }
+
+ if (--skl->cores.usage_count[core_id] == 0) {
+ ret = ctx->fw_ops.set_state_D3(ctx, core_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "unable to put core %d: %d\n",
+ core_id, ret);
+ skl->cores.usage_count[core_id]++;
+ }
+ }
+
+ dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
+ core_id, skl->cores.state[core_id],
+ skl->cores.usage_count[core_id]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_dsp_put_core);
int skl_dsp_wake(struct sst_dsp *ctx)
{
- return ctx->fw_ops.set_state_D0(ctx);
+ return skl_dsp_get_core(ctx, SKL_DSP_CORE0_ID);
}
EXPORT_SYMBOL_GPL(skl_dsp_wake);
int skl_dsp_sleep(struct sst_dsp *ctx)
{
- return ctx->fw_ops.set_state_D3(ctx);
+ return skl_dsp_put_core(ctx, SKL_DSP_CORE0_ID);
}
EXPORT_SYMBOL_GPL(skl_dsp_sleep);
@@ -337,9 +451,7 @@ void skl_dsp_free(struct sst_dsp *dsp)
free_irq(dsp->irq, dsp);
skl_ipc_op_int_disable(dsp);
- skl_ipc_int_disable(dsp);
-
- skl_dsp_disable_core(dsp);
+ skl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK);
}
EXPORT_SYMBOL_GPL(skl_dsp_free);
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index deabe7308d3b..0f8629ef79ac 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -19,6 +19,7 @@
#include <linux/interrupt.h>
#include <sound/memalloc.h>
#include "skl-sst-cldma.h"
+#include "skl-tplg-interface.h"
struct sst_dsp;
struct skl_sst;
@@ -76,35 +77,53 @@ struct sst_dsp_device;
#define SKL_ADSPIC_IPC 1
#define SKL_ADSPIS_IPC 1
+/* Core ID of core0 */
+#define SKL_DSP_CORE0_ID 0
+
+/* Mask for a given core index, c = 0.. number of supported cores - 1 */
+#define SKL_DSP_CORE_MASK(c) BIT(c)
+
+/*
+ * Core 0 mask = SKL_DSP_CORE_MASK(0); Defined separately
+ * since Core0 is primary core and it is used often
+ */
+#define SKL_DSP_CORE0_MASK BIT(0)
+
+/*
+ * Mask for a given number of cores
+ * nc = number of supported cores
+ */
+#define SKL_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0)
+
/* ADSPCS - Audio DSP Control & Status */
-#define SKL_DSP_CORES 1
-#define SKL_DSP_CORE0_MASK 1
-#define SKL_DSP_CORES_MASK ((1 << SKL_DSP_CORES) - 1)
-
-/* Core Reset - asserted high */
-#define SKL_ADSPCS_CRST_SHIFT 0
-#define SKL_ADSPCS_CRST_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CRST_SHIFT)
-#define SKL_ADSPCS_CRST(x) ((x << SKL_ADSPCS_CRST_SHIFT) & SKL_ADSPCS_CRST_MASK)
-
-/* Core run/stall - when set to '1' core is stalled */
-#define SKL_ADSPCS_CSTALL_SHIFT 8
-#define SKL_ADSPCS_CSTALL_MASK (SKL_DSP_CORES_MASK << \
- SKL_ADSPCS_CSTALL_SHIFT)
-#define SKL_ADSPCS_CSTALL(x) ((x << SKL_ADSPCS_CSTALL_SHIFT) & \
- SKL_ADSPCS_CSTALL_MASK)
-
-/* Set Power Active - when set to '1' turn cores on */
-#define SKL_ADSPCS_SPA_SHIFT 16
-#define SKL_ADSPCS_SPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_SPA_SHIFT)
-#define SKL_ADSPCS_SPA(x) ((x << SKL_ADSPCS_SPA_SHIFT) & SKL_ADSPCS_SPA_MASK)
-
-/* Current Power Active - power status of cores, set by hardware */
-#define SKL_ADSPCS_CPA_SHIFT 24
-#define SKL_ADSPCS_CPA_MASK (SKL_DSP_CORES_MASK << SKL_ADSPCS_CPA_SHIFT)
-#define SKL_ADSPCS_CPA(x) ((x << SKL_ADSPCS_CPA_SHIFT) & SKL_ADSPCS_CPA_MASK)
-
-#define SST_DSP_POWER_D0 0x0 /* full On */
-#define SST_DSP_POWER_D3 0x3 /* Off */
+
+/*
+ * Core Reset - asserted high
+ * CRST Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CRST_SHIFT 0
+#define SKL_ADSPCS_CRST_MASK(cm) ((cm) << SKL_ADSPCS_CRST_SHIFT)
+
+/*
+ * Core run/stall - when set to '1' core is stalled
+ * CSTALL Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CSTALL_SHIFT 8
+#define SKL_ADSPCS_CSTALL_MASK(cm) ((cm) << SKL_ADSPCS_CSTALL_SHIFT)
+
+/*
+ * Set Power Active - when set to '1' turn cores on
+ * SPA Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_SPA_SHIFT 16
+#define SKL_ADSPCS_SPA_MASK(cm) ((cm) << SKL_ADSPCS_SPA_SHIFT)
+
+/*
+ * Current Power Active - power status of cores, set by hardware
+ * CPA Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CPA_SHIFT 24
+#define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT)
enum skl_dsp_states {
SKL_DSP_RUNNING = 1,
@@ -115,8 +134,8 @@ struct skl_dsp_fw_ops {
int (*load_fw)(struct sst_dsp *ctx);
/* FW module parser/loader */
int (*parse_fw)(struct sst_dsp *ctx);
- int (*set_state_D0)(struct sst_dsp *ctx);
- int (*set_state_D3)(struct sst_dsp *ctx);
+ int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
+ int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
@@ -157,14 +176,26 @@ int skl_cldma_prepare(struct sst_dsp *ctx);
void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
struct sst_dsp_device *sst_dev, int irq);
-int skl_dsp_enable_core(struct sst_dsp *ctx);
-int skl_dsp_disable_core(struct sst_dsp *ctx);
bool is_skl_dsp_running(struct sst_dsp *ctx);
+
+unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx);
+void skl_dsp_init_core_state(struct sst_dsp *ctx);
+int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx,
+ unsigned int core_mask);
+int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask);
+
irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
int skl_dsp_wake(struct sst_dsp *ctx);
int skl_dsp_sleep(struct sst_dsp *ctx);
void skl_dsp_free(struct sst_dsp *dsp);
+int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id);
+int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id);
+
int skl_dsp_boot(struct sst_dsp *ctx);
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
@@ -175,4 +206,11 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
+int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
+ struct skl_dfw_module *dfw_config);
+int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset);
+void skl_freeup_uuid_list(struct skl_sst *ctx);
+
+int skl_dsp_strip_extended_manifest(struct firmware *fw);
+
#endif /*__SKL_SST_DSP_H__*/
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c
index 543460293b00..96f2f6889b18 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -363,7 +363,7 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
/* first process the header */
switch (reply) {
case IPC_GLB_REPLY_SUCCESS:
- dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
+ dev_dbg(ipc->dev, "ipc FW reply %x: success\n", header.primary);
/* copy the rx data from the mailbox */
sst_dsp_inbox_read(ipc->dsp, msg->rx_data, msg->rx_size);
break;
@@ -692,7 +692,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
/* param_block_size must be in dwords */
u16 param_block_size = msg->param_data_size / sizeof(u32);
- print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+ print_hex_dump_debug("Param data:", DUMP_PREFIX_NONE,
16, 4, buffer, param_block_size, false);
header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index d59d1ba62a43..2e3d4e80ef97 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -45,6 +45,14 @@ struct skl_ipc_header {
u32 extension;
};
+#define SKL_DSP_CORES_MAX 2
+
+struct skl_dsp_cores {
+ unsigned int count;
+ enum skl_dsp_states state[SKL_DSP_CORES_MAX];
+ int usage_count[SKL_DSP_CORES_MAX];
+};
+
struct skl_sst {
struct device *dev;
struct sst_dsp *dsp;
@@ -60,6 +68,15 @@ struct skl_sst {
void (*enable_miscbdcge)(struct device *dev, bool enable);
/*Is CGCTL.MISCBDCGE disabled*/
bool miscbdcg_disabled;
+
+ /* Populate module information */
+ struct list_head uuid_list;
+
+ /* Is firmware loaded */
+ bool fw_loaded;
+
+ /* multi-core */
+ struct skl_dsp_cores cores;
};
struct skl_ipc_init_instance_msg {
@@ -136,5 +153,6 @@ void skl_ipc_int_disable(struct sst_dsp *dsp);
bool skl_ipc_int_status(struct sst_dsp *dsp);
void skl_ipc_free(struct sst_generic_ipc *ipc);
int skl_ipc_init(struct device *dev, struct skl_sst *skl);
+void skl_clear_module_cnt(struct sst_dsp *ctx);
#endif /* __SKL_IPC_H */
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
new file mode 100644
index 000000000000..25fcb796bd86
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-sst-utils.c
@@ -0,0 +1,256 @@
+/*
+ * skl-sst-utils.c - SKL sst utils functions
+ *
+ * Copyright (C) 2016 Intel Corp
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/uuid.h>
+#include "skl-sst-dsp.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-ipc.h"
+
+
+#define UUID_STR_SIZE 37
+#define DEFAULT_HASH_SHA256_LEN 32
+
+/* FW Extended Manifest Header id = $AE1 */
+#define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124
+
+struct skl_dfw_module_mod {
+ char name[100];
+ struct skl_dfw_module skl_dfw_mod;
+};
+
+struct UUID {
+ u8 id[16];
+};
+
+union seg_flags {
+ u32 ul;
+ struct {
+ u32 contents : 1;
+ u32 alloc : 1;
+ u32 load : 1;
+ u32 read_only : 1;
+ u32 code : 1;
+ u32 data : 1;
+ u32 _rsvd0 : 2;
+ u32 type : 4;
+ u32 _rsvd1 : 4;
+ u32 length : 16;
+ } r;
+} __packed;
+
+struct segment_desc {
+ union seg_flags flags;
+ u32 v_base_addr;
+ u32 file_offset;
+};
+
+struct module_type {
+ u32 load_type : 4;
+ u32 auto_start : 1;
+ u32 domain_ll : 1;
+ u32 domain_dp : 1;
+ u32 rsvd : 25;
+} __packed;
+
+struct adsp_module_entry {
+ u32 struct_id;
+ u8 name[8];
+ struct UUID uuid;
+ struct module_type type;
+ u8 hash1[DEFAULT_HASH_SHA256_LEN];
+ u32 entry_point;
+ u16 cfg_offset;
+ u16 cfg_count;
+ u32 affinity_mask;
+ u16 instance_max_count;
+ u16 instance_bss_size;
+ struct segment_desc segments[3];
+} __packed;
+
+struct adsp_fw_hdr {
+ u32 id;
+ u32 len;
+ u8 name[8];
+ u32 preload_page_count;
+ u32 fw_image_flags;
+ u32 feature_mask;
+ u16 major;
+ u16 minor;
+ u16 hotfix;
+ u16 build;
+ u32 num_modules;
+ u32 hw_buf_base;
+ u32 hw_buf_length;
+ u32 load_offset;
+} __packed;
+
+struct uuid_module {
+ uuid_le uuid;
+ int id;
+ int is_loadable;
+
+ struct list_head list;
+};
+
+struct skl_ext_manifest_hdr {
+ u32 id;
+ u32 len;
+ u16 version_major;
+ u16 version_minor;
+ u32 entries;
+};
+
+int snd_skl_get_module_info(struct skl_sst *ctx, u8 *uuid,
+ struct skl_dfw_module *dfw_config)
+{
+ struct uuid_module *module;
+ uuid_le *uuid_mod;
+
+ uuid_mod = (uuid_le *)uuid;
+
+ list_for_each_entry(module, &ctx->uuid_list, list) {
+ if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+ dfw_config->module_id = module->id;
+ dfw_config->is_loadable = module->is_loadable;
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
+
+/*
+ * Parse the firmware binary to get the UUID, module id
+ * and loadable flags
+ */
+int snd_skl_parse_uuids(struct sst_dsp *ctx, unsigned int offset)
+{
+ struct adsp_fw_hdr *adsp_hdr;
+ struct adsp_module_entry *mod_entry;
+ int i, num_entry;
+ uuid_le *uuid_bin;
+ const char *buf;
+ struct skl_sst *skl = ctx->thread_context;
+ struct uuid_module *module;
+ struct firmware stripped_fw;
+ unsigned int safe_file;
+
+ /* Get the FW pointer to derive ADSP header */
+ stripped_fw.data = ctx->fw->data;
+ stripped_fw.size = ctx->fw->size;
+
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
+ buf = stripped_fw.data;
+
+ /* check if we have enough space in file to move to header */
+ safe_file = sizeof(*adsp_hdr) + offset;
+ if (stripped_fw.size <= safe_file) {
+ dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
+ return -EINVAL;
+ }
+
+ adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
+
+ /* check 1st module entry is in file */
+ safe_file += adsp_hdr->len + sizeof(*mod_entry);
+ if (stripped_fw.size <= safe_file) {
+ dev_err(ctx->dev, "Small fw file size, No module entry\n");
+ return -EINVAL;
+ }
+
+ mod_entry = (struct adsp_module_entry *)
+ (buf + offset + adsp_hdr->len);
+
+ num_entry = adsp_hdr->num_modules;
+
+ /* check all entries are in file */
+ safe_file += num_entry * sizeof(*mod_entry);
+ if (stripped_fw.size <= safe_file) {
+ dev_err(ctx->dev, "Small fw file size, No modules\n");
+ return -EINVAL;
+ }
+
+
+ /*
+ * Read the UUID(GUID) from FW Manifest.
+ *
+ * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
+ * Populate the UUID table to store module_id and loadable flags
+ * for the module.
+ */
+
+ for (i = 0; i < num_entry; i++, mod_entry++) {
+ module = kzalloc(sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return -ENOMEM;
+
+ uuid_bin = (uuid_le *)mod_entry->uuid.id;
+ memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
+
+ module->id = i;
+ module->is_loadable = mod_entry->type.load_type;
+
+ list_add_tail(&module->list, &skl->uuid_list);
+
+ dev_dbg(ctx->dev,
+ "Adding uuid :%pUL mod id: %d Loadable: %d\n",
+ &module->uuid, module->id, module->is_loadable);
+ }
+
+ return 0;
+}
+
+void skl_freeup_uuid_list(struct skl_sst *ctx)
+{
+ struct uuid_module *uuid, *_uuid;
+
+ list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
+ list_del(&uuid->list);
+ kfree(uuid);
+ }
+}
+
+/*
+ * some firmware binary contains some extended manifest. This needs
+ * to be stripped in that case before we load and use that image.
+ *
+ * Get the module id for the module by checking
+ * the table for the UUID for the module
+ */
+int skl_dsp_strip_extended_manifest(struct firmware *fw)
+{
+ struct skl_ext_manifest_hdr *hdr;
+
+ /* check if fw file is greater than header we are looking */
+ if (fw->size < sizeof(hdr)) {
+ pr_err("%s: Firmware file small, no hdr\n", __func__);
+ return -EINVAL;
+ }
+
+ hdr = (struct skl_ext_manifest_hdr *)fw->data;
+
+ if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
+ fw->size -= hdr->len;
+ fw->data += hdr->len;
+ }
+
+ return 0;
+}
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index 13ec8d53b526..588f899ceb65 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -68,10 +68,13 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
return ret;
}
+#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284
+
static int skl_load_base_firmware(struct sst_dsp *ctx)
{
int ret = 0, i;
struct skl_sst *skl = ctx->thread_context;
+ struct firmware stripped_fw;
u32 reg;
skl->boot_complete = false;
@@ -81,11 +84,25 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
if (ret < 0) {
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
- skl_dsp_disable_core(ctx);
return -EIO;
}
}
+ ret = snd_skl_parse_uuids(ctx, SKL_ADSP_FW_BIN_HDR_OFFSET);
+ if (ret < 0) {
+ dev_err(ctx->dev,
+ "UUID parsing err: %d\n", ret);
+ release_firmware(ctx->fw);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
+ return ret;
+ }
+
+ /* check for extended manifest */
+ stripped_fw.data = ctx->fw->data;
+ stripped_fw.size = ctx->fw->size;
+
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
ret = skl_dsp_boot(ctx);
if (ret < 0) {
dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
@@ -119,7 +136,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
goto transfer_firmware_failed;
}
- ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
+ ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size);
if (ret < 0) {
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
goto transfer_firmware_failed;
@@ -133,67 +150,87 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
}
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ skl->fw_loaded = true;
}
return 0;
transfer_firmware_failed:
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
skl_load_base_firmware_failed:
- skl_dsp_disable_core(ctx);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
release_firmware(ctx->fw);
ctx->fw = NULL;
return ret;
}
-static int skl_set_dsp_D0(struct sst_dsp *ctx)
+static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
+ struct skl_ipc_dxstate_info dx;
+ struct skl_sst *skl = ctx->thread_context;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- ret = skl_load_base_firmware(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "unable to load firmware\n");
- return ret;
+ /* If core0 is being turned on, we need to load the FW */
+ if (core_id == SKL_DSP_CORE0_ID) {
+ ret = skl_load_base_firmware(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "unable to load firmware\n");
+ return ret;
+ }
}
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ /*
+ * If any core other than core 0 is being moved to D0, enable the
+ * core and send the set dx IPC for the core.
+ */
+ if (core_id != SKL_DSP_CORE0_ID) {
+ ret = skl_dsp_enable_core(ctx, core_mask);
+ if (ret < 0)
+ return ret;
+
+ dx.core_mask = core_mask;
+ dx.dx_mask = core_mask;
+
+ ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
+ SKL_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n",
+ core_id);
+ skl_dsp_disable_core(ctx, core_mask);
+ }
+ }
+
+ skl->cores.state[core_id] = SKL_DSP_RUNNING;
return ret;
}
-static int skl_set_dsp_D3(struct sst_dsp *ctx)
+static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- dev_dbg(ctx->dev, "In %s:\n", __func__);
- mutex_lock(&ctx->mutex);
- if (!is_skl_dsp_running(ctx)) {
- mutex_unlock(&ctx->mutex);
- return 0;
- }
- mutex_unlock(&ctx->mutex);
-
- dx.core_mask = SKL_DSP_CORE0_MASK;
+ dx.core_mask = core_mask;
dx.dx_mask = SKL_IPC_D3_MASK;
+
ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID, SKL_BASE_FW_MODULE_ID, &dx);
if (ret < 0)
- dev_err(ctx->dev,
- "D3 request to FW failed, continuing reset: %d", ret);
-
- /* disable Interrupt */
- ctx->cl_dev.ops.cl_cleanup_controller(ctx);
- skl_cldma_int_disable(ctx);
- skl_ipc_op_int_disable(ctx);
- skl_ipc_int_disable(ctx);
-
- ret = skl_dsp_disable_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "disable dsp core failed ret: %d\n", ret);
- ret = -EIO;
+ dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret);
+
+ if (core_id == SKL_DSP_CORE0_ID) {
+ /* disable Interrupt */
+ ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+ skl_cldma_int_disable(ctx);
+ skl_ipc_op_int_disable(ctx);
+ skl_ipc_int_disable(ctx);
}
- skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ ret = skl_dsp_disable_core(ctx, core_mask);
+ if (ret < 0)
+ return ret;
+
+ skl->cores.state[core_id] = SKL_DSP_RESET;
return ret;
}
@@ -360,6 +397,19 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
return ret;
}
+void skl_clear_module_cnt(struct sst_dsp *ctx)
+{
+ struct skl_module_table *module;
+
+ if (list_empty(&ctx->module_list))
+ return;
+
+ list_for_each_entry(module, &ctx->module_list, list) {
+ module->usage_cnt = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(skl_clear_module_cnt);
+
static void skl_clear_module_table(struct sst_dsp *ctx)
{
struct skl_module_table *module, *tmp;
@@ -409,6 +459,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->dev = dev;
skl_dev.thread_context = skl;
+ INIT_LIST_HEAD(&skl->uuid_list);
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
@@ -432,12 +483,16 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
if (ret)
return ret;
+ skl->cores.count = 2;
+
ret = sst->fw_ops.load_fw(sst);
if (ret < 0) {
dev_err(dev, "Load base fw failed : %d", ret);
goto cleanup;
}
+ skl_dsp_init_core_state(sst);
+
if (dsp)
*dsp = skl;
@@ -452,6 +507,7 @@ EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
skl_clear_module_table(ctx->dsp);
+ skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
ctx->dsp->ops->free(ctx->dsp);
if (ctx->boot_complete) {
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index 3e036b0349b9..cc0150fc2601 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -379,43 +379,6 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
}
/*
- * A pipe can have multiple modules, each of them will be a DAPM widget as
- * well. While managing a pipeline we need to get the list of all the
- * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
- * to get the SKL type widgets in that pipeline
- */
-static int skl_tplg_alloc_pipe_widget(struct device *dev,
- struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
-{
- struct skl_module_cfg *src_module = NULL;
- struct snd_soc_dapm_path *p = NULL;
- struct skl_pipe_module *p_module = NULL;
-
- p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
- if (!p_module)
- return -ENOMEM;
-
- p_module->w = w;
- list_add_tail(&p_module->node, &pipe->w_list);
-
- snd_soc_dapm_widget_for_each_sink_path(w, p) {
- if ((p->sink->priv == NULL)
- && (!is_skl_dsp_widget_type(w)))
- continue;
-
- if ((p->sink->priv != NULL) && p->connect
- && is_skl_dsp_widget_type(p->sink)) {
-
- src_module = p->sink->priv;
- if (pipe->ppl_id == src_module->pipe->ppl_id)
- skl_tplg_alloc_pipe_widget(dev,
- p->sink, pipe);
- }
- }
- return 0;
-}
-
-/*
* some modules can have multiple params set from user control and
* need to be set after module is initialized. If set_param flag is
* set module params will be done after module is initialised.
@@ -448,7 +411,7 @@ static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
if (bc->set_params == SKL_PARAM_SET) {
ret = skl_set_module_params(ctx,
- (u32 *)bc->params, bc->max,
+ (u32 *)bc->params, bc->size,
bc->param_id, mconfig);
if (ret < 0)
return ret;
@@ -483,7 +446,7 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
continue;
mconfig->formats_config.caps = (u32 *)&bc->params;
- mconfig->formats_config.caps_size = bc->max;
+ mconfig->formats_config.caps_size = bc->size;
break;
}
@@ -514,8 +477,6 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
if (!skl_is_pipe_mcps_avail(skl, mconfig))
return -ENOMEM;
- skl_tplg_alloc_pipe_mcps(skl, mconfig);
-
if (mconfig->is_loadable && ctx->dsp->fw_ops.load_mod) {
ret = ctx->dsp->fw_ops.load_mod(ctx->dsp,
mconfig->id.module_id, mconfig->guid);
@@ -539,6 +500,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
if (ret < 0)
return ret;
+ skl_tplg_alloc_pipe_mcps(skl, mconfig);
ret = skl_tplg_set_module_params(w, ctx);
if (ret < 0)
return ret;
@@ -591,9 +553,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
if (!skl_is_pipe_mem_avail(skl, mconfig))
return -ENOMEM;
- skl_tplg_alloc_pipe_mem(skl, mconfig);
- skl_tplg_alloc_pipe_mcps(skl, mconfig);
-
/*
* Create a list of modules for pipe.
* This list contains modules from source to sink
@@ -602,19 +561,8 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
if (ret < 0)
return ret;
- /*
- * we create a w_list of all widgets in that pipe. This list is not
- * freed on PMD event as widgets within a pipe are static. This
- * saves us cycles to get widgets in pipe every time.
- *
- * So if we have already initialized all the widgets of a pipeline
- * we skip, so check for list_empty and create the list if empty
- */
- if (list_empty(&s_pipe->w_list)) {
- ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
- if (ret < 0)
- return ret;
- }
+ skl_tplg_alloc_pipe_mem(skl, mconfig);
+ skl_tplg_alloc_pipe_mcps(skl, mconfig);
/* Init all pipe modules from source to sink */
ret = skl_tplg_init_pipe_modules(skl, s_pipe);
@@ -949,13 +897,17 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
struct skl_pipe *s_pipe = mconfig->pipe;
int ret = 0;
+ if (s_pipe->state == SKL_PIPE_INVALID)
+ return -EINVAL;
+
skl_tplg_free_pipe_mcps(skl, mconfig);
skl_tplg_free_pipe_mem(skl, mconfig);
list_for_each_entry(w_module, &s_pipe->w_list, node) {
dst_module = w_module->w->priv;
- skl_tplg_free_pipe_mcps(skl, dst_module);
+ if (mconfig->m_state >= SKL_MODULE_INIT_DONE)
+ skl_tplg_free_pipe_mcps(skl, dst_module);
if (src_module == NULL) {
src_module = dst_module;
continue;
@@ -1102,7 +1054,7 @@ static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
if (w->power)
skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
- bc->max, bc->param_id, mconfig);
+ bc->size, bc->param_id, mconfig);
/* decrement size for TLV header */
size -= 2 * sizeof(u32);
@@ -1136,6 +1088,10 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
struct skl *skl = get_skl_ctx(w->dapm->dev);
if (ac->params) {
+ if (size > ac->max)
+ return -EINVAL;
+
+ ac->size = size;
/*
* if the param_is is of type Vendor, firmware expects actual
* parameter id and size from the control.
@@ -1151,7 +1107,7 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
if (w->power)
return skl_set_module_params(skl->skl_sst,
- (u32 *)ac->params, ac->max,
+ (u32 *)ac->params, ac->size,
ac->param_id, mconfig);
}
@@ -1159,6 +1115,39 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
}
/*
+ * Fill the dma id for host and link. In case of passthrough
+ * pipeline, this will both host and link in the same
+ * pipeline, so need to copy the link and host based on dev_type
+ */
+static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mcfg->pipe;
+
+ if (pipe->passthru) {
+ switch (mcfg->dev_type) {
+ case SKL_DEVICE_HDALINK:
+ pipe->p_params->link_dma_id = params->link_dma_id;
+ break;
+
+ case SKL_DEVICE_HDAHOST:
+ pipe->p_params->host_dma_id = params->host_dma_id;
+ break;
+
+ default:
+ break;
+ }
+ pipe->p_params->s_fmt = params->s_fmt;
+ pipe->p_params->ch = params->ch;
+ pipe->p_params->s_freq = params->s_freq;
+ pipe->p_params->stream = params->stream;
+
+ } else {
+ memcpy(pipe->p_params, params, sizeof(*params));
+ }
+}
+
+/*
* The FE params are passed by hw_params of the DAI.
* On hw_params, the params are stored in Gateway module of the FE and we
* need to calculate the format in DSP module configuration, that
@@ -1168,10 +1157,9 @@ int skl_tplg_update_pipe_params(struct device *dev,
struct skl_module_cfg *mconfig,
struct skl_pipe_params *params)
{
- struct skl_pipe *pipe = mconfig->pipe;
struct skl_module_fmt *format = NULL;
- memcpy(pipe->p_params, params, sizeof(*params));
+ skl_tplg_fill_dma_id(mconfig, params);
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
format = &mconfig->in_fmt[0];
@@ -1358,12 +1346,11 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
struct skl_module_cfg *mconfig,
struct skl_pipe_params *params)
{
- struct skl_pipe *pipe = mconfig->pipe;
struct nhlt_specific_cfg *cfg;
struct skl *skl = get_skl_ctx(dai->dev);
int link_type = skl_tplg_be_link_type(mconfig->dev_type);
- memcpy(pipe->p_params, params, sizeof(*params));
+ skl_tplg_fill_dma_id(mconfig, params);
if (link_type == NHLT_LINK_HDA)
return 0;
@@ -1554,6 +1541,55 @@ static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
}
}
+static void skl_clear_pin_config(struct snd_soc_platform *platform,
+ struct snd_soc_dapm_widget *w)
+{
+ int i;
+ struct skl_module_cfg *mconfig;
+ struct skl_pipe *pipe;
+
+ if (!strncmp(w->dapm->component->name, platform->component.name,
+ strlen(platform->component.name))) {
+ mconfig = w->priv;
+ pipe = mconfig->pipe;
+ for (i = 0; i < mconfig->max_in_queue; i++) {
+ mconfig->m_in_pin[i].in_use = false;
+ mconfig->m_in_pin[i].pin_state = SKL_PIN_UNBIND;
+ }
+ for (i = 0; i < mconfig->max_out_queue; i++) {
+ mconfig->m_out_pin[i].in_use = false;
+ mconfig->m_out_pin[i].pin_state = SKL_PIN_UNBIND;
+ }
+ pipe->state = SKL_PIPE_INVALID;
+ mconfig->m_state = SKL_MODULE_UNINIT;
+ }
+}
+
+void skl_cleanup_resources(struct skl *skl)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+ struct snd_soc_platform *soc_platform = skl->platform;
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_card *card;
+
+ if (soc_platform == NULL)
+ return;
+
+ card = soc_platform->component.card;
+ if (!card || !card->instantiated)
+ return;
+
+ skl->resource.mem = 0;
+ skl->resource.mcps = 0;
+
+ list_for_each_entry(w, &card->widgets, list) {
+ if (is_skl_dsp_widget_type(w) && (w->priv != NULL))
+ skl_clear_pin_config(soc_platform, w);
+ }
+
+ skl_clear_module_cnt(ctx->dsp);
+}
+
/*
* Topology core widget load callback
*
@@ -1585,6 +1621,10 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
w->priv = mconfig;
memcpy(&mconfig->guid, &dfw_config->uuid, 16);
+ ret = snd_skl_get_module_info(skl->skl_sst, mconfig->guid, dfw_config);
+ if (ret < 0)
+ return ret;
+
mconfig->id.module_id = dfw_config->module_id;
mconfig->id.instance_id = dfw_config->instance_id;
mconfig->mcps = dfw_config->max_mcps;
@@ -1683,6 +1723,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
ac->max = dfw_ac->max;
ac->param_id = dfw_ac->param_id;
ac->set_params = dfw_ac->set_params;
+ ac->size = dfw_ac->max;
if (ac->max) {
ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
@@ -1733,6 +1774,60 @@ static struct snd_soc_tplg_ops skl_tplg_ops = {
.bytes_ext_ops_count = ARRAY_SIZE(skl_tlv_ops),
};
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_create_pipe_widget_list()
+ * helps to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_create_pipe_widget_list(struct snd_soc_platform *platform)
+{
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mcfg = NULL;
+ struct skl_pipe_module *p_module = NULL;
+ struct skl_pipe *pipe;
+
+ list_for_each_entry(w, &platform->component.card->widgets, list) {
+ if (is_skl_dsp_widget_type(w) && w->priv != NULL) {
+ mcfg = w->priv;
+ pipe = mcfg->pipe;
+
+ p_module = devm_kzalloc(platform->dev,
+ sizeof(*p_module), GFP_KERNEL);
+ if (!p_module)
+ return -ENOMEM;
+
+ p_module->w = w;
+ list_add_tail(&p_module->node, &pipe->w_list);
+ }
+ }
+
+ return 0;
+}
+
+static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe)
+{
+ struct skl_pipe_module *w_module;
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+ bool host_found = false, link_found = false;
+
+ list_for_each_entry(w_module, &pipe->w_list, node) {
+ w = w_module->w;
+ mconfig = w->priv;
+
+ if (mconfig->dev_type == SKL_DEVICE_HDAHOST)
+ host_found = true;
+ else if (mconfig->dev_type != SKL_DEVICE_NONE)
+ link_found = true;
+ }
+
+ if (host_found && link_found)
+ pipe->passthru = true;
+ else
+ pipe->passthru = false;
+}
+
/* This will be read from topology manifest, currently defined here */
#define SKL_MAX_MCPS 30000000
#define SKL_FW_MAX_MEM 1000000
@@ -1746,6 +1841,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
const struct firmware *fw;
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl *skl = ebus_to_skl(ebus);
+ struct skl_pipeline *ppl;
ret = request_firmware(&fw, skl->tplg_name, bus->dev);
if (ret < 0) {
@@ -1775,6 +1871,12 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
skl->resource.max_mem = SKL_FW_MAX_MEM;
skl->tplg = fw;
+ ret = skl_tplg_create_pipe_widget_list(platform);
+ if (ret < 0)
+ return ret;
+
+ list_for_each_entry(ppl, &skl->ppl_list, node)
+ skl_tplg_set_pipe_type(skl, ppl->pipe);
return 0;
}
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index e4b399cd7868..22d3ef83817d 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -244,7 +244,8 @@ enum skl_pipe_state {
SKL_PIPE_INVALID = 0,
SKL_PIPE_CREATED = 1,
SKL_PIPE_PAUSED = 2,
- SKL_PIPE_STARTED = 3
+ SKL_PIPE_STARTED = 3,
+ SKL_PIPE_RESET = 4
};
struct skl_pipe_module {
@@ -270,6 +271,7 @@ struct skl_pipe {
struct skl_pipe_params *p_params;
enum skl_pipe_state state;
struct list_head w_list;
+ bool passthru;
};
enum skl_module_state {
@@ -319,6 +321,7 @@ struct skl_algo_data {
u32 param_id;
u32 set_params;
u32 max;
+ u32 size;
char *params;
};
@@ -357,6 +360,8 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
+
int skl_init_module(struct skl_sst *ctx, struct skl_module_cfg *module_config);
int skl_bind_modules(struct skl_sst *ctx, struct skl_module_cfg
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 06d8c263c68f..cd59536a761d 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -35,6 +35,8 @@
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
+static struct skl_machine_pdata skl_dmic_data;
+
/*
* initialize the PCI registers
*/
@@ -184,6 +186,7 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
{
struct skl *skl = ebus_to_skl(ebus);
struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct pci_dev *pci = to_pci_dev(bus->dev);
int ret;
snd_hdac_ext_bus_link_power_down_all(ebus);
@@ -193,9 +196,12 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
return ret;
snd_hdac_bus_stop_chip(bus);
+ update_pci_dword(pci, AZX_PCIREG_PGCTL,
+ AZX_PGCTL_LSRMD_MASK, AZX_PGCTL_LSRMD_MASK);
skl_enable_miscbdcge(bus->dev, false);
snd_hdac_bus_enter_link_reset(bus);
skl_enable_miscbdcge(bus->dev, true);
+ skl_cleanup_resources(skl);
return 0;
}
@@ -242,6 +248,7 @@ static int skl_suspend(struct device *dev)
ret = _skl_suspend(ebus);
if (ret < 0)
return ret;
+ skl->skl_sst->fw_loaded = false;
}
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
@@ -397,6 +404,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data)
platform_device_put(pdev);
return -EIO;
}
+
+ if (mach->pdata)
+ dev_set_drvdata(&pdev->dev, mach->pdata);
+
skl->i2s_dev = pdev;
return 0;
@@ -657,6 +668,8 @@ static int skl_probe(struct pci_dev *pci,
skl->pci_id = pci->device;
+ device_disable_async_suspend(bus->dev);
+
skl->nhlt = skl_nhlt_init(bus->dev);
if (skl->nhlt == NULL)
@@ -666,6 +679,8 @@ static int skl_probe(struct pci_dev *pci,
pci_set_drvdata(skl->pci, ebus);
+ skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
+
/* check if dsp is there */
if (ebus->ppcap) {
err = skl_machine_device_register(skl,
@@ -713,7 +728,7 @@ static int skl_probe(struct pci_dev *pci,
list_for_each_entry(hlink, &ebus->hlink_list, list)
snd_hdac_ext_bus_link_put(ebus, hlink);
- /*configure PM */
+ /* configure PM */
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
@@ -766,8 +781,7 @@ static void skl_remove(struct pci_dev *pci)
struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
struct skl *skl = ebus_to_skl(ebus);
- if (skl->tplg)
- release_firmware(skl->tplg);
+ release_firmware(skl->tplg);
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
@@ -786,15 +800,23 @@ static void skl_remove(struct pci_dev *pci)
static struct sst_acpi_mach sst_skl_devdata[] = {
{ "INT343A", "skl_alc286s_i2s", "intel/dsp_fw_release.bin", NULL, NULL, NULL },
- { "INT343B", "skl_nau88l25_ssm4567_i2s", "intel/dsp_fw_release.bin",
- NULL, NULL, NULL },
- { "MX98357A", "skl_nau88l25_max98357a_i2s", "intel/dsp_fw_release.bin",
- NULL, NULL, NULL },
+ { "INT343B", "skl_n88l25_s4567", "intel/dsp_fw_release.bin",
+ NULL, NULL, &skl_dmic_data },
+ { "MX98357A", "skl_n88l25_m98357a", "intel/dsp_fw_release.bin",
+ NULL, NULL, &skl_dmic_data },
{}
};
static struct sst_acpi_mach sst_bxtp_devdata[] = {
{ "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+ { "DLGS7219", "bxt_da7219_max98357a_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+};
+
+static struct sst_acpi_mach sst_kbl_devdata[] = {
+ { "INT343A", "kbl_alc286s_i2s", "intel/dsp_fw_kbl.bin", NULL, NULL, NULL },
+ { "INT343B", "kbl_n88l25_s4567", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
+ { "MX98357A", "kbl_n88l25_m98357a", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
+ {}
};
/* PCI IDs */
@@ -805,6 +827,9 @@ static const struct pci_device_id skl_ids[] = {
/* BXT-P */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = (unsigned long)&sst_bxtp_devdata},
+ /* KBL */
+ { PCI_DEVICE(0x8086, 0x9D71),
+ .driver_data = (unsigned long)&sst_kbl_devdata},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, skl_ids);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 4b4b3876aea9..9064e5b0d676 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -48,6 +48,8 @@
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
+#define AZX_PCIREG_PGCTL 0x44
+#define AZX_PGCTL_LSRMD_MASK (1 << 4)
#define AZX_PCIREG_CGCTL 0x48
#define AZX_CGCTL_MISCBDCGE_MASK (1 << 6)
@@ -65,6 +67,7 @@ struct skl {
unsigned int init_failed:1; /* delayed init failed */
struct platform_device *dmic_dev;
struct platform_device *i2s_dev;
+ struct snd_soc_platform *platform;
struct nhlt_acpi_table *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
@@ -90,6 +93,11 @@ struct skl_dma_params {
u8 stream_tag;
};
+/* to pass dmic data */
+struct skl_machine_pdata {
+ u32 dmic_num;
+};
+
struct skl_dsp_ops {
int id;
struct skl_dsp_loader_ops (*loader_ops)(void);
@@ -108,9 +116,11 @@ void skl_nhlt_free(struct nhlt_acpi_table *addr);
struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
+int skl_get_dmic_geo(struct skl *skl);
int skl_nhlt_update_topology_bin(struct skl *skl);
int skl_init_dsp(struct skl *skl);
int skl_free_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl);
+void skl_cleanup_resources(struct skl *skl);
#endif /* __SOUND_SOC_SKL_H */
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 49354d17ea55..c4c51a4d3c8f 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -518,7 +518,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
}
}
- rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+ rsnd_mod_bset(adg_mod, SSICKR, 0x80FF0000, ckr);
rsnd_mod_write(adg_mod, BRRA, rbga);
rsnd_mod_write(adg_mod, BRRB, rbgb);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c4464858bf01..cc8f480251e7 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -3282,6 +3282,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
mutex_unlock(&dapm->card->dapm_mutex);
return w;
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,