summaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c34
-rw-r--r--sound/soc/atmel/atmel_wm8904.c50
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c8
-rw-r--r--sound/soc/codecs/Kconfig22
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ac97.c4
-rw-r--r--sound/soc/codecs/adau1701.c6
-rw-r--r--sound/soc/codecs/adau1977.c2
-rw-r--r--sound/soc/codecs/ak4642.c4
-rw-r--r--sound/soc/codecs/ak5386.c50
-rw-r--r--sound/soc/codecs/arizona.c288
-rw-r--r--sound/soc/codecs/arizona.h1
-rw-r--r--sound/soc/codecs/cs4265.c682
-rw-r--r--sound/soc/codecs/cs4265.h64
-rw-r--r--sound/soc/codecs/cs42l52.c14
-rw-r--r--sound/soc/codecs/cs42l56.c76
-rw-r--r--sound/soc/codecs/cs42l73.c2
-rw-r--r--sound/soc/codecs/cs42xx8.c5
-rw-r--r--sound/soc/codecs/cs42xx8.h8
-rw-r--r--sound/soc/codecs/cx20442.c10
-rw-r--r--sound/soc/codecs/max98090.c44
-rw-r--r--sound/soc/codecs/mc13783.c6
-rw-r--r--sound/soc/codecs/pcm1792a.c3
-rw-r--r--sound/soc/codecs/pcm1792a.h3
-rw-r--r--sound/soc/codecs/rt286.c1224
-rw-r--r--sound/soc/codecs/rt286.h198
-rw-r--r--sound/soc/codecs/sgtl5000.c11
-rw-r--r--sound/soc/codecs/sigmadsp-i2c.c35
-rw-r--r--sound/soc/codecs/sigmadsp-regmap.c36
-rw-r--r--sound/soc/codecs/sigmadsp.c65
-rw-r--r--sound/soc/codecs/sigmadsp.h20
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c9
-rw-r--r--sound/soc/codecs/tlv320aic3x.c2
-rw-r--r--sound/soc/codecs/tlv320dac33.c2
-rw-r--r--sound/soc/codecs/twl4030.c5
-rw-r--r--sound/soc/codecs/uda134x.c2
-rw-r--r--sound/soc/codecs/wm5100.c3
-rw-r--r--sound/soc/codecs/wm5102.c65
-rw-r--r--sound/soc/codecs/wm5110.c4
-rw-r--r--sound/soc/codecs/wm8350.c3
-rw-r--r--sound/soc/codecs/wm8903.c3
-rw-r--r--sound/soc/codecs/wm8960.c2
-rw-r--r--sound/soc/codecs/wm8994.c9
-rw-r--r--sound/soc/codecs/wm8996.c6
-rw-r--r--sound/soc/codecs/wm8997.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c6
-rw-r--r--sound/soc/davinci/Kconfig25
-rw-r--r--sound/soc/davinci/Makefile2
-rw-r--r--sound/soc/davinci/davinci-mcasp.c93
-rw-r--r--sound/soc/davinci/edma-pcm.c2
-rw-r--r--sound/soc/davinci/edma-pcm.h7
-rw-r--r--sound/soc/fsl/Kconfig16
-rw-r--r--sound/soc/fsl/Makefile2
-rw-r--r--sound/soc/fsl/fsl_asrc.c995
-rw-r--r--sound/soc/fsl/fsl_asrc.h461
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c391
-rw-r--r--sound/soc/fsl/fsl_dma.c4
-rw-r--r--sound/soc/fsl/fsl_esai.c2
-rw-r--r--sound/soc/fsl/fsl_sai.c39
-rw-r--r--sound/soc/fsl/fsl_spdif.c94
-rw-r--r--sound/soc/fsl/fsl_spdif.h10
-rw-r--r--sound/soc/fsl/fsl_ssi.c6
-rw-r--r--sound/soc/fsl/imx-audmux.c8
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c1
-rw-r--r--sound/soc/generic/simple-card.c13
-rw-r--r--sound/soc/intel/Kconfig12
-rw-r--r--sound/soc/intel/Makefile2
-rw-r--r--sound/soc/intel/broadwell.c251
-rw-r--r--sound/soc/intel/byt-max98090.c27
-rw-r--r--sound/soc/intel/byt-rt5640.c1
-rw-r--r--sound/soc/intel/sst-atom-controls.h30
-rw-r--r--sound/soc/intel/sst-baytrail-ipc.c30
-rw-r--r--sound/soc/intel/sst-baytrail-pcm.c2
-rw-r--r--sound/soc/intel/sst-dsp.c10
-rw-r--r--sound/soc/intel/sst-dsp.h39
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c70
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c40
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c27
-rw-r--r--sound/soc/intel/sst-mfld-dsp.h429
-rw-r--r--sound/soc/intel/sst-mfld-platform-compress.c11
-rw-r--r--sound/soc/intel/sst-mfld-platform-pcm.c319
-rw-r--r--sound/soc/intel/sst-mfld-platform.h29
-rw-r--r--sound/soc/omap/ams-delta.c2
-rw-r--r--sound/soc/pxa/Kconfig11
-rw-r--r--sound/soc/s6000/s6000-i2s.c4
-rw-r--r--sound/soc/samsung/i2s.c29
-rw-r--r--sound/soc/sh/Kconfig2
-rw-r--r--sound/soc/sh/fsi.c189
-rw-r--r--sound/soc/sh/rcar/core.c247
-rw-r--r--sound/soc/sh/rcar/dvc.c135
-rw-r--r--sound/soc/sh/rcar/gen.c554
-rw-r--r--sound/soc/sh/rcar/rsnd.h26
-rw-r--r--sound/soc/sh/rcar/src.c86
-rw-r--r--sound/soc/sh/rcar/ssi.c33
-rw-r--r--sound/soc/soc-cache.c7
-rw-r--r--sound/soc/soc-compress.c13
-rw-r--r--sound/soc/soc-core.c879
-rw-r--r--sound/soc/soc-dapm.c306
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c37
-rw-r--r--sound/soc/soc-jack.c4
-rw-r--r--sound/soc/soc-pcm.c581
-rw-r--r--sound/soc/tegra/tegra_alc5632.c5
-rw-r--r--sound/soc/tegra/tegra_max98090.c5
-rw-r--r--sound/soc/tegra/tegra_rt5640.c5
-rw-r--r--sound/soc/tegra/tegra_wm8753.c3
-rw-r--r--sound/soc/tegra/tegra_wm8903.c5
-rw-r--r--sound/soc/tegra/trimslice.c3
107 files changed, 7857 insertions, 1920 deletions
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index de433cfd..f403f39 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -347,6 +347,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
u32 tfmr, rfmr, tcmr, rcmr;
int start_event;
int ret;
+ int fslen, fslen_ext;
/*
* Currently, there is only one set of dma params for
@@ -388,18 +389,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
}
/*
- * The SSC only supports up to 16-bit samples in I2S format, due
- * to the size of the Frame Mode Register FSLEN field.
- */
- if ((ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_I2S
- && bits > 16) {
- printk(KERN_WARNING
- "atmel_ssc_dai: sample size %d "
- "is too large for I2S\n", bits);
- return -EINVAL;
- }
-
- /*
* Compute SSC register settings.
*/
switch (ssc_p->daifmt
@@ -413,6 +402,17 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
* from the MCK divider, and the BCLK signal
* is output on the SSC TK line.
*/
+
+ if (bits > 16 && !ssc->pdata->has_fslen_ext) {
+ dev_err(dai->dev,
+ "sample size %d is too large for SSC device\n",
+ bits);
+ return -EINVAL;
+ }
+
+ fslen_ext = (bits - 1) / 16;
+ fslen = (bits - 1) % 16;
+
rcmr = SSC_BF(RCMR_PERIOD, ssc_p->rcmr_period)
| SSC_BF(RCMR_STTDLY, START_DELAY)
| SSC_BF(RCMR_START, SSC_START_FALLING_RF)
@@ -420,9 +420,10 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
| SSC_BF(RCMR_CKO, SSC_CKO_NONE)
| SSC_BF(RCMR_CKS, SSC_CKS_DIV);
- rfmr = SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ rfmr = SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
| SSC_BF(RFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(RFMR_FSLEN, (bits - 1))
+ | SSC_BF(RFMR_FSLEN, fslen)
| SSC_BF(RFMR_DATNB, (channels - 1))
| SSC_BIT(RFMR_MSBF)
| SSC_BF(RFMR_LOOP, 0)
@@ -435,10 +436,11 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS)
| SSC_BF(TCMR_CKS, SSC_CKS_DIV);
- tfmr = SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
+ tfmr = SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
+ | SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
| SSC_BF(TFMR_FSDEN, 0)
| SSC_BF(TFMR_FSOS, SSC_FSOS_NEGATIVE)
- | SSC_BF(TFMR_FSLEN, (bits - 1))
+ | SSC_BF(TFMR_FSLEN, fslen)
| SSC_BF(TFMR_DATNB, (channels - 1))
| SSC_BIT(TFMR_MSBF)
| SSC_BF(TFMR_DATDEF, 0)
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
index b4e3690..4052268 100644
--- a/sound/soc/atmel/atmel_wm8904.c
+++ b/sound/soc/atmel/atmel_wm8904.c
@@ -18,10 +18,6 @@
#include "../codecs/wm8904.h"
#include "atmel_ssc_dai.h"
-#define MCLK_RATE 32768
-
-static struct clk *mclk;
-
static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("Mic", NULL),
@@ -61,26 +57,6 @@ static struct snd_soc_ops atmel_asoc_wm8904_ops = {
.hw_params = atmel_asoc_wm8904_hw_params,
};
-static int atmel_set_bias_level(struct snd_soc_card *card,
- struct snd_soc_dapm_context *dapm,
- enum snd_soc_bias_level level)
-{
- if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
- switch (level) {
- case SND_SOC_BIAS_PREPARE:
- clk_prepare_enable(mclk);
- break;
- case SND_SOC_BIAS_OFF:
- clk_disable_unprepare(mclk);
- break;
- default:
- break;
- }
- }
-
- return 0;
-};
-
static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
.name = "WM8904",
.stream_name = "WM8904 PCM",
@@ -94,7 +70,6 @@ static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
static struct snd_soc_card atmel_asoc_wm8904_card = {
.name = "atmel_asoc_wm8904",
.owner = THIS_MODULE,
- .set_bias_level = atmel_set_bias_level,
.dai_link = &atmel_asoc_wm8904_dailink,
.num_links = 1,
.dapm_widgets = atmel_asoc_wm8904_dapm_widgets,
@@ -153,7 +128,6 @@ static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &atmel_asoc_wm8904_card;
struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
- struct clk *clk_src;
int id, ret;
card->dev = &pdev->dev;
@@ -170,30 +144,6 @@ static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
return ret;
}
- mclk = clk_get(NULL, "pck0");
- if (IS_ERR(mclk)) {
- dev_err(&pdev->dev, "failed to get pck0\n");
- ret = PTR_ERR(mclk);
- goto err_set_audio;
- }
-
- clk_src = clk_get(NULL, "clk32k");
- if (IS_ERR(clk_src)) {
- dev_err(&pdev->dev, "failed to get clk32k\n");
- ret = PTR_ERR(clk_src);
- goto err_set_audio;
- }
-
- ret = clk_set_parent(mclk, clk_src);
- clk_put(clk_src);
- if (ret != 0) {
- dev_err(&pdev->dev, "failed to set MCLK parent\n");
- goto err_set_audio;
- }
-
- dev_info(&pdev->dev, "setting pck0 to %dHz\n", MCLK_RATE);
- clk_set_rate(mclk, MCLK_RATE);
-
ret = snd_soc_register_card(card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed\n");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
index a3881c4..bcf5913 100644
--- a/sound/soc/blackfin/bf5xx-i2s-pcm.c
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -290,19 +290,19 @@ static int bf5xx_pcm_silence(struct snd_pcm_substream *substream,
unsigned int sample_size = runtime->sample_bits / 8;
void *buf = runtime->dma_area;
struct bf5xx_i2s_pcm_data *dma_data;
- unsigned int offset, size;
+ unsigned int offset, samples;
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
if (dma_data->tdm_mode) {
offset = pos * 8 * sample_size;
- size = count * 8 * sample_size;
+ samples = count * 8;
} else {
offset = frames_to_bytes(runtime, pos);
- size = frames_to_bytes(runtime, count);
+ samples = count * runtime->channels;
}
- snd_pcm_format_set_silence(runtime->format, buf + offset, size);
+ snd_pcm_format_set_silence(runtime->format, buf + offset, samples);
return 0;
}
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index cbfa1e1..4c75425 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -47,6 +47,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS42L52 if I2C && INPUT
select SND_SOC_CS42L56 if I2C && INPUT
select SND_SOC_CS42L73 if I2C
+ select SND_SOC_CS4265 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
select SND_SOC_CS42XX8_I2C if I2C
@@ -74,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
+ select SND_SOC_RT286 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
@@ -225,11 +227,11 @@ config SND_SOC_ADAU1373
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
depends on I2C
- select SND_SOC_SIGMADSP
+ select SND_SOC_SIGMADSP_I2C
config SND_SOC_ADAU17X1
tristate
- select SND_SOC_SIGMADSP
+ select SND_SOC_SIGMADSP_REGMAP
config SND_SOC_ADAU1761
tristate
@@ -338,6 +340,11 @@ config SND_SOC_CS42L73
tristate "Cirrus Logic CS42L73 CODEC"
depends on I2C
+config SND_SOC_CS4265
+ tristate "Cirrus Logic CS4265 CODEC"
+ depends on I2C
+ select REGMAP_I2C
+
# Cirrus Logic CS4270 Codec
config SND_SOC_CS4270
tristate "Cirrus Logic CS4270 CODEC"
@@ -449,6 +456,9 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
+config SND_SOC_RT286
+ tristate
+
config SND_SOC_RT5631
tristate
@@ -476,6 +486,14 @@ config SND_SOC_SIGMADSP
tristate
select CRC32
+config SND_SOC_SIGMADSP_I2C
+ tristate
+ select SND_SOC_SIGMADSP
+
+config SND_SOC_SIGMADSP_REGMAP
+ tristate
+ select SND_SOC_SIGMADSP
+
config SND_SOC_SIRF_AUDIO_CODEC
tristate "SiRF SoC internal audio codec"
select REGMAP_MMIO
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index be3377b..ade412e 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -37,6 +37,7 @@ snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
snd-soc-cs42l56-objs := cs42l56.o
snd-soc-cs42l73-objs := cs42l73.o
+snd-soc-cs4265-objs := cs4265.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-cs4271-objs := cs4271.o
snd-soc-cs42xx8-objs := cs42xx8.o
@@ -68,6 +69,7 @@ snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rl6231-objs := rl6231.o
+snd-soc-rt286-objs := rt286.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
@@ -77,6 +79,8 @@ snd-soc-sgtl5000-objs := sgtl5000.o
snd-soc-alc5623-objs := alc5623.o
snd-soc-alc5632-objs := alc5632.o
snd-soc-sigmadsp-objs := sigmadsp.o
+snd-soc-sigmadsp-i2c-objs := sigmadsp-i2c.o
+snd-soc-sigmadsp-regmap-objs := sigmadsp-regmap.o
snd-soc-si476x-objs := si476x.o
snd-soc-sirf-audio-codec-objs := sirf-audio-codec.o
snd-soc-sn95031-objs := sn95031.o
@@ -202,6 +206,7 @@ obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o
obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o
+obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
@@ -233,6 +238,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
+obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
@@ -240,6 +246,8 @@ obj-$(CONFIG_SND_SOC_RT5651) += snd-soc-rt5651.o
obj-$(CONFIG_SND_SOC_RT5677) += snd-soc-rt5677.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
+obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o
+obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 8d9ba4b..e889e1b 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -89,8 +89,8 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL,
- &ac97_bus);
+ ret = snd_ac97_bus(codec->component.card->snd_card, 0, soc_ac97_ops,
+ NULL, &ac97_bus);
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index d71c59c..370b742 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -230,8 +230,10 @@ static int adau1701_reg_read(void *context, unsigned int reg,
*value = 0;
- for (i = 0; i < size; i++)
- *value |= recv_buf[i] << (i * 8);
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
return 0;
}
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index fd55da7..70ab357 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -968,7 +968,7 @@ int adau1977_probe(struct device *dev, struct regmap *regmap,
if (adau1977->dvdd_reg)
power_off_mask = ~0;
else
- power_off_mask = ~ADAU1977_BLOCK_POWER_SAI_LDO_EN;
+ power_off_mask = (unsigned int)~ADAU1977_BLOCK_POWER_SAI_LDO_EN;
ret = regmap_update_bits(adau1977->regmap, ADAU1977_REG_BLOCK_POWER_SAI,
power_off_mask, 0x00);
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 3ba4c0f..0417125 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -547,7 +547,7 @@ static const struct ak4642_drvdata ak4648_drvdata = {
.extended_frequencies = 1,
};
-static struct of_device_id ak4642_of_match[];
+static const struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
@@ -593,7 +593,7 @@ static int ak4642_i2c_remove(struct i2c_client *client)
return 0;
}
-static struct of_device_id ak4642_of_match[] = {
+static const struct of_device_id ak4642_of_match[] = {
{ .compatible = "asahi-kasei,ak4642", .data = &ak4642_drvdata},
{ .compatible = "asahi-kasei,ak4643", .data = &ak4643_drvdata},
{ .compatible = "asahi-kasei,ak4648", .data = &ak4648_drvdata},
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index 72e953b..8107a1c 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -14,12 +14,18 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
+static const char * const supply_names[] = {
+ "va", "vd"
+};
+
struct ak5386_priv {
int reset_gpio;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
@@ -32,7 +38,42 @@ static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
{ "Capture", NULL, "AINR" },
};
+static int ak5386_soc_probe(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+
+static int ak5386_soc_remove(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ak5386_soc_suspend(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies);
+ return 0;
+}
+
+static int ak5386_soc_resume(struct snd_soc_codec *codec)
+{
+ struct ak5386_priv *priv = snd_soc_codec_get_drvdata(codec);
+ return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies);
+}
+#else
+#define ak5386_soc_suspend NULL
+#define ak5386_soc_resume NULL
+#endif /* CONFIG_PM */
+
static struct snd_soc_codec_driver soc_codec_ak5386 = {
+ .probe = ak5386_soc_probe,
+ .remove = ak5386_soc_remove,
+ .suspend = ak5386_soc_suspend,
+ .resume = ak5386_soc_resume,
.dapm_widgets = ak5386_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
.dapm_routes = ak5386_dapm_routes,
@@ -122,6 +163,7 @@ static int ak5386_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct ak5386_priv *priv;
+ int ret, i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
@@ -130,6 +172,14 @@ static int ak5386_probe(struct platform_device *pdev)
priv->reset_gpio = -EINVAL;
dev_set_drvdata(dev, priv);
+ for (i = 0; i < ARRAY_SIZE(supply_names); i++)
+ priv->supplies[i].supply = supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies),
+ priv->supplies);
+ if (ret < 0)
+ return ret;
+
if (of_match_device(of_match_ptr(ak5386_dt_ids), dev))
priv->reset_gpio = of_get_named_gpio(dev->of_node,
"reset-gpio", 0);
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 29e198f..2f2e91a 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -243,6 +243,31 @@ int arizona_init_spk(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_spk);
+static const struct snd_soc_dapm_route arizona_mono_routes[] = {
+ { "OUT1R", NULL, "OUT1L" },
+ { "OUT2R", NULL, "OUT2L" },
+ { "OUT3R", NULL, "OUT3L" },
+ { "OUT4R", NULL, "OUT4L" },
+ { "OUT5R", NULL, "OUT5L" },
+ { "OUT6R", NULL, "OUT6L" },
+};
+
+int arizona_init_mono(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int i;
+
+ for (i = 0; i < ARIZONA_MAX_OUTPUT; ++i) {
+ if (arizona->pdata.out_mono[i])
+ snd_soc_dapm_add_routes(&codec->dapm,
+ &arizona_mono_routes[i], 1);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_mono);
+
int arizona_init_gpio(struct snd_soc_codec *codec)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -1127,6 +1152,31 @@ static int arizona_startup(struct snd_pcm_substream *substream,
constraint);
}
+static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
+ unsigned int rate)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ struct reg_default dac_comp[] = {
+ { 0x80, 0x3 },
+ { ARIZONA_DAC_COMP_1, 0 },
+ { ARIZONA_DAC_COMP_2, 0 },
+ { 0x80, 0x0 },
+ };
+
+ mutex_lock(&codec->mutex);
+
+ dac_comp[1].def = arizona->dac_comp_coeff;
+ if (rate >= 176400)
+ dac_comp[2].def = arizona->dac_comp_enabled;
+
+ mutex_unlock(&codec->mutex);
+
+ regmap_multi_reg_write(arizona->regmap,
+ dac_comp,
+ ARRAY_SIZE(dac_comp));
+}
+
static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -1153,6 +1203,15 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
switch (dai_priv->clk) {
case ARIZONA_CLK_SYSCLK:
+ switch (priv->arizona->type) {
+ case WM5102:
+ arizona_wm5102_set_dac_comp(codec,
+ params_rate(params));
+ break;
+ default:
+ break;
+ }
+
snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
if (base)
@@ -1175,6 +1234,27 @@ static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
return 0;
}
+static bool arizona_aif_cfg_changed(struct snd_soc_codec *codec,
+ int base, int bclk, int lrclk, int frame)
+{
+ int val;
+
+ val = snd_soc_read(codec, base + ARIZONA_AIF_BCLK_CTRL);
+ if (bclk != (val & ARIZONA_AIF1_BCLK_FREQ_MASK))
+ return true;
+
+ val = snd_soc_read(codec, base + ARIZONA_AIF_TX_BCLK_RATE);
+ if (lrclk != (val & ARIZONA_AIF1TX_BCPF_MASK))
+ return true;
+
+ val = snd_soc_read(codec, base + ARIZONA_AIF_FRAME_CTRL_1);
+ if (frame != (val & (ARIZONA_AIF1TX_WL_MASK |
+ ARIZONA_AIF1TX_SLOT_LEN_MASK)))
+ return true;
+
+ return false;
+}
+
static int arizona_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -1185,26 +1265,40 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
int base = dai->driver->base;
const int *rates;
int i, ret, val;
+ int channels = params_channels(params);
int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
+ int tdm_width = arizona->tdm_width[dai->id - 1];
+ int tdm_slots = arizona->tdm_slots[dai->id - 1];
int bclk, lrclk, wl, frame, bclk_target;
+ bool reconfig;
+ unsigned int aif_tx_state, aif_rx_state;
if (params_rate(params) % 8000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
- bclk_target = snd_soc_params_to_bclk(params);
- if (chan_limit && chan_limit < params_channels(params)) {
+ if (tdm_slots) {
+ arizona_aif_dbg(dai, "Configuring for %d %d bit TDM slots\n",
+ tdm_slots, tdm_width);
+ bclk_target = tdm_slots * tdm_width * params_rate(params);
+ channels = tdm_slots;
+ } else {
+ bclk_target = snd_soc_params_to_bclk(params);
+ }
+
+ if (chan_limit && chan_limit < channels) {
arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
- bclk_target /= params_channels(params);
+ bclk_target /= channels;
bclk_target *= chan_limit;
}
- /* Force stereo for I2S mode */
+ /* Force multiple of 2 channels for I2S mode */
val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
- if (params_channels(params) == 1 && (val & ARIZONA_AIF1_FMT_MASK)) {
+ if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) {
arizona_aif_dbg(dai, "Forcing stereo mode\n");
- bclk_target *= 2;
+ bclk_target /= channels;
+ bclk_target *= channels + 1;
}
for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
@@ -1228,28 +1322,56 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
wl = snd_pcm_format_width(params_format(params));
frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
+ reconfig = arizona_aif_cfg_changed(codec, base, bclk, lrclk, frame);
+
+ if (reconfig) {
+ /* Save AIF TX/RX state */
+ aif_tx_state = snd_soc_read(codec,
+ base + ARIZONA_AIF_TX_ENABLES);
+ aif_rx_state = snd_soc_read(codec,
+ base + ARIZONA_AIF_RX_ENABLES);
+ /* Disable AIF TX/RX before reconfiguring it */
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_ENABLES, 0xff, 0x0);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_RX_ENABLES, 0xff, 0x0);
+ }
+
ret = arizona_hw_params_rate(substream, params, dai);
if (ret != 0)
- return ret;
+ goto restore_aif;
- regmap_update_bits_async(arizona->regmap,
- base + ARIZONA_AIF_BCLK_CTRL,
- ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
- regmap_update_bits_async(arizona->regmap,
- base + ARIZONA_AIF_TX_BCLK_RATE,
- ARIZONA_AIF1TX_BCPF_MASK, lrclk);
- regmap_update_bits_async(arizona->regmap,
- base + ARIZONA_AIF_RX_BCLK_RATE,
- ARIZONA_AIF1RX_BCPF_MASK, lrclk);
- regmap_update_bits_async(arizona->regmap,
- base + ARIZONA_AIF_FRAME_CTRL_1,
- ARIZONA_AIF1TX_WL_MASK |
- ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
- regmap_update_bits(arizona->regmap, base + ARIZONA_AIF_FRAME_CTRL_2,
- ARIZONA_AIF1RX_WL_MASK |
- ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+ if (reconfig) {
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_BCLK_CTRL,
+ ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_BCLK_RATE,
+ ARIZONA_AIF1TX_BCPF_MASK, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_RX_BCLK_RATE,
+ ARIZONA_AIF1RX_BCPF_MASK, lrclk);
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_FRAME_CTRL_1,
+ ARIZONA_AIF1TX_WL_MASK |
+ ARIZONA_AIF1TX_SLOT_LEN_MASK, frame);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_FRAME_CTRL_2,
+ ARIZONA_AIF1RX_WL_MASK |
+ ARIZONA_AIF1RX_SLOT_LEN_MASK, frame);
+ }
- return 0;
+restore_aif:
+ if (reconfig) {
+ /* Restore AIF TX/RX state */
+ regmap_update_bits_async(arizona->regmap,
+ base + ARIZONA_AIF_TX_ENABLES,
+ 0xff, aif_tx_state);
+ regmap_update_bits(arizona->regmap,
+ base + ARIZONA_AIF_RX_ENABLES,
+ 0xff, aif_rx_state);
+ }
+ return ret;
}
static const char *arizona_dai_clk_str(int clk_id)
@@ -1324,9 +1446,63 @@ static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
ARIZONA_AIF1_TRI, reg);
}
+static void arizona_set_channels_to_mask(struct snd_soc_dai *dai,
+ unsigned int base,
+ int channels, unsigned int mask)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int slot, i;
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ regmap_write(arizona->regmap, base + i, slot);
+
+ mask &= ~(1 << slot);
+ }
+
+ if (mask)
+ arizona_aif_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int arizona_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 arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical AIFs */
+ if (dai->id > ARIZONA_MAX_AIF)
+ return -ENOTSUPP;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_3,
+ tx_max_chan, tx_mask);
+ arizona_set_channels_to_mask(dai, base + ARIZONA_AIF_FRAME_CTRL_11,
+ rx_max_chan, rx_mask);
+
+ arizona->tdm_width[dai->id - 1] = slot_width;
+ arizona->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
const struct snd_soc_dai_ops arizona_dai_ops = {
.startup = arizona_startup,
.set_fmt = arizona_set_fmt,
+ .set_tdm_slot = arizona_set_tdm_slot,
.hw_params = arizona_hw_params,
.set_sysclk = arizona_dai_set_sysclk,
.set_tristate = arizona_set_tristate,
@@ -1400,6 +1576,12 @@ static int arizona_validate_fll(struct arizona_fll *fll,
{
unsigned int Fvco_min;
+ if (fll->fout && Fout != fll->fout) {
+ arizona_fll_err(fll,
+ "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
if (Fref / ARIZONA_FLL_MAX_REFDIV > ARIZONA_FLL_MAX_FREF) {
arizona_fll_err(fll,
"Can't scale %dMHz in to <=13.5MHz\n",
@@ -1478,6 +1660,10 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
while (div <= ARIZONA_FLL_MAX_REFDIV) {
for (ratio = init_ratio; ratio <= ARIZONA_FLL_MAX_FRATIO;
ratio++) {
+ if ((ARIZONA_FLL_VCO_CORNER / 2) /
+ (fll->vco_mult * ratio) < Fref)
+ break;
+
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
@@ -1485,11 +1671,7 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
}
}
- for (ratio = init_ratio - 1; ratio >= 0; ratio--) {
- if (ARIZONA_FLL_VCO_CORNER / (fll->vco_mult * ratio) <
- Fref)
- break;
-
+ for (ratio = init_ratio - 1; ratio > 0; ratio--) {
if (target % (ratio * Fref)) {
cfg->refdiv = refdiv;
cfg->fratio = ratio - 1;
@@ -1616,7 +1798,7 @@ static void arizona_apply_fll(struct arizona *arizona, unsigned int base,
ARIZONA_FLL1_CTRL_UPD | cfg->n);
}
-static bool arizona_is_enabled_fll(struct arizona_fll *fll)
+static int arizona_is_enabled_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
unsigned int reg;
@@ -1632,13 +1814,26 @@ static bool arizona_is_enabled_fll(struct arizona_fll *fll)
return reg & ARIZONA_FLL1_ENA;
}
-static void arizona_enable_fll(struct arizona_fll *fll)
+static int arizona_enable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
int ret;
bool use_sync = false;
+ int already_enabled = arizona_is_enabled_fll(fll);
struct arizona_fll_cfg cfg;
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (already_enabled) {
+ /* Facilitate smooth refclk across the transition */
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x7,
+ ARIZONA_FLL1_GAIN_MASK, 0);
+ regmap_update_bits_async(fll->arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN,
+ ARIZONA_FLL1_FREERUN);
+ }
+
/*
* If we have both REFCLK and SYNCCLK then enable both,
* otherwise apply the SYNCCLK settings to REFCLK.
@@ -1666,7 +1861,7 @@ static void arizona_enable_fll(struct arizona_fll *fll)
ARIZONA_FLL1_SYNC_ENA, 0);
} else {
arizona_fll_err(fll, "No clocks provided\n");
- return;
+ return -EINVAL;
}
/*
@@ -1681,25 +1876,29 @@ static void arizona_enable_fll(struct arizona_fll *fll)
ARIZONA_FLL1_SYNC_BW,
ARIZONA_FLL1_SYNC_BW);
- if (!arizona_is_enabled_fll(fll))
+ if (!already_enabled)
pm_runtime_get(arizona->dev);
/* Clear any pending completions */
try_wait_for_completion(&fll->ok);
regmap_update_bits_async(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_FREERUN, 0);
- regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
if (use_sync)
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA,
ARIZONA_FLL1_SYNC_ENA);
+ if (already_enabled)
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
ret = wait_for_completion_timeout(&fll->ok,
msecs_to_jiffies(250));
if (ret == 0)
arizona_fll_warn(fll, "Timed out waiting for lock\n");
+
+ return 0;
}
static void arizona_disable_fll(struct arizona_fll *fll)
@@ -1713,6 +1912,8 @@ static void arizona_disable_fll(struct arizona_fll *fll)
ARIZONA_FLL1_ENA, 0, &change);
regmap_update_bits(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA, 0);
+ regmap_update_bits_async(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
if (change)
pm_runtime_put_autosuspend(arizona->dev);
@@ -1721,7 +1922,7 @@ static void arizona_disable_fll(struct arizona_fll *fll)
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout)
{
- int ret;
+ int ret = 0;
if (fll->ref_src == source && fll->ref_freq == Fref)
return 0;
@@ -1736,17 +1937,17 @@ int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
fll->ref_freq = Fref;
if (fll->fout && Fref > 0) {
- arizona_enable_fll(fll);
+ ret = arizona_enable_fll(fll);
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(arizona_set_fll_refclk);
int arizona_set_fll(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout)
{
- int ret;
+ int ret = 0;
if (fll->sync_src == source &&
fll->sync_freq == Fref && fll->fout == Fout)
@@ -1768,13 +1969,12 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
fll->sync_freq = Fref;
fll->fout = Fout;
- if (Fout) {
- arizona_enable_fll(fll);
- } else {
+ if (Fout)
+ ret = arizona_enable_fll(fll);
+ else
arizona_disable_fll(fll);
- }
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(arizona_set_fll);
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 05ae17f..942cfb1 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -249,6 +249,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_dai(struct arizona_priv *priv, int dai);
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
new file mode 100644
index 0000000..a20b30c
--- /dev/null
+++ b/sound/soc/codecs/cs4265.c
@@ -0,0 +1,682 @@
+/*
+ * cs4265.c -- CS4265 ALSA SoC audio driver
+ *
+ * Copyright 2014 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/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/regmap.h>
+#include <linux/slab.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 "cs4265.h"
+
+struct cs4265_private {
+ struct device *dev;
+ struct regmap *regmap;
+ struct gpio_desc *reset_gpio;
+ u8 format;
+ u32 sysclk;
+};
+
+static const struct reg_default cs4265_reg_defaults[] = {
+ { CS4265_PWRCTL, 0x0F },
+ { CS4265_DAC_CTL, 0x08 },
+ { CS4265_ADC_CTL, 0x00 },
+ { CS4265_MCLK_FREQ, 0x00 },
+ { CS4265_SIG_SEL, 0x40 },
+ { CS4265_CHB_PGA_CTL, 0x00 },
+ { CS4265_CHA_PGA_CTL, 0x00 },
+ { CS4265_ADC_CTL2, 0x19 },
+ { CS4265_DAC_CHA_VOL, 0x00 },
+ { CS4265_DAC_CHB_VOL, 0x00 },
+ { CS4265_DAC_CTL2, 0xC0 },
+ { CS4265_SPDIF_CTL1, 0x00 },
+ { CS4265_SPDIF_CTL2, 0x00 },
+ { CS4265_INT_MASK, 0x00 },
+ { CS4265_STATUS_MODE_MSB, 0x00 },
+ { CS4265_STATUS_MODE_LSB, 0x00 },
+};
+
+static bool cs4265_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4265_PWRCTL:
+ case CS4265_DAC_CTL:
+ case CS4265_ADC_CTL:
+ case CS4265_MCLK_FREQ:
+ case CS4265_SIG_SEL:
+ case CS4265_CHB_PGA_CTL:
+ case CS4265_CHA_PGA_CTL:
+ case CS4265_ADC_CTL2:
+ case CS4265_DAC_CHA_VOL:
+ case CS4265_DAC_CHB_VOL:
+ case CS4265_DAC_CTL2:
+ case CS4265_SPDIF_CTL1:
+ case CS4265_SPDIF_CTL2:
+ case CS4265_INT_MASK:
+ case CS4265_STATUS_MODE_MSB:
+ case CS4265_STATUS_MODE_LSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs4265_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS4265_INT_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static DECLARE_TLV_DB_SCALE(pga_tlv, -1200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 0);
+
+static const char * const digital_input_mux_text[] = {
+ "SDIN1", "SDIN2"
+};
+
+static SOC_ENUM_SINGLE_DECL(digital_input_mux_enum, CS4265_SIG_SEL, 7,
+ digital_input_mux_text);
+
+static const struct snd_kcontrol_new digital_input_mux =
+ SOC_DAPM_ENUM("Digital Input Mux", digital_input_mux_enum);
+
+static const char * const mic_linein_text[] = {
+ "MIC", "LINEIN"
+};
+
+static SOC_ENUM_SINGLE_DECL(mic_linein_enum, CS4265_ADC_CTL2, 0,
+ mic_linein_text);
+
+static const char * const cam_mode_text[] = {
+ "One Byte", "Two Byte"
+};
+
+static SOC_ENUM_SINGLE_DECL(cam_mode_enum, CS4265_SPDIF_CTL1, 5,
+ cam_mode_text);
+
+static const char * const cam_mono_stereo_text[] = {
+ "Stereo", "Mono"
+};
+
+static SOC_ENUM_SINGLE_DECL(spdif_mono_stereo_enum, CS4265_SPDIF_CTL2, 2,
+ cam_mono_stereo_text);
+
+static const char * const mono_select_text[] = {
+ "Channel A", "Channel B"
+};
+
+static SOC_ENUM_SINGLE_DECL(spdif_mono_select_enum, CS4265_SPDIF_CTL2, 0,
+ mono_select_text);
+
+static const struct snd_kcontrol_new mic_linein_mux =
+ SOC_DAPM_ENUM("ADC Input Capture Mux", mic_linein_enum);
+
+static const struct snd_kcontrol_new loopback_ctl =
+ SOC_DAPM_SINGLE("Switch", CS4265_SIG_SEL, 1, 1, 0);
+
+static const struct snd_kcontrol_new spdif_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 0, 0);
+
+static const struct snd_kcontrol_new dac_switch =
+ SOC_DAPM_SINGLE("Switch", CS4265_PWRCTL, 1, 1, 0);
+
+static const struct snd_kcontrol_new cs4265_snd_controls[] = {
+
+ SOC_DOUBLE_R_SX_TLV("PGA Volume", CS4265_CHA_PGA_CTL,
+ CS4265_CHB_PGA_CTL, 0, 0x28, 0x30, pga_tlv),
+ SOC_DOUBLE_R_TLV("DAC Volume", CS4265_DAC_CHA_VOL,
+ CS4265_DAC_CHB_VOL, 0, 0xFF, 1, dac_tlv),
+ SOC_SINGLE("De-emp 44.1kHz Switch", CS4265_DAC_CTL, 1,
+ 1, 0),
+ SOC_SINGLE("DAC INV Switch", CS4265_DAC_CTL2, 5,
+ 1, 0),
+ SOC_SINGLE("DAC Zero Cross Switch", CS4265_DAC_CTL2, 6,
+ 1, 0),
+ SOC_SINGLE("DAC Soft Ramp Switch", CS4265_DAC_CTL2, 7,
+ 1, 0),
+ SOC_SINGLE("ADC HPF Switch", CS4265_ADC_CTL, 1,
+ 1, 0),
+ SOC_SINGLE("ADC Zero Cross Switch", CS4265_ADC_CTL2, 3,
+ 1, 1),
+ SOC_SINGLE("ADC Soft Ramp Switch", CS4265_ADC_CTL2, 7,
+ 1, 0),
+ SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1,
+ 6, 1, 0),
+ SOC_ENUM("C Data Access", cam_mode_enum),
+ SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2,
+ 3, 1, 0),
+ SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum),
+ SOC_SINGLE("MMTLR Data Switch", 0,
+ 1, 1, 0),
+ SOC_ENUM("Mono Channel Select", spdif_mono_select_enum),
+ SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24),
+};
+
+static const struct snd_soc_dapm_widget cs4265_dapm_widgets[] = {
+
+ SND_SOC_DAPM_INPUT("LINEINL"),
+ SND_SOC_DAPM_INPUT("LINEINR"),
+ SND_SOC_DAPM_INPUT("MICL"),
+ SND_SOC_DAPM_INPUT("MICR"),
+
+ SND_SOC_DAPM_AIF_OUT("DOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SPDIFOUT", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_MUX("ADC Mux", SND_SOC_NOPM, 0, 0, &mic_linein_mux),
+
+ SND_SOC_DAPM_ADC("ADC", NULL, CS4265_PWRCTL, 2, 1),
+ SND_SOC_DAPM_PGA("Pre-amp MIC", CS4265_PWRCTL, 3,
+ 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM,
+ 0, 0, &digital_input_mux),
+
+ SND_SOC_DAPM_MIXER("SDIN1 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SDIN2 Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("SPDIF Transmitter", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SWITCH("Loopback", SND_SOC_NOPM, 0, 0,
+ &loopback_ctl),
+ SND_SOC_DAPM_SWITCH("SPDIF", SND_SOC_NOPM, 0, 0,
+ &spdif_switch),
+ SND_SOC_DAPM_SWITCH("DAC", CS4265_PWRCTL, 1, 1,
+ &dac_switch),
+
+ SND_SOC_DAPM_AIF_IN("DIN1", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DIN2", NULL, 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("TXIN", NULL, 0,
+ CS4265_SPDIF_CTL2, 5, 1),
+
+ SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+ SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+
+};
+
+static const struct snd_soc_dapm_route cs4265_audio_map[] = {
+
+ {"DIN1", NULL, "DAI1 Playback"},
+ {"DIN2", NULL, "DAI2 Playback"},
+ {"SDIN1 Input Mixer", NULL, "DIN1"},
+ {"SDIN2 Input Mixer", NULL, "DIN2"},
+ {"Input Mux", "SDIN1", "SDIN1 Input Mixer"},
+ {"Input Mux", "SDIN2", "SDIN2 Input Mixer"},
+ {"DAC", "Switch", "Input Mux"},
+ {"SPDIF", "Switch", "Input Mux"},
+ {"LINEOUTL", NULL, "DAC"},
+ {"LINEOUTR", NULL, "DAC"},
+ {"SPDIFOUT", NULL, "SPDIF"},
+
+ {"ADC Mux", "LINEIN", "LINEINL"},
+ {"ADC Mux", "LINEIN", "LINEINR"},
+ {"ADC Mux", "MIC", "MICL"},
+ {"ADC Mux", "MIC", "MICR"},
+ {"ADC", NULL, "ADC Mux"},
+ {"DOUT", NULL, "ADC"},
+ {"DAI1 Capture", NULL, "DOUT"},
+ {"DAI2 Capture", NULL, "DOUT"},
+
+ /* Loopback */
+ {"Loopback", "Switch", "ADC"},
+ {"DAC", NULL, "Loopback"},
+};
+
+struct cs4265_clk_para {
+ u32 mclk;
+ u32 rate;
+ u8 fm_mode; /* values 1, 2, or 4 */
+ u8 mclkdiv;
+};
+
+static const struct cs4265_clk_para clk_map_table[] = {
+ /*32k*/
+ {8192000, 32000, 0, 0},
+ {12288000, 32000, 0, 1},
+ {16384000, 32000, 0, 2},
+ {24576000, 32000, 0, 3},
+ {32768000, 32000, 0, 4},
+
+ /*44.1k*/
+ {11289600, 44100, 0, 0},
+ {16934400, 44100, 0, 1},
+ {22579200, 44100, 0, 2},
+ {33868000, 44100, 0, 3},
+ {45158400, 44100, 0, 4},
+
+ /*48k*/
+ {12288000, 48000, 0, 0},
+ {18432000, 48000, 0, 1},
+ {24576000, 48000, 0, 2},
+ {36864000, 48000, 0, 3},
+ {49152000, 48000, 0, 4},
+
+ /*64k*/
+ {8192000, 64000, 1, 0},
+ {1228800, 64000, 1, 1},
+ {1693440, 64000, 1, 2},
+ {2457600, 64000, 1, 3},
+ {3276800, 64000, 1, 4},
+
+ /* 88.2k */
+ {11289600, 88200, 1, 0},
+ {16934400, 88200, 1, 1},
+ {22579200, 88200, 1, 2},
+ {33868000, 88200, 1, 3},
+ {45158400, 88200, 1, 4},
+
+ /* 96k */
+ {12288000, 96000, 1, 0},
+ {18432000, 96000, 1, 1},
+ {24576000, 96000, 1, 2},
+ {36864000, 96000, 1, 3},
+ {49152000, 96000, 1, 4},
+
+ /* 128k */
+ {8192000, 128000, 2, 0},
+ {12288000, 128000, 2, 1},
+ {16934400, 128000, 2, 2},
+ {24576000, 128000, 2, 3},
+ {32768000, 128000, 2, 4},
+
+ /* 176.4k */
+ {11289600, 176400, 2, 0},
+ {16934400, 176400, 2, 1},
+ {22579200, 176400, 2, 2},
+ {33868000, 176400, 2, 3},
+ {49152000, 176400, 2, 4},
+
+ /* 192k */
+ {12288000, 192000, 2, 0},
+ {18432000, 192000, 2, 1},
+ {24576000, 192000, 2, 2},
+ {36864000, 192000, 2, 3},
+ {49152000, 192000, 2, 4},
+};
+
+static int cs4265_get_clk_index(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].rate == rate &&
+ clk_map_table[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs4265_set_sysclk(struct snd_soc_dai *codec_dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ if (clk_id != 0) {
+ dev_err(codec->dev, "Invalid clk_id %d\n", clk_id);
+ return -EINVAL;
+ }
+ for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
+ if (clk_map_table[i].mclk == freq) {
+ cs4265->sysclk = freq;
+ return 0;
+ }
+ }
+ cs4265->sysclk = 0;
+ dev_err(codec->dev, "Invalid freq parameter %d\n", freq);
+ return -EINVAL;
+}
+
+static int cs4265_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ u8 iface = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_MASTER,
+ CS4265_ADC_MASTER);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_MASTER,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ iface |= SND_SOC_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ iface |= SND_SOC_DAIFMT_RIGHT_J;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ iface |= SND_SOC_DAIFMT_LEFT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cs4265->format = iface;
+ return 0;
+}
+
+static int cs4265_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (mute) {
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_MUTE,
+ CS4265_DAC_CTL_MUTE);
+ snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_MUTE,
+ CS4265_SPDIF_CTL2_MUTE);
+ } else {
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_MUTE,
+ 0);
+ snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_MUTE,
+ 0);
+ }
+ return 0;
+}
+
+static int cs4265_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 cs4265_private *cs4265 = snd_soc_codec_get_drvdata(codec);
+ int index;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ ((cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK)
+ == SND_SOC_DAIFMT_RIGHT_J))
+ return -EINVAL;
+
+ index = cs4265_get_clk_index(cs4265->sysclk, params_rate(params));
+ if (index >= 0) {
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_FM, clk_map_table[index].fm_mode);
+ snd_soc_update_bits(codec, CS4265_MCLK_FREQ,
+ CS4265_MCLK_FREQ_MASK,
+ clk_map_table[index].mclkdiv);
+
+ } else {
+ dev_err(codec->dev, "can't get correct mclk\n");
+ return -EINVAL;
+ }
+
+ switch (cs4265->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, (1 << 4));
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_DIF, (1 << 4));
+ snd_soc_update_bits(codec, CS4265_SPDIF_CTL2,
+ CS4265_SPDIF_CTL2_DIF, (1 << 6));
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ if (params_width(params) == 16) {
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, (1 << 5));
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_SPDIF_CTL2_DIF, (1 << 7));
+ } else {
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, (3 << 5));
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_SPDIF_CTL2_DIF, (1 << 7));
+ }
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_update_bits(codec, CS4265_DAC_CTL,
+ CS4265_DAC_CTL_DIF, 0);
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_ADC_DIF, 0);
+ snd_soc_update_bits(codec, CS4265_ADC_CTL,
+ CS4265_SPDIF_CTL2_DIF, (1 << 6));
+
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs4265_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_update_bits(codec, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN,
+ CS4265_PWRCTL_PDN);
+ break;
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, CS4265_PWRCTL,
+ CS4265_PWRCTL_PDN,
+ CS4265_PWRCTL_PDN);
+ break;
+ }
+ codec->dapm.bias_level = level;
+ return 0;
+}
+
+#define CS4265_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+
+static const struct snd_soc_dai_ops cs4265_ops = {
+ .hw_params = cs4265_pcm_hw_params,
+ .digital_mute = cs4265_digital_mute,
+ .set_fmt = cs4265_set_fmt,
+ .set_sysclk = cs4265_set_sysclk,
+};
+
+static struct snd_soc_dai_driver cs4265_dai[] = {
+ {
+ .name = "cs4265-dai1",
+ .playback = {
+ .stream_name = "DAI1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DAI1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .ops = &cs4265_ops,
+ },
+ {
+ .name = "cs4265-dai2",
+ .playback = {
+ .stream_name = "DAI2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DAI2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS4265_RATES,
+ .formats = CS4265_FORMATS,
+ },
+ .ops = &cs4265_ops,
+ },
+};
+
+static const struct snd_soc_codec_driver soc_codec_cs4265 = {
+ .set_bias_level = cs4265_set_bias_level,
+
+ .dapm_widgets = cs4265_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4265_dapm_widgets),
+ .dapm_routes = cs4265_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs4265_audio_map),
+
+ .controls = cs4265_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4265_snd_controls),
+};
+
+static const struct regmap_config cs4265_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS4265_MAX_REGISTER,
+ .reg_defaults = cs4265_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs4265_reg_defaults),
+ .readable_reg = cs4265_readable_register,
+ .volatile_reg = cs4265_volatile_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs4265_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs4265_private *cs4265;
+ int ret = 0;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs4265 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs4265_private),
+ GFP_KERNEL);
+ if (cs4265 == NULL)
+ return -ENOMEM;
+ cs4265->dev = &i2c_client->dev;
+
+ cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
+ if (IS_ERR(cs4265->regmap)) {
+ ret = PTR_ERR(cs4265->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs4265->reset_gpio = devm_gpiod_get(&i2c_client->dev,
+ "reset-gpios");
+ if (IS_ERR(cs4265->reset_gpio)) {
+ ret = PTR_ERR(cs4265->reset_gpio);
+ if (ret != -ENOENT && ret != -ENOSYS)
+ return ret;
+
+ cs4265->reset_gpio = NULL;
+ } else {
+ ret = gpiod_direction_output(cs4265->reset_gpio, 0);
+ if (ret)
+ return ret;
+ mdelay(1);
+ gpiod_set_value_cansleep(cs4265->reset_gpio, 1);
+
+ }
+
+ i2c_set_clientdata(i2c_client, cs4265);
+
+ ret = regmap_read(cs4265->regmap, CS4265_CHIP_ID, &reg);
+ devid = reg & CS4265_CHIP_ID_MASK;
+ if (devid != CS4265_CHIP_ID_VAL) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS4265 Device ID (%X). Expected %X\n",
+ devid, CS4265_CHIP_ID);
+ return ret;
+ }
+ dev_info(&i2c_client->dev,
+ "CS4265 Version %x\n",
+ reg & CS4265_REV_ID_MASK);
+
+ regmap_write(cs4265->regmap, CS4265_PWRCTL, 0x0F);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_cs4265, cs4265_dai,
+ ARRAY_SIZE(cs4265_dai));
+ return ret;
+}
+
+static int cs4265_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id cs4265_of_match[] = {
+ { .compatible = "cirrus,cs4265", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cs4265_of_match);
+
+static const struct i2c_device_id cs4265_id[] = {
+ { "cs4265", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4265_id);
+
+static struct i2c_driver cs4265_i2c_driver = {
+ .driver = {
+ .name = "cs4265",
+ .owner = THIS_MODULE,
+ .of_match_table = cs4265_of_match,
+ },
+ .id_table = cs4265_id,
+ .probe = cs4265_i2c_probe,
+ .remove = cs4265_i2c_remove,
+};
+
+module_i2c_driver(cs4265_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4265 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <paul.handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4265.h b/sound/soc/codecs/cs4265.h
new file mode 100644
index 0000000..0a80a8d
--- /dev/null
+++ b/sound/soc/codecs/cs4265.h
@@ -0,0 +1,64 @@
+/*
+ * cs4265.h -- CS4265 ALSA SoC audio driver
+ *
+ * Copyright 2014 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 __CS4265_H__
+#define __CS4265_H__
+
+#define CS4265_CHIP_ID 0x1
+#define CS4265_CHIP_ID_VAL 0xD0
+#define CS4265_CHIP_ID_MASK 0xF0
+#define CS4265_REV_ID_MASK 0x0F
+
+#define CS4265_PWRCTL 0x02
+#define CS4265_PWRCTL_PDN 1
+
+#define CS4265_DAC_CTL 0x3
+#define CS4265_DAC_CTL_MUTE (1 << 2)
+#define CS4265_DAC_CTL_DIF (3 << 4)
+
+#define CS4265_ADC_CTL 0x4
+#define CS4265_ADC_MASTER 1
+#define CS4265_ADC_DIF (1 << 4)
+#define CS4265_ADC_FM (3 << 6)
+
+#define CS4265_MCLK_FREQ 0x5
+#define CS4265_MCLK_FREQ_MASK (7 << 4)
+
+#define CS4265_SIG_SEL 0x6
+#define CS4265_SIG_SEL_LOOP (1 << 1)
+
+#define CS4265_CHB_PGA_CTL 0x7
+#define CS4265_CHA_PGA_CTL 0x8
+
+#define CS4265_ADC_CTL2 0x9
+
+#define CS4265_DAC_CHA_VOL 0xA
+#define CS4265_DAC_CHB_VOL 0xB
+
+#define CS4265_DAC_CTL2 0xC
+
+#define CS4265_INT_STATUS 0xD
+#define CS4265_INT_MASK 0xE
+#define CS4265_STATUS_MODE_MSB 0xF
+#define CS4265_STATUS_MODE_LSB 0x10
+
+#define CS4265_SPDIF_CTL1 0x11
+
+#define CS4265_SPDIF_CTL2 0x12
+#define CS4265_SPDIF_CTL2_MUTE (1 << 4)
+#define CS4265_SPDIF_CTL2_DIF (3 << 6)
+
+#define CS4265_C_DATA_BUFF 0x13
+#define CS4265_MAX_REGISTER 0x2A
+
+#endif
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 071fc77..969167d 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -399,15 +399,15 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
CS42L52_MASTERB_VOL, 0, 0x34, 0xE4, hl_tlv),
SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L52_HPA_VOL,
- CS42L52_HPB_VOL, 0, 0x34, 0xCC, hpd_tlv),
+ CS42L52_HPB_VOL, 0, 0x34, 0xC0, hpd_tlv),
SOC_ENUM("Headphone Analog Gain", hp_gain_enum),
SOC_DOUBLE_R_SX_TLV("Speaker Volume", CS42L52_SPKA_VOL,
- CS42L52_SPKB_VOL, 0, 0x1, 0xff, hl_tlv),
+ CS42L52_SPKB_VOL, 0, 0x40, 0xC0, hl_tlv),
SOC_DOUBLE_R_SX_TLV("Bypass Volume", CS42L52_PASSTHRUA_VOL,
- CS42L52_PASSTHRUB_VOL, 6, 0x18, 0x90, pga_tlv),
+ CS42L52_PASSTHRUB_VOL, 0, 0x88, 0x90, pga_tlv),
SOC_DOUBLE("Bypass Mute", CS42L52_MISC_CTL, 4, 5, 1, 0),
@@ -417,10 +417,10 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
SOC_ENUM("MIC Bias Level", mic_bias_level_enum),
SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L52_ADCA_VOL,
- CS42L52_ADCB_VOL, 7, 0x80, 0xA0, ipd_tlv),
+ CS42L52_ADCB_VOL, 0, 0xA0, 0x78, ipd_tlv),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume",
CS42L52_ADCA_MIXER_VOL, CS42L52_ADCB_MIXER_VOL,
- 6, 0x7f, 0x19, ipd_tlv),
+ 0, 0x19, 0x7F, ipd_tlv),
SOC_DOUBLE("ADC Switch", CS42L52_ADC_MISC_CTL, 0, 1, 1, 0),
@@ -428,11 +428,11 @@ static const struct snd_kcontrol_new cs42l52_snd_controls[] = {
CS42L52_ADCB_MIXER_VOL, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L52_PGAA_CTL,
- CS42L52_PGAB_CTL, 0, 0x28, 0x30, pga_tlv),
+ CS42L52_PGAB_CTL, 0, 0x28, 0x24, pga_tlv),
SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume",
CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL,
- 0, 0x7f, 0x19, mix_tlv),
+ 0, 0x19, 0x7f, mix_tlv),
SOC_DOUBLE_R("PCM Mixer Switch",
CS42L52_PCMA_MIXER_VOL, CS42L52_PCMB_MIXER_VOL, 7, 1, 1),
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index fdc4bd2..c766a5a 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -318,24 +318,32 @@ static const struct soc_enum adca_swap_enum =
ARRAY_SIZE(left_swap_text),
left_swap_text,
swap_values);
+static const struct snd_kcontrol_new adca_swap_mux =
+ SOC_DAPM_ENUM("Route", adca_swap_enum);
static const struct soc_enum pcma_swap_enum =
SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 4, 3,
ARRAY_SIZE(left_swap_text),
left_swap_text,
swap_values);
+static const struct snd_kcontrol_new pcma_swap_mux =
+ SOC_DAPM_ENUM("Route", pcma_swap_enum);
static const struct soc_enum adcb_swap_enum =
SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 2, 3,
ARRAY_SIZE(right_swap_text),
right_swap_text,
swap_values);
+static const struct snd_kcontrol_new adcb_swap_mux =
+ SOC_DAPM_ENUM("Route", adcb_swap_enum);
static const struct soc_enum pcmb_swap_enum =
SOC_VALUE_ENUM_SINGLE(CS42L56_CHAN_MIX_SWAP, 6, 3,
ARRAY_SIZE(right_swap_text),
right_swap_text,
swap_values);
+static const struct snd_kcontrol_new pcmb_swap_mux =
+ SOC_DAPM_ENUM("Route", pcmb_swap_enum);
static const struct snd_kcontrol_new hpa_switch =
SOC_DAPM_SINGLE("Switch", CS42L56_PWRCTL_2, 6, 1, 1);
@@ -421,15 +429,15 @@ static const struct soc_enum ng_delay_enum =
static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
SOC_DOUBLE_R_SX_TLV("Master Volume", CS42L56_MASTER_A_VOLUME,
- CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xfd, adv_tlv),
+ CS42L56_MASTER_B_VOLUME, 0, 0x34, 0xE4, adv_tlv),
SOC_DOUBLE("Master Mute Switch", CS42L56_DSP_MUTE_CTL, 0, 1, 1, 1),
SOC_DOUBLE_R_SX_TLV("ADC Mixer Volume", CS42L56_ADCA_MIX_VOLUME,
- CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0xa9, hl_tlv),
+ CS42L56_ADCB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv),
SOC_DOUBLE("ADC Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 6, 7, 1, 1),
SOC_DOUBLE_R_SX_TLV("PCM Mixer Volume", CS42L56_PCMA_MIX_VOLUME,
- CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0xa9, hl_tlv),
+ CS42L56_PCMB_MIX_VOLUME, 0, 0x88, 0x90, hl_tlv),
SOC_DOUBLE("PCM Mixer Mute Switch", CS42L56_DSP_MUTE_CTL, 4, 5, 1, 1),
SOC_SINGLE_TLV("Analog Advisory Volume",
@@ -438,16 +446,16 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
CS42L56_DIGINPUT_ADV_VOLUME, 0, 0x00, 1, adv_tlv),
SOC_DOUBLE_R_SX_TLV("PGA Volume", CS42L56_PGAA_MUX_VOLUME,
- CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0xfd, pga_tlv),
+ CS42L56_PGAB_MUX_VOLUME, 0, 0x34, 0x24, pga_tlv),
SOC_DOUBLE_R_TLV("ADC Volume", CS42L56_ADCA_ATTENUATOR,
CS42L56_ADCB_ATTENUATOR, 0, 0x00, 1, adc_tlv),
SOC_DOUBLE("ADC Mute Switch", CS42L56_MISC_ADC_CTL, 2, 3, 1, 1),
SOC_DOUBLE("ADC Boost Switch", CS42L56_GAIN_BIAS_CTL, 3, 2, 1, 1),
SOC_DOUBLE_R_SX_TLV("Headphone Volume", CS42L56_HPA_VOLUME,
- CS42L56_HPA_VOLUME, 0, 0x44, 0x55, hl_tlv),
+ CS42L56_HPB_VOLUME, 0, 0x84, 0x48, hl_tlv),
SOC_DOUBLE_R_SX_TLV("LineOut Volume", CS42L56_LOA_VOLUME,
- CS42L56_LOA_VOLUME, 0, 0x44, 0x55, hl_tlv),
+ CS42L56_LOB_VOLUME, 0, 0x84, 0x48, hl_tlv),
SOC_SINGLE_TLV("Bass Shelving Volume", CS42L56_TONE_CTL,
0, 0x00, 1, tone_tlv),
@@ -467,11 +475,6 @@ static const struct snd_kcontrol_new cs42l56_snd_controls[] = {
SOC_SINGLE("ADCA Invert", CS42L56_MISC_ADC_CTL, 2, 1, 1),
SOC_SINGLE("ADCB Invert", CS42L56_MISC_ADC_CTL, 3, 1, 1),
- SOC_ENUM("PCMA Swap", pcma_swap_enum),
- SOC_ENUM("PCMB Swap", pcmb_swap_enum),
- SOC_ENUM("ADCA Swap", adca_swap_enum),
- SOC_ENUM("ADCB Swap", adcb_swap_enum),
-
SOC_DOUBLE("HPF Switch", CS42L56_HPF_CTL, 5, 7, 1, 1),
SOC_DOUBLE("HPF Freeze Switch", CS42L56_HPF_CTL, 4, 6, 1, 1),
SOC_ENUM("HPFA Corner Freq", hpfa_freq_enum),
@@ -570,6 +573,16 @@ static const struct snd_soc_dapm_widget cs42l56_dapm_widgets[] = {
SND_SOC_DAPM_ADC("ADCA", NULL, CS42L56_PWRCTL_1, 1, 1),
SND_SOC_DAPM_ADC("ADCB", NULL, CS42L56_PWRCTL_1, 2, 1),
+ SND_SOC_DAPM_MUX("ADCA Swap Mux", SND_SOC_NOPM, 0, 0,
+ &adca_swap_mux),
+ SND_SOC_DAPM_MUX("ADCB Swap Mux", SND_SOC_NOPM, 0, 0,
+ &adcb_swap_mux),
+
+ SND_SOC_DAPM_MUX("PCMA Swap Mux", SND_SOC_NOPM, 0, 0,
+ &pcma_swap_mux),
+ SND_SOC_DAPM_MUX("PCMB Swap Mux", SND_SOC_NOPM, 0, 0,
+ &pcmb_swap_mux),
+
SND_SOC_DAPM_DAC("DACA", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DACB", NULL, SND_SOC_NOPM, 0, 0),
@@ -607,8 +620,19 @@ static const struct snd_soc_dapm_route cs42l56_audio_map[] = {
{"Digital Output Mux", NULL, "ADCA"},
{"Digital Output Mux", NULL, "ADCB"},
- {"ADCB", NULL, "ADCB Mux"},
- {"ADCA", NULL, "ADCA Mux"},
+ {"ADCB", NULL, "ADCB Swap Mux"},
+ {"ADCA", NULL, "ADCA Swap Mux"},
+
+ {"ADCA Swap Mux", NULL, "ADCA"},
+ {"ADCB Swap Mux", NULL, "ADCB"},
+
+ {"DACA", "Left", "ADCA Swap Mux"},
+ {"DACA", "LR 2", "ADCA Swap Mux"},
+ {"DACA", "Right", "ADCA Swap Mux"},
+
+ {"DACB", "Left", "ADCB Swap Mux"},
+ {"DACB", "LR 2", "ADCB Swap Mux"},
+ {"DACB", "Right", "ADCB Swap Mux"},
{"ADCA Mux", NULL, "AIN3A"},
{"ADCA Mux", NULL, "AIN2A"},
@@ -633,30 +657,32 @@ static const struct snd_soc_dapm_route cs42l56_audio_map[] = {
{"PGAB Input Mux", NULL, "AIN2B"},
{"PGAB Input Mux", NULL, "AIN3B"},
- {"LOB", NULL, "Lineout Right"},
- {"LOA", NULL, "Lineout Left"},
-
- {"Lineout Right", "Switch", "LINEOUTB Input Mux"},
- {"Lineout Left", "Switch", "LINEOUTA Input Mux"},
+ {"LOB", "Switch", "LINEOUTB Input Mux"},
+ {"LOA", "Switch", "LINEOUTA Input Mux"},
{"LINEOUTA Input Mux", "PGAA", "PGAA"},
{"LINEOUTB Input Mux", "PGAB", "PGAB"},
{"LINEOUTA Input Mux", "DACA", "DACA"},
{"LINEOUTB Input Mux", "DACB", "DACB"},
- {"HPA", NULL, "Headphone Left"},
- {"HPB", NULL, "Headphone Right"},
-
- {"Headphone Right", "Switch", "HPB Input Mux"},
- {"Headphone Left", "Switch", "HPA Input Mux"},
+ {"HPA", "Switch", "HPB Input Mux"},
+ {"HPB", "Switch", "HPA Input Mux"},
{"HPA Input Mux", "PGAA", "PGAA"},
{"HPB Input Mux", "PGAB", "PGAB"},
{"HPA Input Mux", "DACA", "DACA"},
{"HPB Input Mux", "DACB", "DACB"},
- {"DACB", NULL, "HiFi Playback"},
- {"DACA", NULL, "HiFi Playback"},
+ {"DACA", NULL, "PCMA Swap Mux"},
+ {"DACB", NULL, "PCMB Swap Mux"},
+
+ {"PCMB Swap Mux", "Left", "HiFi Playback"},
+ {"PCMB Swap Mux", "LR 2", "HiFi Playback"},
+ {"PCMB Swap Mux", "Right", "HiFi Playback"},
+
+ {"PCMA Swap Mux", "Left", "HiFi Playback"},
+ {"PCMA Swap Mux", "LR 2", "HiFi Playback"},
+ {"PCMA Swap Mux", "Right", "HiFi Playback"},
};
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index ae37179..8658194 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -401,7 +401,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
CS42L73_LOBAVOL, 0, 0x41, 0x4B, hpaloa_tlv),
SOC_DOUBLE_R_SX_TLV("Input PGA Analog Volume", CS42L73_MICAPREPGAAVOL,
- CS42L73_MICBPREPGABVOL, 5, 0x34,
+ CS42L73_MICBPREPGABVOL, 0, 0x34,
0x24, micpga_tlv),
SOC_DOUBLE_R("MIC Preamp Switch", CS42L73_MICAPREPGAAVOL,
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index a25bc60..02b1520 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -219,6 +219,9 @@ static int cs42xx8_set_dai_fmt(struct snd_soc_dai *codec_dai,
case SND_SOC_DAIFMT_RIGHT_J:
val = CS42XX8_INTF_DAC_DIF_RIGHTJ | CS42XX8_INTF_ADC_DIF_RIGHTJ;
break;
+ case SND_SOC_DAIFMT_DSP_A:
+ val = CS42XX8_INTF_DAC_DIF_TDM | CS42XX8_INTF_ADC_DIF_TDM;
+ break;
default:
dev_err(codec->dev, "unsupported dai format\n");
return -EINVAL;
@@ -422,7 +425,7 @@ const struct cs42xx8_driver_data cs42888_data = {
};
EXPORT_SYMBOL_GPL(cs42888_data);
-const struct of_device_id cs42xx8_of_match[] = {
+static const struct of_device_id cs42xx8_of_match[] = {
{ .compatible = "cirrus,cs42448", .data = &cs42448_data, },
{ .compatible = "cirrus,cs42888", .data = &cs42888_data, },
{ /* sentinel */ }
diff --git a/sound/soc/codecs/cs42xx8.h b/sound/soc/codecs/cs42xx8.h
index da0b94a..b2c10e5 100644
--- a/sound/soc/codecs/cs42xx8.h
+++ b/sound/soc/codecs/cs42xx8.h
@@ -128,8 +128,8 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap);
#define CS42XX8_INTF_DAC_DIF_RIGHTJ (2 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_DAC_DIF_ONELINE_20 (4 << CS42XX8_INTF_DAC_DIF_SHIFT)
-#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
-#define CS42XX8_INTF_DAC_DIF_TDM (7 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_ONELINE_24 (5 << CS42XX8_INTF_DAC_DIF_SHIFT)
+#define CS42XX8_INTF_DAC_DIF_TDM (6 << CS42XX8_INTF_DAC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_SHIFT 0
#define CS42XX8_INTF_ADC_DIF_WIDTH 3
#define CS42XX8_INTF_ADC_DIF_MASK (((1 << CS42XX8_INTF_ADC_DIF_WIDTH) - 1) << CS42XX8_INTF_ADC_DIF_SHIFT)
@@ -138,8 +138,8 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap);
#define CS42XX8_INTF_ADC_DIF_RIGHTJ (2 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_RIGHTJ_16 (3 << CS42XX8_INTF_ADC_DIF_SHIFT)
#define CS42XX8_INTF_ADC_DIF_ONELINE_20 (4 << CS42XX8_INTF_ADC_DIF_SHIFT)
-#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
-#define CS42XX8_INTF_ADC_DIF_TDM (7 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_ONELINE_24 (5 << CS42XX8_INTF_ADC_DIF_SHIFT)
+#define CS42XX8_INTF_ADC_DIF_TDM (6 << CS42XX8_INTF_ADC_DIF_SHIFT)
/* ADC Control & DAC De-Emphasis (Address 05h) */
#define CS42XX8_ADCCTL_ADC_HPF_FREEZE_SHIFT 7
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index d5fd00a..8f95b03 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -253,7 +253,7 @@ static void v253_close(struct tty_struct *tty)
/* Prevent the codec driver from further accessing the modem */
codec->hw_write = NULL;
cx20442->control_data = NULL;
- codec->card->pop_time = 0;
+ codec->component.card->pop_time = 0;
}
/* Line discipline .hangup() */
@@ -281,7 +281,7 @@ static void v253_receive(struct tty_struct *tty,
/* Set up codec driver access to modem controls */
cx20442->control_data = tty;
codec->hw_write = (hw_write_t)tty->ops->write;
- codec->card->pop_time = 1;
+ codec->component.card->pop_time = 1;
}
}
@@ -372,7 +372,7 @@ static int cx20442_codec_probe(struct snd_soc_codec *codec)
snd_soc_codec_set_drvdata(codec, cx20442);
codec->hw_write = NULL;
- codec->card->pop_time = 0;
+ codec->component.card->pop_time = 0;
return 0;
}
@@ -383,8 +383,8 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec)
struct cx20442_priv *cx20442 = snd_soc_codec_get_drvdata(codec);
if (cx20442->control_data) {
- struct tty_struct *tty = cx20442->control_data;
- tty_hangup(tty);
+ struct tty_struct *tty = cx20442->control_data;
+ tty_hangup(tty);
}
if (!IS_ERR(cx20442->por)) {
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index f5fccc7..4a063fa 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -26,10 +26,6 @@
#include <sound/max98090.h>
#include "max98090.h"
-#define DEBUG
-#define EXTMIC_METHOD
-#define EXTMIC_METHOD_TEST
-
/* Allows for sparsely populated register maps */
static struct reg_default max98090_reg[] = {
{ 0x00, 0x00 }, /* 00 Software Reset */
@@ -820,7 +816,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
else
val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
-
if (val >= 1) {
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
max98090->pa1en = val - 1; /* Update for volatile */
@@ -1140,7 +1135,6 @@ static const struct snd_kcontrol_new max98090_mixhprsel_mux =
SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
-
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_INPUT("DMICL"),
@@ -1304,7 +1298,6 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
-
SND_SOC_DAPM_INPUT("DMIC3"),
SND_SOC_DAPM_INPUT("DMIC4"),
@@ -1315,7 +1308,6 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
-
{"MIC1 Input", NULL, "MIC1"},
{"MIC2 Input", NULL, "MIC2"},
@@ -1493,17 +1485,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"SPKR", NULL, "SPK Right Out"},
{"RCVL", NULL, "RCV Left Out"},
{"RCVR", NULL, "RCV Right Out"},
-
};
static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
-
/* DMIC inputs */
{"DMIC3", NULL, "DMIC3_ENA"},
{"DMIC4", NULL, "DMIC4_ENA"},
{"DMIC3", NULL, "AHPF"},
{"DMIC4", NULL, "AHPF"},
-
};
static int max98090_add_widgets(struct snd_soc_codec *codec)
@@ -1531,7 +1520,6 @@ static int max98090_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
ARRAY_SIZE(max98091_dapm_routes));
-
}
return 0;
@@ -2212,22 +2200,11 @@ static struct snd_soc_dai_driver max98090_dai[] = {
}
};
-static void max98090_handle_pdata(struct snd_soc_codec *codec)
-{
- struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
- struct max98090_pdata *pdata = max98090->pdata;
-
- if (!pdata) {
- dev_err(codec->dev, "No platform data\n");
- return;
- }
-
-}
-
static int max98090_probe(struct snd_soc_codec *codec)
{
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
struct max98090_cdata *cdata;
+ enum max98090_type devtype;
int ret = 0;
dev_dbg(codec->dev, "max98090_probe\n");
@@ -2263,16 +2240,21 @@ static int max98090_probe(struct snd_soc_codec *codec)
}
if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
- max98090->devtype = MAX98090;
+ devtype = MAX98090;
dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
} else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
- max98090->devtype = MAX98091;
+ devtype = MAX98091;
dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
} else {
- max98090->devtype = MAX98090;
+ devtype = MAX98090;
dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
}
+ if (max98090->devtype != devtype) {
+ dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
+ max98090->devtype = devtype;
+ }
+
max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
@@ -2284,7 +2266,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
/* Register for interrupts */
dev_dbg(codec->dev, "irq = %d\n", max98090->irq);
- ret = request_threaded_irq(max98090->irq, NULL,
+ ret = devm_request_threaded_irq(codec->dev, max98090->irq, NULL,
max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max98090_interrupt", codec);
if (ret < 0) {
@@ -2317,8 +2299,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
- max98090_handle_pdata(codec);
-
max98090_add_widgets(codec);
err_access:
@@ -2428,7 +2408,7 @@ static int max98090_runtime_suspend(struct device *dev)
}
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int max98090_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
@@ -2460,12 +2440,14 @@ static const struct dev_pm_ops max98090_pm = {
static const struct i2c_device_id max98090_i2c_id[] = {
{ "max98090", MAX98090 },
+ { "max98091", MAX98091 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
static const struct of_device_id max98090_of_match[] = {
{ .compatible = "maxim,max98090", },
+ { .compatible = "maxim,max98091", },
{ }
};
MODULE_DEVICE_TABLE(of, max98090_of_match);
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 9965277..388f90a 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -766,11 +766,11 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
if (ret)
- return ret;
+ goto out;
ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
if (ret)
- return ret;
+ goto out;
}
dev_set_drvdata(&pdev->dev, priv);
@@ -783,6 +783,8 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
+out:
+ of_node_put(np);
return ret;
}
diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
index 3a80ba4..57b0c94 100644
--- a/sound/soc/codecs/pcm1792a.c
+++ b/sound/soc/codecs/pcm1792a.c
@@ -36,6 +36,7 @@
#define PCM1792A_DAC_VOL_LEFT 0x10
#define PCM1792A_DAC_VOL_RIGHT 0x11
#define PCM1792A_FMT_CONTROL 0x12
+#define PCM1792A_MODE_CONTROL 0x13
#define PCM1792A_SOFT_MUTE PCM1792A_FMT_CONTROL
#define PCM1792A_FMT_MASK 0x70
@@ -164,6 +165,8 @@ static const struct snd_kcontrol_new pcm1792a_controls[] = {
SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
pcm1792a_dac_tlv),
+ SOC_SINGLE("DAC Invert Output Switch", PCM1792A_MODE_CONTROL, 7, 1, 0),
+ SOC_SINGLE("DAC Rolloff Filter Switch", PCM1792A_MODE_CONTROL, 1, 1, 0),
};
static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h
index 7a83d1f..51d5470 100644
--- a/sound/soc/codecs/pcm1792a.h
+++ b/sound/soc/codecs/pcm1792a.h
@@ -18,7 +18,8 @@
#define __PCM1792A_H__
#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
- SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S16_LE)
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
new file mode 100644
index 0000000..218f86e
--- /dev/null
+++ b/sound/soc/codecs/rt286.c
@@ -0,0 +1,1224 @@
+/*
+ * rt286.c -- RT286 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 Realtek Semiconductor Corp.
+ * Author: Bard Liao <bardliao@realtek.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/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.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 <sound/jack.h>
+#include <linux/workqueue.h>
+#include <sound/rt286.h>
+#include <sound/hda_verbs.h>
+
+#include "rt286.h"
+
+#define RT286_VENDOR_ID 0x10ec0286
+
+struct rt286_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct rt286_platform_data pdata;
+ struct i2c_client *i2c;
+ struct snd_soc_jack *jack;
+ struct delayed_work jack_detect_work;
+ int sys_clk;
+ struct reg_default *index_cache;
+};
+
+static struct reg_default rt286_index_def[] = {
+ { 0x01, 0xaaaa },
+ { 0x02, 0x8aaa },
+ { 0x03, 0x0002 },
+ { 0x04, 0xaf01 },
+ { 0x08, 0x000d },
+ { 0x09, 0xd810 },
+ { 0x0a, 0x0060 },
+ { 0x0b, 0x0000 },
+ { 0x0d, 0x2800 },
+ { 0x0f, 0x0000 },
+ { 0x19, 0x0a17 },
+ { 0x20, 0x0020 },
+ { 0x33, 0x0208 },
+ { 0x49, 0x0004 },
+ { 0x4f, 0x50e9 },
+ { 0x50, 0x2c00 },
+ { 0x63, 0x2902 },
+ { 0x67, 0x1111 },
+ { 0x68, 0x1016 },
+ { 0x69, 0x273f },
+};
+#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def)
+
+static const struct reg_default rt286_reg[] = {
+ { 0x00170500, 0x00000400 },
+ { 0x00220000, 0x00000031 },
+ { 0x00239000, 0x0000007f },
+ { 0x0023a000, 0x0000007f },
+ { 0x00270500, 0x00000400 },
+ { 0x00370500, 0x00000400 },
+ { 0x00870500, 0x00000400 },
+ { 0x00920000, 0x00000031 },
+ { 0x00935000, 0x000000c3 },
+ { 0x00936000, 0x000000c3 },
+ { 0x00970500, 0x00000400 },
+ { 0x00b37000, 0x00000097 },
+ { 0x00b37200, 0x00000097 },
+ { 0x00b37300, 0x00000097 },
+ { 0x00c37000, 0x00000000 },
+ { 0x00c37100, 0x00000080 },
+ { 0x01270500, 0x00000400 },
+ { 0x01370500, 0x00000400 },
+ { 0x01371f00, 0x411111f0 },
+ { 0x01439000, 0x00000080 },
+ { 0x0143a000, 0x00000080 },
+ { 0x01470700, 0x00000000 },
+ { 0x01470500, 0x00000400 },
+ { 0x01470c00, 0x00000000 },
+ { 0x01470100, 0x00000000 },
+ { 0x01837000, 0x00000000 },
+ { 0x01870500, 0x00000400 },
+ { 0x02050000, 0x00000000 },
+ { 0x02139000, 0x00000080 },
+ { 0x0213a000, 0x00000080 },
+ { 0x02170100, 0x00000000 },
+ { 0x02170500, 0x00000400 },
+ { 0x02170700, 0x00000000 },
+ { 0x02270100, 0x00000000 },
+ { 0x02370100, 0x00000000 },
+ { 0x02040000, 0x00004002 },
+ { 0x01870700, 0x00000020 },
+ { 0x00830000, 0x000000c3 },
+ { 0x00930000, 0x000000c3 },
+ { 0x01270700, 0x00000000 },
+};
+
+static bool rt286_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT286_GET_HP_SENSE:
+ case RT286_GET_MIC1_SENSE:
+ case RT286_PROC_COEF:
+ return true;
+ default:
+ return false;
+ }
+
+
+}
+
+static bool rt286_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0 ... 0xff:
+ case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
+ case RT286_GET_HP_SENSE:
+ case RT286_GET_MIC1_SENSE:
+ case RT286_SET_AUDIO_POWER:
+ case RT286_SET_HPO_POWER:
+ case RT286_SET_SPK_POWER:
+ case RT286_SET_DMIC1_POWER:
+ case RT286_SPK_MUX:
+ case RT286_HPO_MUX:
+ case RT286_ADC0_MUX:
+ case RT286_ADC1_MUX:
+ case RT286_SET_MIC1:
+ case RT286_SET_PIN_HPO:
+ case RT286_SET_PIN_SPK:
+ case RT286_SET_PIN_DMIC1:
+ case RT286_SPK_EAPD:
+ case RT286_SET_AMP_GAIN_HPO:
+ case RT286_SET_DMIC2_DEFAULT:
+ case RT286_DACL_GAIN:
+ case RT286_DACR_GAIN:
+ case RT286_ADCL_GAIN:
+ case RT286_ADCR_GAIN:
+ case RT286_MIC_GAIN:
+ case RT286_SPOL_GAIN:
+ case RT286_SPOR_GAIN:
+ case RT286_HPOL_GAIN:
+ case RT286_HPOR_GAIN:
+ case RT286_F_DAC_SWITCH:
+ case RT286_F_RECMIX_SWITCH:
+ case RT286_REC_MIC_SWITCH:
+ case RT286_REC_I2S_SWITCH:
+ case RT286_REC_LINE_SWITCH:
+ case RT286_REC_BEEP_SWITCH:
+ case RT286_DAC_FORMAT:
+ case RT286_ADC_FORMAT:
+ case RT286_COEF_INDEX:
+ case RT286_PROC_COEF:
+ case RT286_SET_AMP_GAIN_ADC_IN1:
+ case RT286_SET_AMP_GAIN_ADC_IN2:
+ case RT286_SET_POWER(RT286_DAC_OUT1):
+ case RT286_SET_POWER(RT286_DAC_OUT2):
+ case RT286_SET_POWER(RT286_ADC_IN1):
+ case RT286_SET_POWER(RT286_ADC_IN2):
+ case RT286_SET_POWER(RT286_DMIC2):
+ case RT286_SET_POWER(RT286_MIC1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
+{
+ struct i2c_client *client = context;
+ struct rt286_priv *rt286 = i2c_get_clientdata(client);
+ u8 data[4];
+ int ret, i;
+
+ /*handle index registers*/
+ if (reg <= 0xff) {
+ rt286_hw_write(client, RT286_COEF_INDEX, reg);
+ reg = RT286_PROC_COEF;
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ if (reg == rt286->index_cache[i].reg) {
+ rt286->index_cache[i].def = value;
+ break;
+ }
+
+ }
+ }
+
+ data[0] = (reg >> 24) & 0xff;
+ data[1] = (reg >> 16) & 0xff;
+ /*
+ * 4 bit VID: reg should be 0
+ * 12 bit VID: value should be 0
+ * So we use an OR operator to handle it rather than use if condition.
+ */
+ data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
+ data[3] = value & 0xff;
+
+ ret = i2c_master_send(client, data, 4);
+
+ if (ret == 4)
+ return 0;
+ else
+ pr_err("ret=%d\n", ret);
+ if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct i2c_msg xfer[2];
+ int ret;
+ __be32 be_reg;
+ unsigned int index, vid, buf = 0x0;
+
+ /*handle index registers*/
+ if (reg <= 0xff) {
+ rt286_hw_write(client, RT286_COEF_INDEX, reg);
+ reg = RT286_PROC_COEF;
+ }
+
+ reg = reg | 0x80000;
+ vid = (reg >> 8) & 0xfff;
+
+ if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
+ index = (reg >> 8) & 0xf;
+ reg = (reg & ~0xf0f) | index;
+ }
+ be_reg = cpu_to_be32(reg);
+
+ /* Write register */
+ xfer[0].addr = client->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = 4;
+ xfer[0].buf = (u8 *)&be_reg;
+
+ /* Read data */
+ xfer[1].addr = client->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = 4;
+ xfer[1].buf = (u8 *)&buf;
+
+ ret = i2c_transfer(client->adapter, xfer, 2);
+ if (ret < 0)
+ return ret;
+ else if (ret != 2)
+ return -EIO;
+
+ *value = be32_to_cpu(buf);
+
+ return 0;
+}
+
+static void rt286_index_sync(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ int i;
+
+ for (i = 0; i < INDEX_CACHE_SIZE; i++) {
+ snd_soc_write(codec, rt286->index_cache[i].reg,
+ rt286->index_cache[i].def);
+ }
+}
+
+static int rt286_support_power_controls[] = {
+ RT286_DAC_OUT1,
+ RT286_DAC_OUT2,
+ RT286_ADC_IN1,
+ RT286_ADC_IN2,
+ RT286_MIC1,
+ RT286_DMIC1,
+ RT286_DMIC2,
+ RT286_SPK_OUT,
+ RT286_HP_OUT,
+};
+#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls)
+
+static int rt286_jack_detect(struct snd_soc_codec *codec, bool *hp, bool *mic)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val, buf;
+ int i;
+
+ *hp = false;
+ *mic = false;
+
+ if (rt286->pdata.cbj_en) {
+ buf = snd_soc_read(codec, RT286_GET_HP_SENSE);
+ *hp = buf & 0x80000000;
+ if (*hp) {
+ /* power on HV,VERF */
+ snd_soc_update_bits(codec,
+ RT286_POWER_CTRL1, 0x1001, 0x0);
+ /* power LDO1 */
+ snd_soc_update_bits(codec,
+ RT286_POWER_CTRL2, 0x4, 0x4);
+ snd_soc_write(codec, RT286_SET_MIC1, 0x24);
+ val = snd_soc_read(codec, RT286_CBJ_CTRL2);
+
+ msleep(200);
+ i = 40;
+ while (((val & 0x0800) == 0) && (i > 0)) {
+ val = snd_soc_read(codec,
+ RT286_CBJ_CTRL2);
+ i--;
+ msleep(20);
+ }
+
+ if (0x0400 == (val & 0x0700)) {
+ *mic = false;
+
+ snd_soc_write(codec,
+ RT286_SET_MIC1, 0x20);
+ /* power off HV,VERF */
+ snd_soc_update_bits(codec,
+ RT286_POWER_CTRL1, 0x1001, 0x1001);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+ snd_soc_update_bits(codec,
+ RT286_CBJ_CTRL1, 0x0030, 0x0000);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+ } else if ((0x0200 == (val & 0x0700)) ||
+ (0x0100 == (val & 0x0700))) {
+ *mic = true;
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+ snd_soc_update_bits(codec,
+ RT286_CBJ_CTRL1, 0x0030, 0x0020);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+ } else {
+ *mic = false;
+ }
+
+ snd_soc_update_bits(codec,
+ RT286_MISC_CTRL1,
+ 0x0060, 0x0000);
+ } else {
+ snd_soc_update_bits(codec,
+ RT286_MISC_CTRL1,
+ 0x0060, 0x0020);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL3,
+ 0xc000, 0x8000);
+ snd_soc_update_bits(codec,
+ RT286_CBJ_CTRL1,
+ 0x0030, 0x0020);
+ snd_soc_update_bits(codec,
+ RT286_A_BIAS_CTRL2,
+ 0xc000, 0x8000);
+
+ *mic = false;
+ }
+ } else {
+ buf = snd_soc_read(codec, RT286_GET_HP_SENSE);
+ *hp = buf & 0x80000000;
+ buf = snd_soc_read(codec, RT286_GET_MIC1_SENSE);
+ *mic = buf & 0x80000000;
+ }
+
+ return 0;
+}
+
+static void rt286_jack_detect_work(struct work_struct *work)
+{
+ struct rt286_priv *rt286 =
+ container_of(work, struct rt286_priv, jack_detect_work.work);
+ int status = 0;
+ bool hp = false;
+ bool mic = false;
+
+ rt286_jack_detect(rt286->codec, &hp, &mic);
+
+ if (hp == true)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic == true)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt286->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+}
+
+int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ rt286->jack = jack;
+
+ /* Send an initial empty report */
+ snd_soc_jack_report(rt286->jack, 0,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt286_mic_detect);
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt286_snd_controls[] = {
+ SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN,
+ RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN,
+ RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
+ SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN,
+ 0, 0x3, 0, mic_vol_tlv),
+ SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN,
+ RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1),
+};
+
+/* Digital Mixer */
+static const struct snd_kcontrol_new rt286_front_mix[] = {
+ SOC_DAPM_SINGLE("DAC Switch", RT286_F_DAC_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+};
+
+/* Analog Input Mixer */
+static const struct snd_kcontrol_new rt286_rec_mix[] = {
+ SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+ SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH,
+ RT286_MUTE_SFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new spo_enable_control =
+ SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK,
+ RT286_SET_PIN_SFT, 1, 0);
+
+static const struct snd_kcontrol_new hpol_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN,
+ RT286_MUTE_SFT, 1, 1);
+
+static const struct snd_kcontrol_new hpor_enable_control =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN,
+ RT286_MUTE_SFT, 1, 1);
+
+/* ADC0 source */
+static const char * const rt286_adc_src[] = {
+ "Mic", "RECMIX", "Dmic"
+};
+
+static const int rt286_adc_values[] = {
+ 0, 4, 5,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT,
+ RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc0_mux =
+ SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(
+ rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT,
+ RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
+
+static const struct snd_kcontrol_new rt286_adc1_mux =
+ SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum);
+
+static const char * const rt286_dac_src[] = {
+ "Front", "Surround"
+};
+/* HP-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX,
+ 0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_hpo_mux =
+SOC_DAPM_ENUM("HPO source", rt286_hpo_enum);
+
+/* SPK-OUT source */
+static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX,
+ 0, rt286_dac_src);
+
+static const struct snd_kcontrol_new rt286_spo_mux =
+SOC_DAPM_ENUM("SPO source", rt286_spo_enum);
+
+static int rt286_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec,
+ RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec,
+ RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt286_adc_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ unsigned int nid;
+
+ nid = (w->reg >> 20) & 0xff;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec,
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+ 0x7080, 0x7000);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec,
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
+ 0x7080, 0x7080);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+ /* Input Lines */
+ SND_SOC_DAPM_INPUT("DMIC1 Pin"),
+ SND_SOC_DAPM_INPUT("DMIC2 Pin"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("LINE1"),
+ SND_SOC_DAPM_INPUT("Beep"),
+
+ /* DMIC */
+ SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1,
+ NULL, 0, rt286_set_dmic1_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1,
+ NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
+ 0, 0, NULL, 0),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
+ rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)),
+
+ /* ADCs */
+ SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1,
+ &rt286_adc0_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1,
+ &rt286_adc1_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ /* Output Side */
+ /* DACs */
+ SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Output Mux */
+ SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux),
+ SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux),
+
+ SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO,
+ RT286_SET_PIN_SFT, 0, NULL, 0),
+
+ /* Output Mixer */
+ SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1,
+ rt286_front_mix, ARRAY_SIZE(rt286_front_mix)),
+ SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1,
+ NULL, 0),
+
+ /* Output Pga */
+ SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
+ &spo_enable_control, rt286_spk_event,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
+ &hpol_enable_control),
+ SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
+ &hpor_enable_control),
+
+ /* Output Lines */
+ SND_SOC_DAPM_OUTPUT("SPOL"),
+ SND_SOC_DAPM_OUTPUT("SPOR"),
+ SND_SOC_DAPM_OUTPUT("HPO Pin"),
+ SND_SOC_DAPM_OUTPUT("SPDIF"),
+};
+
+static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+ {"DMIC1", NULL, "DMIC1 Pin"},
+ {"DMIC2", NULL, "DMIC2 Pin"},
+ {"DMIC1", NULL, "DMIC Receiver"},
+ {"DMIC2", NULL, "DMIC Receiver"},
+
+ {"RECMIX", "Beep Switch", "Beep"},
+ {"RECMIX", "Line1 Switch", "LINE1"},
+ {"RECMIX", "Mic1 Switch", "MIC1"},
+
+ {"ADC 0 Mux", "Dmic", "DMIC1"},
+ {"ADC 0 Mux", "RECMIX", "RECMIX"},
+ {"ADC 0 Mux", "Mic", "MIC1"},
+ {"ADC 1 Mux", "Dmic", "DMIC2"},
+ {"ADC 1 Mux", "RECMIX", "RECMIX"},
+ {"ADC 1 Mux", "Mic", "MIC1"},
+
+ {"ADC 0", NULL, "ADC 0 Mux"},
+ {"ADC 1", NULL, "ADC 1 Mux"},
+
+ {"AIF1TX", NULL, "ADC 0"},
+ {"AIF2TX", NULL, "ADC 1"},
+
+ {"DAC 0", NULL, "AIF1RX"},
+ {"DAC 1", NULL, "AIF2RX"},
+
+ {"Front", "DAC Switch", "DAC 0"},
+ {"Front", "RECMIX Switch", "RECMIX"},
+
+ {"Surround", NULL, "DAC 1"},
+
+ {"SPK Mux", "Front", "Front"},
+ {"SPK Mux", "Surround", "Surround"},
+
+ {"HPO Mux", "Front", "Front"},
+ {"HPO Mux", "Surround", "Surround"},
+
+ {"SPO", "Switch", "SPK Mux"},
+ {"HPO L", "Switch", "HPO Mux"},
+ {"HPO R", "Switch", "HPO Mux"},
+ {"HPO L", NULL, "HP Power"},
+ {"HPO R", NULL, "HP Power"},
+
+ {"SPOL", NULL, "SPO"},
+ {"SPOR", NULL, "SPO"},
+ {"HPO Pin", NULL, "HPO L"},
+ {"HPO Pin", NULL, "HPO R"},
+};
+
+static int rt286_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 rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0;
+ int d_len_code;
+
+ switch (params_rate(params)) {
+ /* bit 14 0:48K 1:44.1K */
+ case 44100:
+ val |= 0x4000;
+ break;
+ case 48000:
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported sample rate %d\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ switch (rt286->sys_clk) {
+ case 12288000:
+ case 24576000:
+ if (params_rate(params) != 48000) {
+ dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt286->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ case 11289600:
+ case 22579200:
+ if (params_rate(params) != 44100) {
+ dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
+ params_rate(params), rt286->sys_clk);
+ return -EINVAL;
+ }
+ break;
+ }
+
+ if (params_channels(params) <= 16) {
+ /* bit 3:0 Number of Channel */
+ val |= (params_channels(params) - 1);
+ } else {
+ dev_err(codec->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ d_len_code = 0;
+ switch (params_width(params)) {
+ /* bit 6:4 Bits per Sample */
+ case 16:
+ d_len_code = 0;
+ val |= (0x1 << 4);
+ break;
+ case 32:
+ d_len_code = 2;
+ val |= (0x4 << 4);
+ break;
+ case 20:
+ d_len_code = 1;
+ val |= (0x2 << 4);
+ break;
+ case 24:
+ d_len_code = 2;
+ val |= (0x3 << 4);
+ break;
+ case 8:
+ d_len_code = 3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
+ dev_dbg(codec->dev, "format val = 0x%x\n", val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val);
+ else
+ snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, val);
+
+ return 0;
+}
+
+static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x800, 0x800);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x800, 0x0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x0);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x1 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x2 << 8);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x300, 0x3 << 8);
+ break;
+ default:
+ return -EINVAL;
+ }
+ /* bit 15 Stream Type 0:PCM 1:Non-PCM */
+ snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0);
+ snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0);
+
+ return 0;
+}
+
+static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
+
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x0100, 0x0);
+ snd_soc_update_bits(codec,
+ RT286_PLL_CTRL1, 0x20, 0x20);
+ } else {
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x0100, 0x0100);
+ snd_soc_update_bits(codec,
+ RT286_PLL_CTRL, 0x4, 0x4);
+ snd_soc_update_bits(codec,
+ RT286_PLL_CTRL1, 0x20, 0x0);
+ }
+
+ switch (freq) {
+ case 19200000:
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ dev_err(codec->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x40, 0x40);
+ break;
+ case 24000000:
+ if (RT286_SCLK_S_MCLK == clk_id) {
+ dev_err(codec->dev, "Should not use MCLK\n");
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x40, 0x0);
+ break;
+ case 12288000:
+ case 11289600:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x8, 0x0);
+ snd_soc_update_bits(codec,
+ RT286_CLK_DIV, 0xfc1e, 0x0004);
+ break;
+ case 24576000:
+ case 22579200:
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL2, 0x8, 0x8);
+ snd_soc_update_bits(codec,
+ RT286_CLK_DIV, 0xfc1e, 0x5406);
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported system clock\n");
+ return -EINVAL;
+ }
+
+ rt286->sys_clk = freq;
+
+ return 0;
+}
+
+static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_codec *codec = dai->codec;
+
+ dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
+ if (50 == ratio)
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x1000, 0x1000);
+ else
+ snd_soc_update_bits(codec,
+ RT286_I2S_CTRL1, 0x1000, 0x0);
+
+
+ return 0;
+}
+
+static int rt286_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
+ snd_soc_write(codec,
+ RT286_SET_AUDIO_POWER, AC_PWRST_D0);
+ snd_soc_update_bits(codec,
+ RT286_DC_GAIN, 0x200, 0x200);
+ }
+ break;
+
+ case SND_SOC_BIAS_ON:
+ mdelay(10);
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ snd_soc_write(codec,
+ RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+ snd_soc_update_bits(codec,
+ RT286_DC_GAIN, 0x200, 0x0);
+ break;
+
+ default:
+ break;
+ }
+ codec->dapm.bias_level = level;
+
+ return 0;
+}
+
+static irqreturn_t rt286_irq(int irq, void *data)
+{
+ struct rt286_priv *rt286 = data;
+ bool hp = false;
+ bool mic = false;
+ int status = 0;
+
+ rt286_jack_detect(rt286->codec, &hp, &mic);
+
+ /* Clear IRQ */
+ snd_soc_update_bits(rt286->codec,
+ RT286_IRQ_CTRL, 0x1, 0x1);
+
+ if (hp == true)
+ status |= SND_JACK_HEADPHONE;
+
+ if (mic == true)
+ status |= SND_JACK_MICROPHONE;
+
+ snd_soc_jack_report(rt286->jack, status,
+ SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
+
+ pm_wakeup_event(&rt286->i2c->dev, 300);
+
+ return IRQ_HANDLED;
+}
+
+static int rt286_probe(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ codec->dapm.bias_level = SND_SOC_BIAS_OFF;
+ rt286->codec = codec;
+
+ return 0;
+}
+
+static int rt286_remove(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ cancel_delayed_work_sync(&rt286->jack_detect_work);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt286_suspend(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt286->regmap, true);
+ regcache_mark_dirty(rt286->regmap);
+
+ return 0;
+}
+
+static int rt286_resume(struct snd_soc_codec *codec)
+{
+ struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt286->regmap, false);
+ rt286_index_sync(codec);
+ regcache_sync(rt286->regmap);
+
+ return 0;
+}
+#else
+#define rt286_suspend NULL
+#define rt286_resume NULL
+#endif
+
+#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static const struct snd_soc_dai_ops rt286_aif_dai_ops = {
+ .hw_params = rt286_hw_params,
+ .set_fmt = rt286_set_dai_fmt,
+ .set_sysclk = rt286_set_dai_sysclk,
+ .set_bclk_ratio = rt286_set_bclk_ratio,
+};
+
+static struct snd_soc_dai_driver rt286_dai[] = {
+ {
+ .name = "rt286-aif1",
+ .id = RT286_AIF1,
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .ops = &rt286_aif_dai_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "rt286-aif2",
+ .id = RT286_AIF2,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = RT286_STEREO_RATES,
+ .formats = RT286_FORMATS,
+ },
+ .ops = &rt286_aif_dai_ops,
+ .symmetric_rates = 1,
+ },
+
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt286 = {
+ .probe = rt286_probe,
+ .remove = rt286_remove,
+ .suspend = rt286_suspend,
+ .resume = rt286_resume,
+ .set_bias_level = rt286_set_bias_level,
+ .idle_bias_off = true,
+ .controls = rt286_snd_controls,
+ .num_controls = ARRAY_SIZE(rt286_snd_controls),
+ .dapm_widgets = rt286_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
+ .dapm_routes = rt286_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
+};
+
+static const struct regmap_config rt286_regmap = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .max_register = 0x02370100,
+ .volatile_reg = rt286_volatile_register,
+ .readable_reg = rt286_readable_register,
+ .reg_write = rt286_hw_write,
+ .reg_read = rt286_hw_read,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt286_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt286_reg),
+};
+
+static const struct i2c_device_id rt286_i2c_id[] = {
+ {"rt286", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
+
+static const struct acpi_device_id rt286_acpi_match[] = {
+ { "INT343A", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
+
+static int rt286_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt286_priv *rt286;
+ int i, ret;
+
+ rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
+ GFP_KERNEL);
+ if (NULL == rt286)
+ return -ENOMEM;
+
+ rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap);
+ if (IS_ERR(rt286->regmap)) {
+ ret = PTR_ERR(rt286->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt286->regmap,
+ RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
+ if (ret != RT286_VENDOR_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %x is not rt286\n", ret);
+ return -ENODEV;
+ }
+
+ rt286->index_cache = rt286_index_def;
+ rt286->i2c = i2c;
+ i2c_set_clientdata(i2c, rt286);
+
+ if (pdata)
+ rt286->pdata = *pdata;
+
+ regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
+
+ for (i = 0; i < RT286_POWER_REG_LEN; i++)
+ regmap_write(rt286->regmap,
+ RT286_SET_POWER(rt286_support_power_controls[i]),
+ AC_PWRST_D1);
+
+ if (!rt286->pdata.cbj_en) {
+ regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
+ regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
+ regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xf000, 0xb000);
+ } else {
+ regmap_update_bits(rt286->regmap,
+ RT286_CBJ_CTRL1, 0xf000, 0x5000);
+ }
+
+ mdelay(10);
+
+ if (!rt286->pdata.gpio2_en)
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
+ else
+ regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
+
+ mdelay(10);
+
+ /*Power down LDO2*/
+ regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
+
+ /*Set depop parameter*/
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
+ regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
+
+ if (rt286->i2c->irq) {
+ regmap_update_bits(rt286->regmap,
+ RT286_IRQ_CTRL, 0x2, 0x2);
+
+ INIT_DELAYED_WORK(&rt286->jack_detect_work,
+ rt286_jack_detect_work);
+ schedule_delayed_work(&rt286->jack_detect_work,
+ msecs_to_jiffies(1250));
+
+ ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to reguest IRQ: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
+ rt286_dai, ARRAY_SIZE(rt286_dai));
+
+ return ret;
+}
+
+static int rt286_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt286);
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+
+static struct i2c_driver rt286_i2c_driver = {
+ .driver = {
+ .name = "rt286",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(rt286_acpi_match),
+ },
+ .probe = rt286_i2c_probe,
+ .remove = rt286_i2c_remove,
+ .id_table = rt286_i2c_id,
+};
+
+module_i2c_driver(rt286_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT286 driver");
+MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt286.h b/sound/soc/codecs/rt286.h
new file mode 100644
index 0000000..b539b73
--- /dev/null
+++ b/sound/soc/codecs/rt286.h
@@ -0,0 +1,198 @@
+/*
+ * rt286.h -- RT286 ALSA SoC audio driver
+ *
+ * Copyright 2011 Realtek Microelectronics
+ * Author: Johnny Hsu <johnnyhsu@realtek.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 __RT286_H__
+#define __RT286_H__
+
+#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
+
+#define RT286_AUDIO_FUNCTION_GROUP 0x01
+#define RT286_DAC_OUT1 0x02
+#define RT286_DAC_OUT2 0x03
+#define RT286_ADC_IN1 0x09
+#define RT286_ADC_IN2 0x08
+#define RT286_MIXER_IN 0x0b
+#define RT286_MIXER_OUT1 0x0c
+#define RT286_MIXER_OUT2 0x0d
+#define RT286_DMIC1 0x12
+#define RT286_DMIC2 0x13
+#define RT286_SPK_OUT 0x14
+#define RT286_MIC1 0x18
+#define RT286_LINE1 0x1a
+#define RT286_BEEP 0x1d
+#define RT286_SPDIF 0x1e
+#define RT286_VENDOR_REGISTERS 0x20
+#define RT286_HP_OUT 0x21
+#define RT286_MIXER_IN1 0x22
+#define RT286_MIXER_IN2 0x23
+
+#define RT286_SET_PIN_SFT 6
+#define RT286_SET_PIN_ENABLE 0x40
+#define RT286_SET_PIN_DISABLE 0
+#define RT286_SET_EAPD_HIGH 0x2
+#define RT286_SET_EAPD_LOW 0
+
+#define RT286_MUTE_SFT 7
+
+/* Verb commands */
+#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
+#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
+#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP)
+#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT)
+#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT)
+#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1)
+#define RT286_SPK_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0)
+#define RT286_HPO_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0)
+#define RT286_ADC0_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0)
+#define RT286_ADC1_MUX\
+ VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0)
+#define RT286_SET_MIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0)
+#define RT286_SET_PIN_HPO\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0)
+#define RT286_SET_PIN_SPK\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0)
+#define RT286_SET_PIN_DMIC1\
+ VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0)
+#define RT286_SPK_EAPD\
+ VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0)
+#define RT286_SET_AMP_GAIN_HPO\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN1\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0)
+#define RT286_SET_AMP_GAIN_ADC_IN2\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0)
+#define RT286_GET_HP_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0)
+#define RT286_GET_MIC1_SENSE\
+ VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0)
+#define RT286_SET_DMIC2_DEFAULT\
+ VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0)
+#define RT286_DACL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000)
+#define RT286_DACR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000)
+#define RT286_ADCL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000)
+#define RT286_ADCR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000)
+#define RT286_MIC_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000)
+#define RT286_SPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000)
+#define RT286_SPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000)
+#define RT286_HPOL_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000)
+#define RT286_HPOR_GAIN\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000)
+#define RT286_F_DAC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000)
+#define RT286_F_RECMIX_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100)
+#define RT286_REC_MIC_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000)
+#define RT286_REC_I2S_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100)
+#define RT286_REC_LINE_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200)
+#define RT286_REC_BEEP_SWITCH\
+ VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300)
+#define RT286_DAC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0)
+#define RT286_ADC_FORMAT\
+ VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0)
+#define RT286_COEF_INDEX\
+ VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
+#define RT286_PROC_COEF\
+ VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
+
+/* Index registers */
+#define RT286_A_BIAS_CTRL1 0x01
+#define RT286_A_BIAS_CTRL2 0x02
+#define RT286_POWER_CTRL1 0x03
+#define RT286_A_BIAS_CTRL3 0x04
+#define RT286_POWER_CTRL2 0x08
+#define RT286_I2S_CTRL1 0x09
+#define RT286_I2S_CTRL2 0x0a
+#define RT286_CLK_DIV 0x0b
+#define RT286_DC_GAIN 0x0d
+#define RT286_POWER_CTRL3 0x0f
+#define RT286_MIC1_DET_CTRL 0x19
+#define RT286_MISC_CTRL1 0x20
+#define RT286_IRQ_CTRL 0x33
+#define RT286_PLL_CTRL1 0x49
+#define RT286_CBJ_CTRL1 0x4f
+#define RT286_CBJ_CTRL2 0x50
+#define RT286_PLL_CTRL 0x63
+#define RT286_DEPOP_CTRL1 0x66
+#define RT286_DEPOP_CTRL2 0x67
+#define RT286_DEPOP_CTRL3 0x68
+#define RT286_DEPOP_CTRL4 0x69
+
+/* SPDIF (0x06) */
+#define RT286_SPDIF_SEL_SFT 0
+#define RT286_SPDIF_SEL_PCM0 0
+#define RT286_SPDIF_SEL_PCM1 1
+#define RT286_SPDIF_SEL_SPOUT 2
+#define RT286_SPDIF_SEL_PP 3
+
+/* RECMIX (0x0b) */
+#define RT286_M_REC_BEEP_SFT 0
+#define RT286_M_REC_LINE1_SFT 1
+#define RT286_M_REC_MIC1_SFT 2
+#define RT286_M_REC_I2S_SFT 3
+
+/* Front (0x0c) */
+#define RT286_M_FRONT_DAC_SFT 0
+#define RT286_M_FRONT_REC_SFT 1
+
+/* SPK-OUT (0x14) */
+#define RT286_M_SPK_MUX_SFT 14
+#define RT286_SPK_SEL_MASK 0x1
+#define RT286_SPK_SEL_SFT 0
+#define RT286_SPK_SEL_F 0
+#define RT286_SPK_SEL_S 1
+
+/* HP-OUT (0x21) */
+#define RT286_M_HP_MUX_SFT 14
+#define RT286_HP_SEL_MASK 0x1
+#define RT286_HP_SEL_SFT 0
+#define RT286_HP_SEL_F 0
+#define RT286_HP_SEL_S 1
+
+/* ADC (0x22) (0x23) */
+#define RT286_ADC_SEL_MASK 0x7
+#define RT286_ADC_SEL_SFT 0
+#define RT286_ADC_SEL_SURR 0
+#define RT286_ADC_SEL_FRONT 1
+#define RT286_ADC_SEL_DMIC 2
+#define RT286_ADC_SEL_BEEP 4
+#define RT286_ADC_SEL_LINE1 5
+#define RT286_ADC_SEL_I2S 6
+#define RT286_ADC_SEL_MIC1 7
+
+#define RT286_SCLK_S_MCLK 0
+#define RT286_SCLK_S_PLL 1
+
+enum {
+ RT286_AIF1,
+ RT286_AIF2,
+ RT286_AIFS,
+};
+
+int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+#endif /* __RT286_H__ */
+
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 3d39f0b..8f4c73d 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1277,7 +1277,7 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
return ret;
}
- ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
+ ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
if (ret)
goto err_ldo_remove;
@@ -1285,13 +1285,16 @@ static int sgtl5000_enable_regulators(struct snd_soc_codec *codec)
ret = regulator_bulk_enable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
if (ret)
- goto err_ldo_remove;
+ goto err_regulator_free;
/* wait for all power rails bring up */
udelay(10);
return 0;
+err_regulator_free:
+ regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
err_ldo_remove:
if (!external_vddd)
ldo_regulator_remove(codec);
@@ -1361,6 +1364,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
err:
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
+ regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
ldo_regulator_remove(codec);
return ret;
@@ -1374,6 +1379,8 @@ static int sgtl5000_remove(struct snd_soc_codec *codec)
regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
sgtl5000->supplies);
+ regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
+ sgtl5000->supplies);
ldo_regulator_remove(codec);
return 0;
diff --git a/sound/soc/codecs/sigmadsp-i2c.c b/sound/soc/codecs/sigmadsp-i2c.c
new file mode 100644
index 0000000..246081a
--- /dev/null
+++ b/sound/soc/codecs/sigmadsp-i2c.c
@@ -0,0 +1,35 @@
+/*
+ * Load Analog Devices SigmaStudio firmware files
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/i2c.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+#include "sigmadsp.h"
+
+static int sigma_action_write_i2c(void *control_data,
+ const struct sigma_action *sa, size_t len)
+{
+ return i2c_master_send(control_data, (const unsigned char *)&sa->addr,
+ len);
+}
+
+int process_sigma_firmware(struct i2c_client *client, const char *name)
+{
+ struct sigma_firmware ssfw;
+
+ ssfw.control_data = client;
+ ssfw.write = sigma_action_write_i2c;
+
+ return _process_sigma_firmware(&client->dev, &ssfw, name);
+}
+EXPORT_SYMBOL(process_sigma_firmware);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("SigmaDSP I2C firmware loader");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp-regmap.c b/sound/soc/codecs/sigmadsp-regmap.c
new file mode 100644
index 0000000..f78ed8d
--- /dev/null
+++ b/sound/soc/codecs/sigmadsp-regmap.c
@@ -0,0 +1,36 @@
+/*
+ * Load Analog Devices SigmaStudio firmware files
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/regmap.h>
+#include <linux/export.h>
+#include <linux/module.h>
+
+#include "sigmadsp.h"
+
+static int sigma_action_write_regmap(void *control_data,
+ const struct sigma_action *sa, size_t len)
+{
+ return regmap_raw_write(control_data, be16_to_cpu(sa->addr),
+ sa->payload, len - 2);
+}
+
+int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap,
+ const char *name)
+{
+ struct sigma_firmware ssfw;
+
+ ssfw.control_data = regmap;
+ ssfw.write = sigma_action_write_regmap;
+
+ return _process_sigma_firmware(dev, &ssfw, name);
+}
+EXPORT_SYMBOL(process_sigma_firmware_regmap);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("SigmaDSP regmap firmware loader");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index 4068f24..f2de7e0 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -34,23 +34,6 @@ enum {
SIGMA_ACTION_END,
};
-struct sigma_action {
- u8 instr;
- u8 len_hi;
- __le16 len;
- __be16 addr;
- unsigned char payload[];
-} __packed;
-
-struct sigma_firmware {
- const struct firmware *fw;
- size_t pos;
-
- void *control_data;
- int (*write)(void *control_data, const struct sigma_action *sa,
- size_t len);
-};
-
static inline u32 sigma_action_len(struct sigma_action *sa)
{
return (sa->len_hi << 16) | le16_to_cpu(sa->len);
@@ -138,7 +121,7 @@ process_sigma_actions(struct sigma_firmware *ssfw)
return 0;
}
-static int _process_sigma_firmware(struct device *dev,
+int _process_sigma_firmware(struct device *dev,
struct sigma_firmware *ssfw, const char *name)
{
int ret;
@@ -197,50 +180,6 @@ static int _process_sigma_firmware(struct device *dev,
return ret;
}
-
-#if IS_ENABLED(CONFIG_I2C)
-
-static int sigma_action_write_i2c(void *control_data,
- const struct sigma_action *sa, size_t len)
-{
- return i2c_master_send(control_data, (const unsigned char *)&sa->addr,
- len);
-}
-
-int process_sigma_firmware(struct i2c_client *client, const char *name)
-{
- struct sigma_firmware ssfw;
-
- ssfw.control_data = client;
- ssfw.write = sigma_action_write_i2c;
-
- return _process_sigma_firmware(&client->dev, &ssfw, name);
-}
-EXPORT_SYMBOL(process_sigma_firmware);
-
-#endif
-
-#if IS_ENABLED(CONFIG_REGMAP)
-
-static int sigma_action_write_regmap(void *control_data,
- const struct sigma_action *sa, size_t len)
-{
- return regmap_raw_write(control_data, be16_to_cpu(sa->addr),
- sa->payload, len - 2);
-}
-
-int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap,
- const char *name)
-{
- struct sigma_firmware ssfw;
-
- ssfw.control_data = regmap;
- ssfw.write = sigma_action_write_regmap;
-
- return _process_sigma_firmware(dev, &ssfw, name);
-}
-EXPORT_SYMBOL(process_sigma_firmware_regmap);
-
-#endif
+EXPORT_SYMBOL_GPL(_process_sigma_firmware);
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sigmadsp.h b/sound/soc/codecs/sigmadsp.h
index e439cbd..c47cd23 100644
--- a/sound/soc/codecs/sigmadsp.h
+++ b/sound/soc/codecs/sigmadsp.h
@@ -12,6 +12,26 @@
#include <linux/device.h>
#include <linux/regmap.h>
+struct sigma_action {
+ u8 instr;
+ u8 len_hi;
+ __le16 len;
+ __be16 addr;
+ unsigned char payload[];
+} __packed;
+
+struct sigma_firmware {
+ const struct firmware *fw;
+ size_t pos;
+
+ void *control_data;
+ int (*write)(void *control_data, const struct sigma_action *sa,
+ size_t len);
+};
+
+int _process_sigma_firmware(struct device *dev,
+ struct sigma_firmware *ssfw, const char *name);
+
struct i2c_client;
extern int process_sigma_firmware(struct i2c_client *client, const char *name);
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 2341910..1cdae8c 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1178,7 +1178,7 @@ static void aic31xx_pdata_from_of(struct aic31xx_priv *aic31xx)
}
#endif /* CONFIG_OF */
-static void aic31xx_device_init(struct aic31xx_priv *aic31xx)
+static int aic31xx_device_init(struct aic31xx_priv *aic31xx)
{
int ret, i;
@@ -1197,7 +1197,7 @@ static void aic31xx_device_init(struct aic31xx_priv *aic31xx)
"aic31xx-reset-pin");
if (ret < 0) {
dev_err(aic31xx->dev, "not able to acquire gpio\n");
- return;
+ return ret;
}
}
@@ -1210,6 +1210,7 @@ static void aic31xx_device_init(struct aic31xx_priv *aic31xx)
if (ret != 0)
dev_err(aic31xx->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
}
static int aic31xx_i2c_probe(struct i2c_client *i2c,
@@ -1239,7 +1240,9 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
aic31xx->pdata.codec_type = id->driver_data;
- aic31xx_device_init(aic31xx);
+ ret = aic31xx_device_init(aic31xx);
+ if (ret)
+ return ret;
return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
aic31xx_dai_driver,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index e12fafb..5360772 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -879,7 +879,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
case SNDRV_PCM_FORMAT_S20_3LE:
data |= (0x01 << 4);
break;
- case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
data |= (0x02 << 4);
break;
case SNDRV_PCM_FORMAT_S32_LE:
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index df3a750..ff006cc 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -1404,7 +1404,7 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
if (dac33->irq >= 0) {
ret = request_irq(dac33->irq, dac33_interrupt_handler,
IRQF_TRIGGER_RISING,
- codec->name, codec);
+ codec->component.name, codec);
if (ret < 0) {
dev_err(codec->dev, "Could not request IRQ%d (%d)\n",
dac33->irq, ret);
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 69e12a3..6ab1570 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -344,17 +344,16 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
static void twl4030_apll_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
- int status = -1;
if (enable) {
twl4030->apll_enabled++;
if (twl4030->apll_enabled == 1)
- status = twl4030_audio_enable_resource(
+ twl4030_audio_enable_resource(
TWL4030_AUDIO_RES_APLL);
} else {
twl4030->apll_enabled--;
if (!twl4030->apll_enabled)
- status = twl4030_audio_disable_resource(
+ twl4030_audio_disable_resource(
TWL4030_AUDIO_RES_APLL);
}
}
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index edf27ac..12fc0ae 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -479,7 +479,7 @@ static struct snd_soc_dai_driver uda134x_dai = {
static int uda134x_soc_probe(struct snd_soc_codec *codec)
{
struct uda134x_priv *uda134x;
- struct uda134x_platform_data *pd = codec->card->dev->platform_data;
+ struct uda134x_platform_data *pd = codec->component.card->dev->platform_data;
const struct snd_soc_dapm_widget *widgets;
unsigned num_widgets;
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 91a9ea2..7bb0d36 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -735,8 +735,7 @@ WM5100_MIXER_CONTROLS("LHPF4", WM5100_HPLP4MIX_INPUT_1_SOURCE),
static void wm5100_seq_notifier(struct snd_soc_dapm_context *dapm,
enum snd_soc_dapm_type event, int subseq)
{
- struct snd_soc_codec *codec = container_of(dapm,
- struct snd_soc_codec, dapm);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
u16 val, expect, i;
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 289b64d..f602349 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -612,6 +612,62 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
return 0;
}
+static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ uint16_t data;
+
+ mutex_lock(&codec->mutex);
+ data = cpu_to_be16(arizona->dac_comp_coeff);
+ memcpy(ucontrol->value.bytes.data, &data, sizeof(data));
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+
+static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&codec->mutex);
+ memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data,
+ sizeof(arizona->dac_comp_coeff));
+ arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff);
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+
+static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&codec->mutex);
+ ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+
+static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+
+ mutex_lock(&codec->mutex);
+ arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
+ mutex_unlock(&codec->mutex);
+
+ return 0;
+}
+
static const char *wm5102_osr_text[] = {
"Low power", "Normal", "High performance",
};
@@ -843,6 +899,12 @@ SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+SND_SOC_BYTES_EXT("Output Compensation Coefficient", 2,
+ wm5102_out_comp_coeff_get, wm5102_out_comp_coeff_put),
+
+SOC_SINGLE_EXT("Output Compensation Switch", 0, 0, 1, 0,
+ wm5102_out_comp_switch_get, wm5102_out_comp_switch_put),
+
WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
@@ -1653,6 +1715,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm5102-aif2",
@@ -1674,6 +1737,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm5102-aif3",
@@ -1695,6 +1759,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm5102-slim1",
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 2e5fcb5..2f2ec26 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1485,6 +1485,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm5110-aif2",
@@ -1506,6 +1507,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm5110-aif3",
@@ -1527,6 +1529,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm5110-slim1",
@@ -1596,6 +1599,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
+ arizona_init_mono(codec);
ret = snd_soc_add_codec_controls(codec, wm_adsp2_fw_controls, 8);
if (ret != 0)
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index 392285e..d9e634c 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1341,21 +1341,18 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
{
struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec);
struct wm8350 *wm8350 = priv->wm8350;
- int irq;
int ena;
switch (which) {
case WM8350_JDL:
priv->hpl.jack = jack;
priv->hpl.report = report;
- irq = WM8350_IRQ_CODEC_JCK_DET_L;
ena = WM8350_JDL_ENA;
break;
case WM8350_JDR:
priv->hpr.jack = jack;
priv->hpr.report = report;
- irq = WM8350_IRQ_CODEC_JCK_DET_R;
ena = WM8350_JDR_ENA;
break;
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index b84940c..ec3250d 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -281,8 +281,7 @@ static int wm8903_dcs_event(struct snd_soc_dapm_widget *w,
static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
enum snd_soc_dapm_type event, int subseq)
{
- struct snd_soc_codec *codec = container_of(dapm,
- struct snd_soc_codec, dapm);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
int dcs_mode = WM8903_DCS_MODE_WRITE_STOP;
int i, val;
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index a145d04..e96349b 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -472,7 +472,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec)
* list each time to find the desired power state do so now
* and save the result.
*/
- list_for_each_entry(w, &codec->card->widgets, list) {
+ list_for_each_entry(w, &codec->component.card->widgets, list) {
if (w->dapm != &codec->dapm)
continue;
if (strcmp(w->name, "LOUT1 PGA") == 0)
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 247b390..9719d3c 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -3505,6 +3505,7 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
return IRQ_HANDLED;
}
+/* Should be called with accdet_lock held */
static void wm1811_micd_stop(struct snd_soc_codec *codec)
{
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
@@ -3512,14 +3513,10 @@ static void wm1811_micd_stop(struct snd_soc_codec *codec)
if (!wm8994->jackdet)
return;
- mutex_lock(&wm8994->accdet_lock);
-
snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, WM8958_MICD_ENA, 0);
wm1811_jackdet_set_mode(codec, WM1811_JACKDET_MODE_JACK);
- mutex_unlock(&wm8994->accdet_lock);
-
if (wm8994->wm8994->pdata.jd_ext_cap)
snd_soc_dapm_disable_pin(&codec->dapm,
"MICBIAS2");
@@ -3560,10 +3557,10 @@ static void wm8958_open_circuit_work(struct work_struct *work)
open_circuit_work.work);
struct device *dev = wm8994->wm8994->dev;
- wm1811_micd_stop(wm8994->hubs.codec);
-
mutex_lock(&wm8994->accdet_lock);
+ wm1811_micd_stop(wm8994->hubs.codec);
+
dev_dbg(dev, "Reporting open circuit\n");
wm8994->jack_mic = false;
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 6926633..f16ff4f 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -620,15 +620,12 @@ static int bg_event(struct snd_soc_dapm_widget *w,
static int cp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- int ret = 0;
-
switch (event) {
case SND_SOC_DAPM_POST_PMU:
msleep(5);
break;
default:
WARN(1, "Invalid event %d\n", event);
- ret = -EINVAL;
}
return 0;
@@ -690,8 +687,7 @@ static void wait_for_dc_servo(struct snd_soc_codec *codec, u16 mask)
static void wm8996_seq_notifier(struct snd_soc_dapm_context *dapm,
enum snd_soc_dapm_type event, int subseq)
{
- struct snd_soc_codec *codec = container_of(dapm,
- struct snd_soc_codec, dapm);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
u16 val, mask;
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index bb9b47b..ab33fe5 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -967,6 +967,7 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm8997-aif2",
@@ -988,6 +989,7 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
},
.ops = &arizona_dai_ops,
.symmetric_rates = 1,
+ .symmetric_samplebits = 1,
},
{
.name = "wm8997-slim1",
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 0600271..f412a99 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -1382,7 +1382,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int ret;
int val;
- dsp->card = codec->card;
+ dsp->card = codec->component.card;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1617,7 +1617,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
- dsp->card = codec->card;
+ dsp->card = codec->component.card;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -1758,3 +1758,5 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
return 0;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
+
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig
index 50a0987..d69510c 100644
--- a/sound/soc/davinci/Kconfig
+++ b/sound/soc/davinci/Kconfig
@@ -1,12 +1,29 @@
config SND_DAVINCI_SOC
- tristate "SoC Audio for TI DAVINCI or AM33XX/AM43XX chips"
- depends on ARCH_DAVINCI || SOC_AM33XX || SOC_AM43XX
+ tristate "SoC Audio for TI DAVINCI"
+ depends on ARCH_DAVINCI
+
+config SND_EDMA_SOC
+ tristate "SoC Audio for Texas Instruments chips using eDMA (AM33XX/43XX)"
+ depends on SOC_AM33XX || SOC_AM43XX
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M here if you want audio support for TI SoC which uses eDMA.
+ The following line of SoCs are supported by this platform driver:
+ - AM335x
+ - AM437x/AM438x
config SND_DAVINCI_SOC_I2S
tristate
config SND_DAVINCI_SOC_MCASP
- tristate
+ tristate "Multichannel Audio Serial Port (McASP) support"
+ depends on SND_DAVINCI_SOC || SND_OMAP_SOC || SND_EDMA_SOC
+ help
+ Say Y or M here if you want to have support for McASP IP found in
+ various Texas Instruments SoCs like:
+ - daVinci devices
+ - Sitara line of SoCs (AM335x, AM438x, etc)
+ - DRA7x devices
config SND_DAVINCI_SOC_VCIF
tristate
@@ -18,7 +35,7 @@ config SND_DAVINCI_SOC_GENERIC_EVM
config SND_AM33XX_SOC_EVM
tristate "SoC Audio for the AM33XX chip based boards"
- depends on SND_DAVINCI_SOC && SOC_AM33XX && I2C
+ depends on SND_EDMA_SOC && SOC_AM33XX && I2C
select SND_DAVINCI_SOC_GENERIC_EVM
help
Say Y or M if you want to add support for SoC audio on AM33XX
diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile
index 744d4d9..09bf2ba 100644
--- a/sound/soc/davinci/Makefile
+++ b/sound/soc/davinci/Makefile
@@ -1,10 +1,12 @@
# DAVINCI Platform Support
snd-soc-davinci-objs := davinci-pcm.o
+snd-soc-edma-objs := edma-pcm.o
snd-soc-davinci-i2s-objs := davinci-i2s.o
snd-soc-davinci-mcasp-objs:= davinci-mcasp.o
snd-soc-davinci-vcif-objs:= davinci-vcif.o
obj-$(CONFIG_SND_DAVINCI_SOC) += snd-soc-davinci.o
+obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o
obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o
obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o
obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 9afb146..c28508d 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -27,6 +27,7 @@
#include <linux/of_platform.h>
#include <linux/of_device.h>
+#include <sound/asoundef.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -36,6 +37,7 @@
#include <sound/omap-pcm.h>
#include "davinci-pcm.h"
+#include "edma-pcm.h"
#include "davinci-mcasp.h"
#define MCASP_MAX_AFIFO_DEPTH 64
@@ -63,6 +65,7 @@ struct davinci_mcasp {
u8 num_serializer;
u8 *serial_dir;
u8 version;
+ u8 bclk_div;
u16 bclk_lrclk_ratio;
int streams;
@@ -417,6 +420,7 @@ static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div
ACLKXDIV(div - 1), ACLKXDIV_MASK);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG,
ACLKRDIV(div - 1), ACLKRDIV_MASK);
+ mcasp->bclk_div = div;
break;
case 2: /* BCLK/LRCLK ratio */
@@ -637,8 +641,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
}
/* S/PDIF */
-static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp)
+static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp,
+ unsigned int rate)
{
+ u32 cs_value = 0;
+ u8 *cs_bytes = (u8*) &cs_value;
+
/* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0
and LSB first */
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15));
@@ -660,6 +668,46 @@ static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp)
/* Enable the DIT */
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN);
+ /* Set S/PDIF channel status bits */
+ cs_bytes[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
+ cs_bytes[1] = IEC958_AES1_CON_PCM_CODER;
+
+ switch (rate) {
+ case 22050:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ cs_bytes[3] |= IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ printk(KERN_WARNING "unsupported sampling rate: %d\n", rate);
+ return -EINVAL;
+ }
+
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRA_REG, cs_value);
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRB_REG, cs_value);
+
return 0;
}
@@ -675,15 +723,22 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
int period_size = params_period_size(params);
int ret;
- /* If mcasp is BCLK master we need to set BCLK divider */
- if (mcasp->bclk_master) {
+ /*
+ * If mcasp is BCLK master, and a BCLK divider was not provided by
+ * the machine driver, we need to calculate the ratio.
+ */
+ if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) {
unsigned int bclk_freq = snd_soc_params_to_bclk(params);
+ unsigned int div = mcasp->sysclk_freq / bclk_freq;
if (mcasp->sysclk_freq % bclk_freq != 0) {
- dev_err(mcasp->dev, "Can't produce required BCLK\n");
- return -EINVAL;
+ if (((mcasp->sysclk_freq / div) - bclk_freq) >
+ (bclk_freq - (mcasp->sysclk_freq / (div+1))))
+ div++;
+ dev_warn(mcasp->dev,
+ "Inaccurate BCLK: %u Hz / %u != %u Hz\n",
+ mcasp->sysclk_freq, div, bclk_freq);
}
- davinci_mcasp_set_clkdiv(
- cpu_dai, 1, mcasp->sysclk_freq / bclk_freq);
+ davinci_mcasp_set_clkdiv(cpu_dai, 1, div);
}
ret = mcasp_common_hw_param(mcasp, substream->stream,
@@ -692,7 +747,7 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
return ret;
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
- ret = mcasp_dit_hw_param(mcasp);
+ ret = mcasp_dit_hw_param(mcasp, params_rate(params));
else
ret = mcasp_i2s_hw_param(mcasp, substream->stream);
@@ -720,6 +775,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
case SNDRV_PCM_FORMAT_U24_LE:
case SNDRV_PCM_FORMAT_S24_LE:
+ dma_params->data_type = 4;
+ word_length = 24;
+ break;
+
case SNDRV_PCM_FORMAT_U32_LE:
case SNDRV_PCM_FORMAT_S32_LE:
dma_params->data_type = 4;
@@ -778,7 +837,7 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
{
struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
- if (mcasp->version == MCASP_VERSION_4) {
+ if (mcasp->version >= MCASP_VERSION_3) {
/* Using dmaengine PCM */
dai->playback_dma_data =
&mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
@@ -1223,14 +1282,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
goto err;
switch (mcasp->version) {
+#if IS_BUILTIN(CONFIG_SND_DAVINCI_SOC) || \
+ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
+ IS_MODULE(CONFIG_SND_DAVINCI_SOC))
case MCASP_VERSION_1:
case MCASP_VERSION_2:
- case MCASP_VERSION_3:
ret = davinci_soc_platform_register(&pdev->dev);
break;
+#endif
+#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \
+ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
+ IS_MODULE(CONFIG_SND_EDMA_SOC))
+ case MCASP_VERSION_3:
+ ret = edma_pcm_platform_register(&pdev->dev);
+ break;
+#endif
+#if IS_BUILTIN(CONFIG_SND_OMAP_SOC) || \
+ (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \
+ IS_MODULE(CONFIG_SND_OMAP_SOC))
case MCASP_VERSION_4:
ret = omap_pcm_platform_register(&pdev->dev);
break;
+#endif
default:
dev_err(&pdev->dev, "Invalid McASP version: %d\n",
mcasp->version);
diff --git a/sound/soc/davinci/edma-pcm.c b/sound/soc/davinci/edma-pcm.c
index d38afb1..605e643 100644
--- a/sound/soc/davinci/edma-pcm.c
+++ b/sound/soc/davinci/edma-pcm.c
@@ -28,8 +28,8 @@
static const struct snd_pcm_hardware edma_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_BATCH |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
SNDRV_PCM_INFO_INTERLEAVED,
.buffer_bytes_max = 128 * 1024,
.period_bytes_min = 32,
diff --git a/sound/soc/davinci/edma-pcm.h b/sound/soc/davinci/edma-pcm.h
index 894c378..b095774 100644
--- a/sound/soc/davinci/edma-pcm.h
+++ b/sound/soc/davinci/edma-pcm.h
@@ -20,6 +20,13 @@
#ifndef __EDMA_PCM_H__
#define __EDMA_PCM_H__
+#if IS_ENABLED(CONFIG_SND_EDMA_SOC)
int edma_pcm_platform_register(struct device *dev);
+#else
+static inline int edma_pcm_platform_register(struct device *dev)
+{
+ return 0;
+}
+#endif /* CONFIG_SND_EDMA_SOC */
#endif /* __EDMA_PCM_H__ */
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 3793362..f54a8fc 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -2,9 +2,20 @@ menu "SoC Audio for Freescale CPUs"
comment "Common SoC Audio options for Freescale CPUs:"
+config SND_SOC_FSL_ASRC
+ tristate "Asynchronous Sample Rate Converter (ASRC) module support"
+ select REGMAP_MMIO
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y if you want to add Asynchronous Sample Rate Converter (ASRC)
+ support for the Freescale CPUs.
+ This option is only useful for out-of-tree drivers since
+ in-tree drivers select it automatically.
+
config SND_SOC_FSL_SAI
tristate "Synchronous Audio Interface (SAI) module support"
select REGMAP_MMIO
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y if you want to add Synchronous Audio Interface (SAI)
@@ -15,7 +26,7 @@ config SND_SOC_FSL_SAI
config SND_SOC_FSL_SSI
tristate "Synchronous Serial Interface module support"
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
- select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+ select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC)
select REGMAP_MMIO
help
Say Y if you want to add Synchronous Serial Interface (SSI)
@@ -27,7 +38,7 @@ config SND_SOC_FSL_SPDIF
tristate "Sony/Philips Digital Interface module support"
select REGMAP_MMIO
select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
- select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && ARCH_MXC
+ select SND_SOC_IMX_PCM_FIQ if SND_IMX_SOC != n && (MXC_TZIC || MXC_AVIC)
help
Say Y if you want to add Sony/Philips Digital Interface (SPDIF)
support for the Freescale CPUs.
@@ -37,6 +48,7 @@ config SND_SOC_FSL_SPDIF
config SND_SOC_FSL_ESAI
tristate "Enhanced Serial Audio Interface (ESAI) module support"
select REGMAP_MMIO
+ select SND_SOC_IMX_PCM_DMA if SND_IMX_SOC != n
select SND_SOC_FSL_UTILS
help
Say Y if you want to add Enhanced Synchronous Audio Interface
diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile
index db254e3..9ff5926 100644
--- a/sound/soc/fsl/Makefile
+++ b/sound/soc/fsl/Makefile
@@ -11,6 +11,7 @@ snd-soc-p1022-rdk-objs := p1022_rdk.o
obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
# Freescale SSI/DMA/SAI/SPDIF Support
+snd-soc-fsl-asrc-objs := fsl_asrc.o fsl_asrc_dma.o
snd-soc-fsl-sai-objs := fsl_sai.o
snd-soc-fsl-ssi-y := fsl_ssi.o
snd-soc-fsl-ssi-$(CONFIG_DEBUG_FS) += fsl_ssi_dbg.o
@@ -18,6 +19,7 @@ snd-soc-fsl-spdif-objs := fsl_spdif.o
snd-soc-fsl-esai-objs := fsl_esai.o
snd-soc-fsl-utils-objs := fsl_utils.o
snd-soc-fsl-dma-objs := fsl_dma.o
+obj-$(CONFIG_SND_SOC_FSL_ASRC) += snd-soc-fsl-asrc.o
obj-$(CONFIG_SND_SOC_FSL_SAI) += snd-soc-fsl-sai.o
obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
new file mode 100644
index 0000000..8221104
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -0,0 +1,995 @@
+/*
+ * Freescale ASRC ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/dma-imx.h>
+#include <linux/pm_runtime.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_asrc.h"
+
+#define IDEAL_RATIO_DECIMAL_DEPTH 26
+
+#define pair_err(fmt, ...) \
+ dev_err(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+#define pair_dbg(fmt, ...) \
+ dev_dbg(&asrc_priv->pdev->dev, "Pair %c: " fmt, 'A' + index, ##__VA_ARGS__)
+
+/* Sample rates are aligned with that defined in pcm.h file */
+static const u8 process_option[][8][2] = {
+ /* 32kHz 44.1kHz 48kHz 64kHz 88.2kHz 96kHz 176kHz 192kHz */
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 5512Hz */
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 8kHz */
+ {{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 11025Hz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 16kHz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0},}, /* 22050Hz */
+ {{0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0}, {0, 0},}, /* 32kHz */
+ {{0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 44.1kHz */
+ {{0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0}, {0, 0},}, /* 48kHz */
+ {{1, 2}, {0, 2}, {0, 2}, {0, 1}, {0, 1}, {0, 1}, {0, 1}, {0, 0},}, /* 64kHz */
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 88.2kHz */
+ {{1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 1}, {1, 1}, {1, 1}, {1, 1},}, /* 96kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 176kHz */
+ {{2, 2}, {2, 2}, {2, 2}, {2, 1}, {2, 1}, {2, 1}, {2, 1}, {2, 1},}, /* 192kHz */
+};
+
+/* Corresponding to process_option */
+static int supported_input_rate[] = {
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, 88200,
+ 96000, 176400, 192000,
+};
+
+static int supported_asrc_rate[] = {
+ 32000, 44100, 48000, 64000, 88200, 96000, 176400, 192000,
+};
+
+/**
+ * The following tables map the relationship between asrc_inclk/asrc_outclk in
+ * fsl_asrc.h and the registers of ASRCSR
+ */
+static unsigned char input_clk_map_imx35[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+static unsigned char output_clk_map_imx35[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+};
+
+/* i.MX53 uses the same map for input and output */
+static unsigned char input_clk_map_imx53[] = {
+/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
+ 0x0, 0x1, 0x2, 0x7, 0x4, 0x5, 0x6, 0x3, 0x8, 0x9, 0xa, 0xb, 0xc, 0xf, 0xe, 0xd,
+};
+
+static unsigned char output_clk_map_imx53[] = {
+/* 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf */
+ 0x8, 0x9, 0xa, 0x7, 0xc, 0x5, 0x6, 0xb, 0x0, 0x1, 0x2, 0x3, 0x4, 0xf, 0xe, 0xd,
+};
+
+static unsigned char *clk_map[2];
+
+/**
+ * Request ASRC pair
+ *
+ * It assigns pair by the order of A->C->B because allocation of pair B,
+ * within range [ANCA, ANCA+ANCB-1], depends on the channels of pair A
+ * while pair A and pair C are comparatively independent.
+ */
+static int fsl_asrc_request_pair(int channels, struct fsl_asrc_pair *pair)
+{
+ enum asrc_pair_index index = ASRC_INVALID_PAIR;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct device *dev = &asrc_priv->pdev->dev;
+ unsigned long lock_flags;
+ int i, ret = 0;
+
+ spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+
+ for (i = ASRC_PAIR_A; i < ASRC_PAIR_MAX_NUM; i++) {
+ if (asrc_priv->pair[i] != NULL)
+ continue;
+
+ index = i;
+
+ if (i != ASRC_PAIR_B)
+ break;
+ }
+
+ if (index == ASRC_INVALID_PAIR) {
+ dev_err(dev, "all pairs are busy now\n");
+ ret = -EBUSY;
+ } else if (asrc_priv->channel_avail < channels) {
+ dev_err(dev, "can't afford required channels: %d\n", channels);
+ ret = -EINVAL;
+ } else {
+ asrc_priv->channel_avail -= channels;
+ asrc_priv->pair[index] = pair;
+ pair->channels = channels;
+ pair->index = index;
+ }
+
+ spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+
+ return ret;
+}
+
+/**
+ * Release ASRC pair
+ *
+ * It clears the resource from asrc_priv and releases the occupied channels.
+ */
+static void fsl_asrc_release_pair(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ unsigned long lock_flags;
+
+ /* Make sure the pair is disabled */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_MASK(index), 0);
+
+ spin_lock_irqsave(&asrc_priv->lock, lock_flags);
+
+ asrc_priv->channel_avail += pair->channels;
+ asrc_priv->pair[index] = NULL;
+ pair->error = 0;
+
+ spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
+}
+
+/**
+ * Configure input and output thresholds
+ */
+static void fsl_asrc_set_watermarks(struct fsl_asrc_pair *pair, u32 in, u32 out)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+
+ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ ASRMCRi_EXTTHRSHi_MASK |
+ ASRMCRi_INFIFO_THRESHOLD_MASK |
+ ASRMCRi_OUTFIFO_THRESHOLD_MASK,
+ ASRMCRi_EXTTHRSHi |
+ ASRMCRi_INFIFO_THRESHOLD(in) |
+ ASRMCRi_OUTFIFO_THRESHOLD(out));
+}
+
+/**
+ * Calculate the total divisor between asrck clock rate and sample rate
+ *
+ * It follows the formula clk_rate = samplerate * (2 ^ prescaler) * divider
+ */
+static u32 fsl_asrc_cal_asrck_divisor(struct fsl_asrc_pair *pair, u32 div)
+{
+ u32 ps;
+
+ /* Calculate the divisors: prescaler [2^0, 2^7], divder [1, 8] */
+ for (ps = 0; div > 8; ps++)
+ div >>= 1;
+
+ return ((div - 1) << ASRCDRi_AxCPi_WIDTH) | ps;
+}
+
+/**
+ * Calculate and set the ratio for Ideal Ratio mode only
+ *
+ * The ratio is a 32-bit fixed point value with 26 fractional bits.
+ */
+static int fsl_asrc_set_ideal_ratio(struct fsl_asrc_pair *pair,
+ int inrate, int outrate)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ unsigned long ratio;
+ int i;
+
+ if (!outrate) {
+ pair_err("output rate should not be zero\n");
+ return -EINVAL;
+ }
+
+ /* Calculate the intergal part of the ratio */
+ ratio = (inrate / outrate) << IDEAL_RATIO_DECIMAL_DEPTH;
+
+ /* ... and then the 26 depth decimal part */
+ inrate %= outrate;
+
+ for (i = 1; i <= IDEAL_RATIO_DECIMAL_DEPTH; i++) {
+ inrate <<= 1;
+
+ if (inrate < outrate)
+ continue;
+
+ ratio |= 1 << (IDEAL_RATIO_DECIMAL_DEPTH - i);
+ inrate -= outrate;
+
+ if (!inrate)
+ break;
+ }
+
+ regmap_write(asrc_priv->regmap, REG_ASRIDRL(index), ratio);
+ regmap_write(asrc_priv->regmap, REG_ASRIDRH(index), ratio >> 24);
+
+ return 0;
+}
+
+/**
+ * Configure the assigned ASRC pair
+ *
+ * It configures those ASRC registers according to a configuration instance
+ * of struct asrc_config which includes in/output sample rate, width, channel
+ * and clock settings.
+ */
+static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair)
+{
+ struct asrc_config *config = pair->config;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ u32 inrate, outrate, indiv, outdiv;
+ u32 clk_index[2], div[2];
+ int in, out, channels;
+ struct clk *clk;
+ bool ideal;
+
+ if (!config) {
+ pair_err("invalid pair config\n");
+ return -EINVAL;
+ }
+
+ /* Validate channels */
+ if (config->channel_num < 1 || config->channel_num > 10) {
+ pair_err("does not support %d channels\n", config->channel_num);
+ return -EINVAL;
+ }
+
+ /* Validate output width */
+ if (config->output_word_width == ASRC_WIDTH_8_BIT) {
+ pair_err("does not support 8bit width output\n");
+ return -EINVAL;
+ }
+
+ inrate = config->input_sample_rate;
+ outrate = config->output_sample_rate;
+ ideal = config->inclk == INCLK_NONE;
+
+ /* Validate input and output sample rates */
+ for (in = 0; in < ARRAY_SIZE(supported_input_rate); in++)
+ if (inrate == supported_input_rate[in])
+ break;
+
+ if (in == ARRAY_SIZE(supported_input_rate)) {
+ pair_err("unsupported input sample rate: %dHz\n", inrate);
+ return -EINVAL;
+ }
+
+ for (out = 0; out < ARRAY_SIZE(supported_asrc_rate); out++)
+ if (outrate == supported_asrc_rate[out])
+ break;
+
+ if (out == ARRAY_SIZE(supported_asrc_rate)) {
+ pair_err("unsupported output sample rate: %dHz\n", outrate);
+ return -EINVAL;
+ }
+
+ /* Validate input and output clock sources */
+ clk_index[IN] = clk_map[IN][config->inclk];
+ clk_index[OUT] = clk_map[OUT][config->outclk];
+
+ /* We only have output clock for ideal ratio mode */
+ clk = asrc_priv->asrck_clk[clk_index[ideal ? OUT : IN]];
+
+ div[IN] = clk_get_rate(clk) / inrate;
+ if (div[IN] == 0) {
+ pair_err("failed to support input sample rate %dHz by asrck_%x\n",
+ inrate, clk_index[ideal ? OUT : IN]);
+ return -EINVAL;
+ }
+
+ clk = asrc_priv->asrck_clk[clk_index[OUT]];
+
+ /* Use fixed output rate for Ideal Ratio mode (INCLK_NONE) */
+ if (ideal)
+ div[OUT] = clk_get_rate(clk) / IDEAL_RATIO_RATE;
+ else
+ div[OUT] = clk_get_rate(clk) / outrate;
+
+ if (div[OUT] == 0) {
+ pair_err("failed to support output sample rate %dHz by asrck_%x\n",
+ outrate, clk_index[OUT]);
+ return -EINVAL;
+ }
+
+ /* Set the channel number */
+ channels = config->channel_num;
+
+ if (asrc_priv->channel_bits < 4)
+ channels /= 2;
+
+ /* Update channels for current pair */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCNCR,
+ ASRCNCR_ANCi_MASK(index, asrc_priv->channel_bits),
+ ASRCNCR_ANCi(index, channels, asrc_priv->channel_bits));
+
+ /* Default setting: Automatic selection for processing mode */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_USRi_MASK(index), 0);
+
+ /* Set the input and output clock sources */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCSR,
+ ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
+ ASRCSR_AICS(index, clk_index[IN]) |
+ ASRCSR_AOCS(index, clk_index[OUT]));
+
+ /* Calculate the input clock divisors */
+ indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
+ outdiv = fsl_asrc_cal_asrck_divisor(pair, div[OUT]);
+
+ /* Suppose indiv and outdiv includes prescaler, so add its MASK too */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCDR(index),
+ ASRCDRi_AOCPi_MASK(index) | ASRCDRi_AICPi_MASK(index) |
+ ASRCDRi_AOCDi_MASK(index) | ASRCDRi_AICDi_MASK(index),
+ ASRCDRi_AOCP(index, outdiv) | ASRCDRi_AICP(index, indiv));
+
+ /* Implement word_width configurations */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR1(index),
+ ASRMCR1i_OW16_MASK | ASRMCR1i_IWD_MASK,
+ ASRMCR1i_OW16(config->output_word_width) |
+ ASRMCR1i_IWD(config->input_word_width));
+
+ /* Enable BUFFER STALL */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRMCR(index),
+ ASRMCRi_BUFSTALLi_MASK, ASRMCRi_BUFSTALLi);
+
+ /* Set default thresholds for input and output FIFO */
+ fsl_asrc_set_watermarks(pair, ASRC_INPUTFIFO_THRESHOLD,
+ ASRC_INPUTFIFO_THRESHOLD);
+
+ /* Configure the followings only for Ideal Ratio mode */
+ if (!ideal)
+ return 0;
+
+ /* Clear ASTSx bit to use Ideal Ratio mode */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ATSi_MASK(index), 0);
+
+ /* Enable Ideal Ratio mode */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
+ ASRCTR_IDR(index) | ASRCTR_USR(index));
+
+ /* Apply configurations for pre- and post-processing */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCFG,
+ ASRCFG_PREMODi_MASK(index) | ASRCFG_POSTMODi_MASK(index),
+ ASRCFG_PREMOD(index, process_option[in][out][0]) |
+ ASRCFG_POSTMOD(index, process_option[in][out][1]));
+
+ return fsl_asrc_set_ideal_ratio(pair, inrate, outrate);
+}
+
+/**
+ * Start the assigned ASRC pair
+ *
+ * It enables the assigned pair and makes it stopped at the stall level.
+ */
+static void fsl_asrc_start_pair(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ int reg, retry = 10, i;
+
+ /* Enable the current pair */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_MASK(index), ASRCTR_ASRCE(index));
+
+ /* Wait for status of initialization */
+ do {
+ udelay(5);
+ regmap_read(asrc_priv->regmap, REG_ASRCFG, &reg);
+ reg &= ASRCFG_INIRQi_MASK(index);
+ } while (!reg && --retry);
+
+ /* Make the input fifo to ASRC STALL level */
+ regmap_read(asrc_priv->regmap, REG_ASRCNCR, &reg);
+ for (i = 0; i < pair->channels * 4; i++)
+ regmap_write(asrc_priv->regmap, REG_ASRDI(index), 0);
+
+ /* Enable overload interrupt */
+ regmap_write(asrc_priv->regmap, REG_ASRIER, ASRIER_AOLIE);
+}
+
+/**
+ * Stop the assigned ASRC pair
+ */
+static void fsl_asrc_stop_pair(struct fsl_asrc_pair *pair)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+
+ /* Stop the current pair */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_MASK(index), 0);
+}
+
+/**
+ * Get DMA channel according to the pair and direction.
+ */
+struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir)
+{
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ enum asrc_pair_index index = pair->index;
+ char name[4];
+
+ sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
+
+ return dma_request_slave_channel(&asrc_priv->pdev->dev, name);
+}
+EXPORT_SYMBOL_GPL(fsl_asrc_get_dma_channel);
+
+static int fsl_asrc_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+ int width = snd_pcm_format_width(params_format(params));
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ unsigned int channels = params_channels(params);
+ unsigned int rate = params_rate(params);
+ struct asrc_config config;
+ int word_width, ret;
+
+ ret = fsl_asrc_request_pair(channels, pair);
+ if (ret) {
+ dev_err(dai->dev, "fail to request asrc pair\n");
+ return ret;
+ }
+
+ pair->config = &config;
+
+ if (width == 16)
+ width = ASRC_WIDTH_16_BIT;
+ else
+ width = ASRC_WIDTH_24_BIT;
+
+ if (asrc_priv->asrc_width == 16)
+ word_width = ASRC_WIDTH_16_BIT;
+ else
+ word_width = ASRC_WIDTH_24_BIT;
+
+ config.pair = pair->index;
+ config.channel_num = channels;
+ config.inclk = INCLK_NONE;
+ config.outclk = OUTCLK_ASRCK1_CLK;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ config.input_word_width = width;
+ config.output_word_width = word_width;
+ config.input_sample_rate = rate;
+ config.output_sample_rate = asrc_priv->asrc_rate;
+ } else {
+ config.input_word_width = word_width;
+ config.output_word_width = width;
+ config.input_sample_rate = asrc_priv->asrc_rate;
+ config.output_sample_rate = rate;
+ }
+
+ ret = fsl_asrc_config_pair(pair);
+ if (ret) {
+ dev_err(dai->dev, "fail to config asrc pair\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsl_asrc_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ if (pair)
+ fsl_asrc_release_pair(pair);
+
+ return 0;
+}
+
+static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ fsl_asrc_start_pair(pair);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ fsl_asrc_stop_pair(pair);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops fsl_asrc_dai_ops = {
+ .hw_params = fsl_asrc_dai_hw_params,
+ .hw_free = fsl_asrc_dai_hw_free,
+ .trigger = fsl_asrc_dai_trigger,
+};
+
+static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
+{
+ struct fsl_asrc *asrc_priv = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &asrc_priv->dma_params_tx,
+ &asrc_priv->dma_params_rx);
+
+ return 0;
+}
+
+#define FSL_ASRC_RATES SNDRV_PCM_RATE_8000_192000
+#define FSL_ASRC_FORMATS (SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE)
+
+static struct snd_soc_dai_driver fsl_asrc_dai = {
+ .probe = fsl_asrc_dai_probe,
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rates = FSL_ASRC_RATES,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rates = FSL_ASRC_RATES,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_asrc_component = {
+ .name = "fsl-asrc-dai",
+};
+
+static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ASRCTR:
+ case REG_ASRIER:
+ case REG_ASRCNCR:
+ case REG_ASRCFG:
+ case REG_ASRCSR:
+ case REG_ASRCDR1:
+ case REG_ASRCDR2:
+ case REG_ASRSTR:
+ case REG_ASRPM1:
+ case REG_ASRPM2:
+ case REG_ASRPM3:
+ case REG_ASRPM4:
+ case REG_ASRPM5:
+ case REG_ASRTFR1:
+ case REG_ASRCCR:
+ case REG_ASRDOA:
+ case REG_ASRDOB:
+ case REG_ASRDOC:
+ case REG_ASRIDRHA:
+ case REG_ASRIDRLA:
+ case REG_ASRIDRHB:
+ case REG_ASRIDRLB:
+ case REG_ASRIDRHC:
+ case REG_ASRIDRLC:
+ case REG_ASR76K:
+ case REG_ASR56K:
+ case REG_ASRMCRA:
+ case REG_ASRFSTA:
+ case REG_ASRMCRB:
+ case REG_ASRFSTB:
+ case REG_ASRMCRC:
+ case REG_ASRFSTC:
+ case REG_ASRMCR1A:
+ case REG_ASRMCR1B:
+ case REG_ASRMCR1C:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_asrc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ASRSTR:
+ case REG_ASRDIA:
+ case REG_ASRDIB:
+ case REG_ASRDIC:
+ case REG_ASRDOA:
+ case REG_ASRDOB:
+ case REG_ASRDOC:
+ case REG_ASRFSTA:
+ case REG_ASRFSTB:
+ case REG_ASRFSTC:
+ case REG_ASRCFG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case REG_ASRCTR:
+ case REG_ASRIER:
+ case REG_ASRCNCR:
+ case REG_ASRCFG:
+ case REG_ASRCSR:
+ case REG_ASRCDR1:
+ case REG_ASRCDR2:
+ case REG_ASRSTR:
+ case REG_ASRPM1:
+ case REG_ASRPM2:
+ case REG_ASRPM3:
+ case REG_ASRPM4:
+ case REG_ASRPM5:
+ case REG_ASRTFR1:
+ case REG_ASRCCR:
+ case REG_ASRDIA:
+ case REG_ASRDIB:
+ case REG_ASRDIC:
+ case REG_ASRIDRHA:
+ case REG_ASRIDRLA:
+ case REG_ASRIDRHB:
+ case REG_ASRIDRLB:
+ case REG_ASRIDRHC:
+ case REG_ASRIDRLC:
+ case REG_ASR76K:
+ case REG_ASR56K:
+ case REG_ASRMCRA:
+ case REG_ASRMCRB:
+ case REG_ASRMCRC:
+ case REG_ASRMCR1A:
+ case REG_ASRMCR1B:
+ case REG_ASRMCR1C:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static struct regmap_config fsl_asrc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+
+ .max_register = REG_ASRMCR1C,
+ .readable_reg = fsl_asrc_readable_reg,
+ .volatile_reg = fsl_asrc_volatile_reg,
+ .writeable_reg = fsl_asrc_writeable_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+/**
+ * Initialize ASRC registers with a default configurations
+ */
+static int fsl_asrc_init(struct fsl_asrc *asrc_priv)
+{
+ /* Halt ASRC internal FP when input FIFO needs data for pair A, B, C */
+ regmap_write(asrc_priv->regmap, REG_ASRCTR, ASRCTR_ASRCEN);
+
+ /* Disable interrupt by default */
+ regmap_write(asrc_priv->regmap, REG_ASRIER, 0x0);
+
+ /* Apply recommended settings for parameters from Reference Manual */
+ regmap_write(asrc_priv->regmap, REG_ASRPM1, 0x7fffff);
+ regmap_write(asrc_priv->regmap, REG_ASRPM2, 0x255555);
+ regmap_write(asrc_priv->regmap, REG_ASRPM3, 0xff7280);
+ regmap_write(asrc_priv->regmap, REG_ASRPM4, 0xff7280);
+ regmap_write(asrc_priv->regmap, REG_ASRPM5, 0xff7280);
+
+ /* Base address for task queue FIFO. Set to 0x7C */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRTFR1,
+ ASRTFR1_TF_BASE_MASK, ASRTFR1_TF_BASE(0xfc));
+
+ /* Set the processing clock for 76KHz to 133M */
+ regmap_write(asrc_priv->regmap, REG_ASR76K, 0x06D6);
+
+ /* Set the processing clock for 56KHz to 133M */
+ return regmap_write(asrc_priv->regmap, REG_ASR56K, 0x0947);
+}
+
+/**
+ * Interrupt handler for ASRC
+ */
+static irqreturn_t fsl_asrc_isr(int irq, void *dev_id)
+{
+ struct fsl_asrc *asrc_priv = (struct fsl_asrc *)dev_id;
+ struct device *dev = &asrc_priv->pdev->dev;
+ enum asrc_pair_index index;
+ u32 status;
+
+ regmap_read(asrc_priv->regmap, REG_ASRSTR, &status);
+
+ /* Clean overload error */
+ regmap_write(asrc_priv->regmap, REG_ASRSTR, ASRSTR_AOLE);
+
+ /*
+ * We here use dev_dbg() for all exceptions because ASRC itself does
+ * not care if FIFO overflowed or underrun while a warning in the
+ * interrupt would result a ridged conversion.
+ */
+ for (index = ASRC_PAIR_A; index < ASRC_PAIR_MAX_NUM; index++) {
+ if (!asrc_priv->pair[index])
+ continue;
+
+ if (status & ASRSTR_ATQOL) {
+ asrc_priv->pair[index]->error |= ASRC_TASK_Q_OVERLOAD;
+ dev_dbg(dev, "ASRC Task Queue FIFO overload\n");
+ }
+
+ if (status & ASRSTR_AOOL(index)) {
+ asrc_priv->pair[index]->error |= ASRC_OUTPUT_TASK_OVERLOAD;
+ pair_dbg("Output Task Overload\n");
+ }
+
+ if (status & ASRSTR_AIOL(index)) {
+ asrc_priv->pair[index]->error |= ASRC_INPUT_TASK_OVERLOAD;
+ pair_dbg("Input Task Overload\n");
+ }
+
+ if (status & ASRSTR_AODO(index)) {
+ asrc_priv->pair[index]->error |= ASRC_OUTPUT_BUFFER_OVERFLOW;
+ pair_dbg("Output Data Buffer has overflowed\n");
+ }
+
+ if (status & ASRSTR_AIDU(index)) {
+ asrc_priv->pair[index]->error |= ASRC_INPUT_BUFFER_UNDERRUN;
+ pair_dbg("Input Data Buffer has underflowed\n");
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int fsl_asrc_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct fsl_asrc *asrc_priv;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret, i;
+ char tmp[16];
+
+ asrc_priv = devm_kzalloc(&pdev->dev, sizeof(*asrc_priv), GFP_KERNEL);
+ if (!asrc_priv)
+ return -ENOMEM;
+
+ asrc_priv->pdev = pdev;
+ strcpy(asrc_priv->name, np->name);
+
+ /* Get the addresses and IRQ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ asrc_priv->paddr = res->start;
+
+ /* Register regmap and let it prepare core clock */
+ if (of_property_read_bool(np, "big-endian"))
+ fsl_asrc_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
+
+ asrc_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "mem", regs,
+ &fsl_asrc_regmap_config);
+ if (IS_ERR(asrc_priv->regmap)) {
+ dev_err(&pdev->dev, "failed to init regmap\n");
+ return PTR_ERR(asrc_priv->regmap);
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+ return irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, fsl_asrc_isr, 0,
+ asrc_priv->name, asrc_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to claim irq %u: %d\n", irq, ret);
+ return ret;
+ }
+
+ asrc_priv->mem_clk = devm_clk_get(&pdev->dev, "mem");
+ if (IS_ERR(asrc_priv->mem_clk)) {
+ dev_err(&pdev->dev, "failed to get mem clock\n");
+ return PTR_ERR(asrc_priv->mem_clk);
+ }
+
+ asrc_priv->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(asrc_priv->ipg_clk)) {
+ dev_err(&pdev->dev, "failed to get ipg clock\n");
+ return PTR_ERR(asrc_priv->ipg_clk);
+ }
+
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++) {
+ sprintf(tmp, "asrck_%x", i);
+ asrc_priv->asrck_clk[i] = devm_clk_get(&pdev->dev, tmp);
+ if (IS_ERR(asrc_priv->asrck_clk[i])) {
+ dev_err(&pdev->dev, "failed to get %s clock\n", tmp);
+ return PTR_ERR(asrc_priv->asrck_clk[i]);
+ }
+ }
+
+ if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx35-asrc")) {
+ asrc_priv->channel_bits = 3;
+ clk_map[IN] = input_clk_map_imx35;
+ clk_map[OUT] = output_clk_map_imx35;
+ } else {
+ asrc_priv->channel_bits = 4;
+ clk_map[IN] = input_clk_map_imx53;
+ clk_map[OUT] = output_clk_map_imx53;
+ }
+
+ ret = fsl_asrc_init(asrc_priv);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
+ return -EINVAL;
+ }
+
+ asrc_priv->channel_avail = 10;
+
+ ret = of_property_read_u32(np, "fsl,asrc-rate",
+ &asrc_priv->asrc_rate);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output rate\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "fsl,asrc-width",
+ &asrc_priv->asrc_width);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get output width\n");
+ return -EINVAL;
+ }
+
+ if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) {
+ dev_warn(&pdev->dev, "unsupported width, switching to 24bit\n");
+ asrc_priv->asrc_width = 24;
+ }
+
+ platform_set_drvdata(pdev, asrc_priv);
+ pm_runtime_enable(&pdev->dev);
+ spin_lock_init(&asrc_priv->lock);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
+ &fsl_asrc_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC DAI\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_platform(&pdev->dev, &fsl_asrc_platform);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register ASoC platform\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "driver registered\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+static int fsl_asrc_runtime_resume(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ int i;
+
+ clk_prepare_enable(asrc_priv->mem_clk);
+ clk_prepare_enable(asrc_priv->ipg_clk);
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
+ clk_prepare_enable(asrc_priv->asrck_clk[i]);
+
+ return 0;
+}
+
+static int fsl_asrc_runtime_suspend(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ int i;
+
+ for (i = 0; i < ASRC_CLK_MAX_NUM; i++)
+ clk_disable_unprepare(asrc_priv->asrck_clk[i]);
+ clk_disable_unprepare(asrc_priv->ipg_clk);
+ clk_disable_unprepare(asrc_priv->mem_clk);
+
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_asrc_suspend(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+
+ regcache_cache_only(asrc_priv->regmap, true);
+ regcache_mark_dirty(asrc_priv->regmap);
+
+ return 0;
+}
+
+static int fsl_asrc_resume(struct device *dev)
+{
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ u32 asrctr;
+
+ /* Stop all pairs provisionally */
+ regmap_read(asrc_priv->regmap, REG_ASRCTR, &asrctr);
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_ALL_MASK, 0);
+
+ /* Restore all registers */
+ regcache_cache_only(asrc_priv->regmap, false);
+ regcache_sync(asrc_priv->regmap);
+
+ /* Restart enabled pairs */
+ regmap_update_bits(asrc_priv->regmap, REG_ASRCTR,
+ ASRCTR_ASRCEi_ALL_MASK, asrctr);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_asrc_pm = {
+ SET_RUNTIME_PM_OPS(fsl_asrc_runtime_suspend, fsl_asrc_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(fsl_asrc_suspend, fsl_asrc_resume)
+};
+
+static const struct of_device_id fsl_asrc_ids[] = {
+ { .compatible = "fsl,imx35-asrc", },
+ { .compatible = "fsl,imx53-asrc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_asrc_ids);
+
+static struct platform_driver fsl_asrc_driver = {
+ .probe = fsl_asrc_probe,
+ .driver = {
+ .name = "fsl-asrc",
+ .of_match_table = fsl_asrc_ids,
+ .pm = &fsl_asrc_pm,
+ },
+};
+module_platform_driver(fsl_asrc_driver);
+
+MODULE_DESCRIPTION("Freescale ASRC ASoC driver");
+MODULE_AUTHOR("Nicolin Chen <nicoleotsuka@gmail.com>");
+MODULE_ALIAS("platform:fsl-asrc");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/fsl/fsl_asrc.h b/sound/soc/fsl/fsl_asrc.h
new file mode 100644
index 0000000..a3f211f
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc.h
@@ -0,0 +1,461 @@
+/*
+ * fsl_asrc.h - Freescale ASRC ALSA SoC header file
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_ASRC_H
+#define _FSL_ASRC_H
+
+#define IN 0
+#define OUT 1
+
+#define ASRC_DMA_BUFFER_NUM 2
+#define ASRC_INPUTFIFO_THRESHOLD 32
+#define ASRC_OUTPUTFIFO_THRESHOLD 32
+#define ASRC_FIFO_THRESHOLD_MIN 0
+#define ASRC_FIFO_THRESHOLD_MAX 63
+#define ASRC_DMA_BUFFER_SIZE (1024 * 48 * 4)
+#define ASRC_MAX_BUFFER_SIZE (1024 * 48)
+#define ASRC_OUTPUT_LAST_SAMPLE 8
+
+#define IDEAL_RATIO_RATE 1000000
+
+#define REG_ASRCTR 0x00
+#define REG_ASRIER 0x04
+#define REG_ASRCNCR 0x0C
+#define REG_ASRCFG 0x10
+#define REG_ASRCSR 0x14
+
+#define REG_ASRCDR1 0x18
+#define REG_ASRCDR2 0x1C
+#define REG_ASRCDR(i) ((i < 2) ? REG_ASRCDR1 : REG_ASRCDR2)
+
+#define REG_ASRSTR 0x20
+#define REG_ASRRA 0x24
+#define REG_ASRRB 0x28
+#define REG_ASRRC 0x2C
+#define REG_ASRPM1 0x40
+#define REG_ASRPM2 0x44
+#define REG_ASRPM3 0x48
+#define REG_ASRPM4 0x4C
+#define REG_ASRPM5 0x50
+#define REG_ASRTFR1 0x54
+#define REG_ASRCCR 0x5C
+
+#define REG_ASRDIA 0x60
+#define REG_ASRDOA 0x64
+#define REG_ASRDIB 0x68
+#define REG_ASRDOB 0x6C
+#define REG_ASRDIC 0x70
+#define REG_ASRDOC 0x74
+#define REG_ASRDI(i) (REG_ASRDIA + (i << 3))
+#define REG_ASRDO(i) (REG_ASRDOA + (i << 3))
+#define REG_ASRDx(x, i) (x == IN ? REG_ASRDI(i) : REG_ASRDO(i))
+
+#define REG_ASRIDRHA 0x80
+#define REG_ASRIDRLA 0x84
+#define REG_ASRIDRHB 0x88
+#define REG_ASRIDRLB 0x8C
+#define REG_ASRIDRHC 0x90
+#define REG_ASRIDRLC 0x94
+#define REG_ASRIDRH(i) (REG_ASRIDRHA + (i << 3))
+#define REG_ASRIDRL(i) (REG_ASRIDRLA + (i << 3))
+
+#define REG_ASR76K 0x98
+#define REG_ASR56K 0x9C
+
+#define REG_ASRMCRA 0xA0
+#define REG_ASRFSTA 0xA4
+#define REG_ASRMCRB 0xA8
+#define REG_ASRFSTB 0xAC
+#define REG_ASRMCRC 0xB0
+#define REG_ASRFSTC 0xB4
+#define REG_ASRMCR(i) (REG_ASRMCRA + (i << 3))
+#define REG_ASRFST(i) (REG_ASRFSTA + (i << 3))
+
+#define REG_ASRMCR1A 0xC0
+#define REG_ASRMCR1B 0xC4
+#define REG_ASRMCR1C 0xC8
+#define REG_ASRMCR1(i) (REG_ASRMCR1A + (i << 2))
+
+
+/* REG0 0x00 REG_ASRCTR */
+#define ASRCTR_ATSi_SHIFT(i) (20 + i)
+#define ASRCTR_ATSi_MASK(i) (1 << ASRCTR_ATSi_SHIFT(i))
+#define ASRCTR_ATS(i) (1 << ASRCTR_ATSi_SHIFT(i))
+#define ASRCTR_USRi_SHIFT(i) (14 + (i << 1))
+#define ASRCTR_USRi_MASK(i) (1 << ASRCTR_USRi_SHIFT(i))
+#define ASRCTR_USR(i) (1 << ASRCTR_USRi_SHIFT(i))
+#define ASRCTR_IDRi_SHIFT(i) (13 + (i << 1))
+#define ASRCTR_IDRi_MASK(i) (1 << ASRCTR_IDRi_SHIFT(i))
+#define ASRCTR_IDR(i) (1 << ASRCTR_IDRi_SHIFT(i))
+#define ASRCTR_SRST_SHIFT 4
+#define ASRCTR_SRST_MASK (1 << ASRCTR_SRST_SHIFT)
+#define ASRCTR_SRST (1 << ASRCTR_SRST_SHIFT)
+#define ASRCTR_ASRCEi_SHIFT(i) (1 + i)
+#define ASRCTR_ASRCEi_MASK(i) (1 << ASRCTR_ASRCEi_SHIFT(i))
+#define ASRCTR_ASRCE(i) (1 << ASRCTR_ASRCEi_SHIFT(i))
+#define ASRCTR_ASRCEi_ALL_MASK (0x7 << ASRCTR_ASRCEi_SHIFT(0))
+#define ASRCTR_ASRCEN_SHIFT 0
+#define ASRCTR_ASRCEN_MASK (1 << ASRCTR_ASRCEN_SHIFT)
+#define ASRCTR_ASRCEN (1 << ASRCTR_ASRCEN_SHIFT)
+
+/* REG1 0x04 REG_ASRIER */
+#define ASRIER_AFPWE_SHIFT 7
+#define ASRIER_AFPWE_MASK (1 << ASRIER_AFPWE_SHIFT)
+#define ASRIER_AFPWE (1 << ASRIER_AFPWE_SHIFT)
+#define ASRIER_AOLIE_SHIFT 6
+#define ASRIER_AOLIE_MASK (1 << ASRIER_AOLIE_SHIFT)
+#define ASRIER_AOLIE (1 << ASRIER_AOLIE_SHIFT)
+#define ASRIER_ADOEi_SHIFT(i) (3 + i)
+#define ASRIER_ADOEi_MASK(i) (1 << ASRIER_ADOEi_SHIFT(i))
+#define ASRIER_ADOE(i) (1 << ASRIER_ADOEi_SHIFT(i))
+#define ASRIER_ADIEi_SHIFT(i) (0 + i)
+#define ASRIER_ADIEi_MASK(i) (1 << ASRIER_ADIEi_SHIFT(i))
+#define ASRIER_ADIE(i) (1 << ASRIER_ADIEi_SHIFT(i))
+
+/* REG2 0x0C REG_ASRCNCR */
+#define ASRCNCR_ANCi_SHIFT(i, b) (b * i)
+#define ASRCNCR_ANCi_MASK(i, b) (((1 << b) - 1) << ASRCNCR_ANCi_SHIFT(i, b))
+#define ASRCNCR_ANCi(i, v, b) ((v << ASRCNCR_ANCi_SHIFT(i, b)) & ASRCNCR_ANCi_MASK(i, b))
+
+/* REG3 0x10 REG_ASRCFG */
+#define ASRCFG_INIRQi_SHIFT(i) (21 + i)
+#define ASRCFG_INIRQi_MASK(i) (1 << ASRCFG_INIRQi_SHIFT(i))
+#define ASRCFG_INIRQi (1 << ASRCFG_INIRQi_SHIFT(i))
+#define ASRCFG_NDPRi_SHIFT(i) (18 + i)
+#define ASRCFG_NDPRi_MASK(i) (1 << ASRCFG_NDPRi_SHIFT(i))
+#define ASRCFG_NDPRi (1 << ASRCFG_NDPRi_SHIFT(i))
+#define ASRCFG_POSTMODi_SHIFT(i) (8 + (i << 2))
+#define ASRCFG_POSTMODi_WIDTH 2
+#define ASRCFG_POSTMODi_MASK(i) (((1 << ASRCFG_POSTMODi_WIDTH) - 1) << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMOD(i, v) ((v) << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_UP(i) (0 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_DCON(i) (1 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_POSTMODi_DOWN(i) (2 << ASRCFG_POSTMODi_SHIFT(i))
+#define ASRCFG_PREMODi_SHIFT(i) (6 + (i << 2))
+#define ASRCFG_PREMODi_WIDTH 2
+#define ASRCFG_PREMODi_MASK(i) (((1 << ASRCFG_PREMODi_WIDTH) - 1) << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMOD(i, v) ((v) << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_UP(i) (0 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_DCON(i) (1 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_DOWN(i) (2 << ASRCFG_PREMODi_SHIFT(i))
+#define ASRCFG_PREMODi_BYPASS(i) (3 << ASRCFG_PREMODi_SHIFT(i))
+
+/* REG4 0x14 REG_ASRCSR */
+#define ASRCSR_AxCSi_WIDTH 4
+#define ASRCSR_AxCSi_MASK ((1 << ASRCSR_AxCSi_WIDTH) - 1)
+#define ASRCSR_AOCSi_SHIFT(i) (12 + (i << 2))
+#define ASRCSR_AOCSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AOCSi_SHIFT(i))
+#define ASRCSR_AOCS(i, v) ((v) << ASRCSR_AOCSi_SHIFT(i))
+#define ASRCSR_AICSi_SHIFT(i) (i << 2)
+#define ASRCSR_AICSi_MASK(i) (((1 << ASRCSR_AxCSi_WIDTH) - 1) << ASRCSR_AICSi_SHIFT(i))
+#define ASRCSR_AICS(i, v) ((v) << ASRCSR_AICSi_SHIFT(i))
+
+/* REG5&6 0x18 & 0x1C REG_ASRCDR1 & ASRCDR2 */
+#define ASRCDRi_AxCPi_WIDTH 3
+#define ASRCDRi_AICPi_SHIFT(i) (0 + (i % 2) * 6)
+#define ASRCDRi_AICPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICPi_SHIFT(i))
+#define ASRCDRi_AICP(i, v) ((v) << ASRCDRi_AICPi_SHIFT(i))
+#define ASRCDRi_AICDi_SHIFT(i) (3 + (i % 2) * 6)
+#define ASRCDRi_AICDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AICDi_SHIFT(i))
+#define ASRCDRi_AICD(i, v) ((v) << ASRCDRi_AICDi_SHIFT(i))
+#define ASRCDRi_AOCPi_SHIFT(i) ((i < 2) ? 12 + i * 6 : 6)
+#define ASRCDRi_AOCPi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCPi_SHIFT(i))
+#define ASRCDRi_AOCP(i, v) ((v) << ASRCDRi_AOCPi_SHIFT(i))
+#define ASRCDRi_AOCDi_SHIFT(i) ((i < 2) ? 15 + i * 6 : 9)
+#define ASRCDRi_AOCDi_MASK(i) (((1 << ASRCDRi_AxCPi_WIDTH) - 1) << ASRCDRi_AOCDi_SHIFT(i))
+#define ASRCDRi_AOCD(i, v) ((v) << ASRCDRi_AOCDi_SHIFT(i))
+
+/* REG7 0x20 REG_ASRSTR */
+#define ASRSTR_DSLCNT_SHIFT 21
+#define ASRSTR_DSLCNT_MASK (1 << ASRSTR_DSLCNT_SHIFT)
+#define ASRSTR_DSLCNT (1 << ASRSTR_DSLCNT_SHIFT)
+#define ASRSTR_ATQOL_SHIFT 20
+#define ASRSTR_ATQOL_MASK (1 << ASRSTR_ATQOL_SHIFT)
+#define ASRSTR_ATQOL (1 << ASRSTR_ATQOL_SHIFT)
+#define ASRSTR_AOOLi_SHIFT(i) (17 + i)
+#define ASRSTR_AOOLi_MASK(i) (1 << ASRSTR_AOOLi_SHIFT(i))
+#define ASRSTR_AOOL(i) (1 << ASRSTR_AOOLi_SHIFT(i))
+#define ASRSTR_AIOLi_SHIFT(i) (14 + i)
+#define ASRSTR_AIOLi_MASK(i) (1 << ASRSTR_AIOLi_SHIFT(i))
+#define ASRSTR_AIOL(i) (1 << ASRSTR_AIOLi_SHIFT(i))
+#define ASRSTR_AODOi_SHIFT(i) (11 + i)
+#define ASRSTR_AODOi_MASK(i) (1 << ASRSTR_AODOi_SHIFT(i))
+#define ASRSTR_AODO(i) (1 << ASRSTR_AODOi_SHIFT(i))
+#define ASRSTR_AIDUi_SHIFT(i) (8 + i)
+#define ASRSTR_AIDUi_MASK(i) (1 << ASRSTR_AIDUi_SHIFT(i))
+#define ASRSTR_AIDU(i) (1 << ASRSTR_AIDUi_SHIFT(i))
+#define ASRSTR_FPWT_SHIFT 7
+#define ASRSTR_FPWT_MASK (1 << ASRSTR_FPWT_SHIFT)
+#define ASRSTR_FPWT (1 << ASRSTR_FPWT_SHIFT)
+#define ASRSTR_AOLE_SHIFT 6
+#define ASRSTR_AOLE_MASK (1 << ASRSTR_AOLE_SHIFT)
+#define ASRSTR_AOLE (1 << ASRSTR_AOLE_SHIFT)
+#define ASRSTR_AODEi_SHIFT(i) (3 + i)
+#define ASRSTR_AODFi_MASK(i) (1 << ASRSTR_AODEi_SHIFT(i))
+#define ASRSTR_AODF(i) (1 << ASRSTR_AODEi_SHIFT(i))
+#define ASRSTR_AIDEi_SHIFT(i) (0 + i)
+#define ASRSTR_AIDEi_MASK(i) (1 << ASRSTR_AIDEi_SHIFT(i))
+#define ASRSTR_AIDE(i) (1 << ASRSTR_AIDEi_SHIFT(i))
+
+/* REG10 0x54 REG_ASRTFR1 */
+#define ASRTFR1_TF_BASE_WIDTH 7
+#define ASRTFR1_TF_BASE_SHIFT 6
+#define ASRTFR1_TF_BASE_MASK (((1 << ASRTFR1_TF_BASE_WIDTH) - 1) << ASRTFR1_TF_BASE_SHIFT)
+#define ASRTFR1_TF_BASE(i) ((i) << ASRTFR1_TF_BASE_SHIFT)
+
+/*
+ * REG22 0xA0 REG_ASRMCRA
+ * REG24 0xA8 REG_ASRMCRB
+ * REG26 0xB0 REG_ASRMCRC
+ */
+#define ASRMCRi_ZEROBUFi_SHIFT 23
+#define ASRMCRi_ZEROBUFi_MASK (1 << ASRMCRi_ZEROBUFi_SHIFT)
+#define ASRMCRi_ZEROBUFi (1 << ASRMCRi_ZEROBUFi_SHIFT)
+#define ASRMCRi_EXTTHRSHi_SHIFT 22
+#define ASRMCRi_EXTTHRSHi_MASK (1 << ASRMCRi_EXTTHRSHi_SHIFT)
+#define ASRMCRi_EXTTHRSHi (1 << ASRMCRi_EXTTHRSHi_SHIFT)
+#define ASRMCRi_BUFSTALLi_SHIFT 21
+#define ASRMCRi_BUFSTALLi_MASK (1 << ASRMCRi_BUFSTALLi_SHIFT)
+#define ASRMCRi_BUFSTALLi (1 << ASRMCRi_BUFSTALLi_SHIFT)
+#define ASRMCRi_BYPASSPOLYi_SHIFT 20
+#define ASRMCRi_BYPASSPOLYi_MASK (1 << ASRMCRi_BYPASSPOLYi_SHIFT)
+#define ASRMCRi_BYPASSPOLYi (1 << ASRMCRi_BYPASSPOLYi_SHIFT)
+#define ASRMCRi_OUTFIFO_THRESHOLD_WIDTH 6
+#define ASRMCRi_OUTFIFO_THRESHOLD_SHIFT 12
+#define ASRMCRi_OUTFIFO_THRESHOLD_MASK (((1 << ASRMCRi_OUTFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT)
+#define ASRMCRi_OUTFIFO_THRESHOLD(v) (((v) << ASRMCRi_OUTFIFO_THRESHOLD_SHIFT) & ASRMCRi_OUTFIFO_THRESHOLD_MASK)
+#define ASRMCRi_RSYNIFi_SHIFT 11
+#define ASRMCRi_RSYNIFi_MASK (1 << ASRMCRi_RSYNIFi_SHIFT)
+#define ASRMCRi_RSYNIFi (1 << ASRMCRi_RSYNIFi_SHIFT)
+#define ASRMCRi_RSYNOFi_SHIFT 10
+#define ASRMCRi_RSYNOFi_MASK (1 << ASRMCRi_RSYNOFi_SHIFT)
+#define ASRMCRi_RSYNOFi (1 << ASRMCRi_RSYNOFi_SHIFT)
+#define ASRMCRi_INFIFO_THRESHOLD_WIDTH 6
+#define ASRMCRi_INFIFO_THRESHOLD_SHIFT 0
+#define ASRMCRi_INFIFO_THRESHOLD_MASK (((1 << ASRMCRi_INFIFO_THRESHOLD_WIDTH) - 1) << ASRMCRi_INFIFO_THRESHOLD_SHIFT)
+#define ASRMCRi_INFIFO_THRESHOLD(v) (((v) << ASRMCRi_INFIFO_THRESHOLD_SHIFT) & ASRMCRi_INFIFO_THRESHOLD_MASK)
+
+/*
+ * REG23 0xA4 REG_ASRFSTA
+ * REG25 0xAC REG_ASRFSTB
+ * REG27 0xB4 REG_ASRFSTC
+ */
+#define ASRFSTi_OAFi_SHIFT 23
+#define ASRFSTi_OAFi_MASK (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_OAFi (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_OUTPUT_FIFO_WIDTH 7
+#define ASRFSTi_OUTPUT_FIFO_SHIFT 12
+#define ASRFSTi_OUTPUT_FIFO_MASK (((1 << ASRFSTi_OUTPUT_FIFO_WIDTH) - 1) << ASRFSTi_OUTPUT_FIFO_SHIFT)
+#define ASRFSTi_IAEi_SHIFT 11
+#define ASRFSTi_IAEi_MASK (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_IAEi (1 << ASRFSTi_OAFi_SHIFT)
+#define ASRFSTi_INPUT_FIFO_WIDTH 7
+#define ASRFSTi_INPUT_FIFO_SHIFT 0
+#define ASRFSTi_INPUT_FIFO_MASK ((1 << ASRFSTi_INPUT_FIFO_WIDTH) - 1)
+
+/* REG28 0xC0 & 0xC4 & 0xC8 REG_ASRMCR1i */
+#define ASRMCR1i_IWD_WIDTH 3
+#define ASRMCR1i_IWD_SHIFT 9
+#define ASRMCR1i_IWD_MASK (((1 << ASRMCR1i_IWD_WIDTH) - 1) << ASRMCR1i_IWD_SHIFT)
+#define ASRMCR1i_IWD(v) ((v) << ASRMCR1i_IWD_SHIFT)
+#define ASRMCR1i_IMSB_SHIFT 8
+#define ASRMCR1i_IMSB_MASK (1 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_IMSB_MSB (1 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_IMSB_LSB (0 << ASRMCR1i_IMSB_SHIFT)
+#define ASRMCR1i_OMSB_SHIFT 2
+#define ASRMCR1i_OMSB_MASK (1 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OMSB_MSB (1 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OMSB_LSB (0 << ASRMCR1i_OMSB_SHIFT)
+#define ASRMCR1i_OSGN_SHIFT 1
+#define ASRMCR1i_OSGN_MASK (1 << ASRMCR1i_OSGN_SHIFT)
+#define ASRMCR1i_OSGN (1 << ASRMCR1i_OSGN_SHIFT)
+#define ASRMCR1i_OW16_SHIFT 0
+#define ASRMCR1i_OW16_MASK (1 << ASRMCR1i_OW16_SHIFT)
+#define ASRMCR1i_OW16(v) ((v) << ASRMCR1i_OW16_SHIFT)
+
+
+enum asrc_pair_index {
+ ASRC_INVALID_PAIR = -1,
+ ASRC_PAIR_A = 0,
+ ASRC_PAIR_B = 1,
+ ASRC_PAIR_C = 2,
+};
+
+#define ASRC_PAIR_MAX_NUM (ASRC_PAIR_C + 1)
+
+enum asrc_inclk {
+ INCLK_NONE = 0x03,
+ INCLK_ESAI_RX = 0x00,
+ INCLK_SSI1_RX = 0x01,
+ INCLK_SSI2_RX = 0x02,
+ INCLK_SSI3_RX = 0x07,
+ INCLK_SPDIF_RX = 0x04,
+ INCLK_MLB_CLK = 0x05,
+ INCLK_PAD = 0x06,
+ INCLK_ESAI_TX = 0x08,
+ INCLK_SSI1_TX = 0x09,
+ INCLK_SSI2_TX = 0x0a,
+ INCLK_SSI3_TX = 0x0b,
+ INCLK_SPDIF_TX = 0x0c,
+ INCLK_ASRCK1_CLK = 0x0f,
+};
+
+enum asrc_outclk {
+ OUTCLK_NONE = 0x03,
+ OUTCLK_ESAI_TX = 0x00,
+ OUTCLK_SSI1_TX = 0x01,
+ OUTCLK_SSI2_TX = 0x02,
+ OUTCLK_SSI3_TX = 0x07,
+ OUTCLK_SPDIF_TX = 0x04,
+ OUTCLK_MLB_CLK = 0x05,
+ OUTCLK_PAD = 0x06,
+ OUTCLK_ESAI_RX = 0x08,
+ OUTCLK_SSI1_RX = 0x09,
+ OUTCLK_SSI2_RX = 0x0a,
+ OUTCLK_SSI3_RX = 0x0b,
+ OUTCLK_SPDIF_RX = 0x0c,
+ OUTCLK_ASRCK1_CLK = 0x0f,
+};
+
+#define ASRC_CLK_MAX_NUM 16
+
+enum asrc_word_width {
+ ASRC_WIDTH_24_BIT = 0,
+ ASRC_WIDTH_16_BIT = 1,
+ ASRC_WIDTH_8_BIT = 2,
+};
+
+struct asrc_config {
+ enum asrc_pair_index pair;
+ unsigned int channel_num;
+ unsigned int buffer_num;
+ unsigned int dma_buffer_size;
+ unsigned int input_sample_rate;
+ unsigned int output_sample_rate;
+ enum asrc_word_width input_word_width;
+ enum asrc_word_width output_word_width;
+ enum asrc_inclk inclk;
+ enum asrc_outclk outclk;
+};
+
+struct asrc_req {
+ unsigned int chn_num;
+ enum asrc_pair_index index;
+};
+
+struct asrc_querybuf {
+ unsigned int buffer_index;
+ unsigned int input_length;
+ unsigned int output_length;
+ unsigned long input_offset;
+ unsigned long output_offset;
+};
+
+struct asrc_convert_buffer {
+ void *input_buffer_vaddr;
+ void *output_buffer_vaddr;
+ unsigned int input_buffer_length;
+ unsigned int output_buffer_length;
+};
+
+struct asrc_status_flags {
+ enum asrc_pair_index index;
+ unsigned int overload_error;
+};
+
+enum asrc_error_status {
+ ASRC_TASK_Q_OVERLOAD = 0x01,
+ ASRC_OUTPUT_TASK_OVERLOAD = 0x02,
+ ASRC_INPUT_TASK_OVERLOAD = 0x04,
+ ASRC_OUTPUT_BUFFER_OVERFLOW = 0x08,
+ ASRC_INPUT_BUFFER_UNDERRUN = 0x10,
+};
+
+struct dma_block {
+ dma_addr_t dma_paddr;
+ void *dma_vaddr;
+ unsigned int length;
+};
+
+/**
+ * fsl_asrc_pair: ASRC Pair private data
+ *
+ * @asrc_priv: pointer to its parent module
+ * @config: configuration profile
+ * @error: error record
+ * @index: pair index (ASRC_PAIR_A, ASRC_PAIR_B, ASRC_PAIR_C)
+ * @channels: occupied channel number
+ * @desc: input and output dma descriptors
+ * @dma_chan: inputer and output DMA channels
+ * @dma_data: private dma data
+ * @pos: hardware pointer position
+ * @private: pair private area
+ */
+struct fsl_asrc_pair {
+ struct fsl_asrc *asrc_priv;
+ struct asrc_config *config;
+ unsigned int error;
+
+ enum asrc_pair_index index;
+ unsigned int channels;
+
+ struct dma_async_tx_descriptor *desc[2];
+ struct dma_chan *dma_chan[2];
+ struct imx_dma_data dma_data;
+ unsigned int pos;
+
+ void *private;
+};
+
+/**
+ * fsl_asrc_pair: ASRC private data
+ *
+ * @dma_params_rx: DMA parameters for receive channel
+ * @dma_params_tx: DMA parameters for transmit channel
+ * @pdev: platform device pointer
+ * @regmap: regmap handler
+ * @paddr: physical address to the base address of registers
+ * @mem_clk: clock source to access register
+ * @ipg_clk: clock source to drive peripheral
+ * @asrck_clk: clock sources to driver ASRC internal logic
+ * @lock: spin lock for resource protection
+ * @pair: pair pointers
+ * @channel_bits: width of ASRCNCR register for each pair
+ * @channel_avail: non-occupied channel numbers
+ * @asrc_rate: default sample rate for ASoC Back-Ends
+ * @asrc_width: default sample width for ASoC Back-Ends
+ * @name: driver name
+ */
+struct fsl_asrc {
+ struct snd_dmaengine_dai_dma_data dma_params_rx;
+ struct snd_dmaengine_dai_dma_data dma_params_tx;
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ unsigned long paddr;
+ struct clk *mem_clk;
+ struct clk *ipg_clk;
+ struct clk *asrck_clk[ASRC_CLK_MAX_NUM];
+ spinlock_t lock;
+
+ struct fsl_asrc_pair *pair[ASRC_PAIR_MAX_NUM];
+ unsigned int channel_bits;
+ unsigned int channel_avail;
+
+ int asrc_rate;
+ int asrc_width;
+
+ char name[32];
+};
+
+extern struct snd_soc_platform_driver fsl_asrc_platform;
+struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair, bool dir);
+#endif /* _FSL_ASRC_H */
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
new file mode 100644
index 0000000..ffc000b
--- /dev/null
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -0,0 +1,391 @@
+/*
+ * Freescale ASRC ALSA SoC Platform (DMA) driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <nicoleotsuka@gmail.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_data/dma-imx.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "fsl_asrc.h"
+
+#define FSL_ASRC_DMABUF_SIZE (256 * 1024)
+
+static struct snd_pcm_hardware snd_imx_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME,
+ .buffer_bytes_max = FSL_ASRC_DMABUF_SIZE,
+ .period_bytes_min = 128,
+ .period_bytes_max = 65535, /* Limited by SDMA engine */
+ .periods_min = 2,
+ .periods_max = 255,
+ .fifo_size = 0,
+};
+
+static bool filter(struct dma_chan *chan, void *param)
+{
+ if (!imx_dma_is_general_purpose(chan))
+ return false;
+
+ chan->private = param;
+
+ return true;
+}
+
+static void fsl_asrc_dma_complete(void *arg)
+{
+ struct snd_pcm_substream *substream = arg;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ pair->pos += snd_pcm_lib_period_bytes(substream);
+ if (pair->pos >= snd_pcm_lib_buffer_bytes(substream))
+ pair->pos = 0;
+
+ snd_pcm_period_elapsed(substream);
+}
+
+static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
+{
+ u8 dir = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? OUT : IN;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct device *dev = rtd->platform->dev;
+ unsigned long flags = DMA_CTRL_ACK;
+
+ /* Prepare and submit Front-End DMA channel */
+ if (!substream->runtime->no_period_wakeup)
+ flags |= DMA_PREP_INTERRUPT;
+
+ pair->pos = 0;
+ pair->desc[!dir] = dmaengine_prep_dma_cyclic(
+ pair->dma_chan[!dir], runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags);
+ if (!pair->desc[!dir]) {
+ dev_err(dev, "failed to prepare slave DMA for Front-End\n");
+ return -ENOMEM;
+ }
+
+ pair->desc[!dir]->callback = fsl_asrc_dma_complete;
+ pair->desc[!dir]->callback_param = substream;
+
+ dmaengine_submit(pair->desc[!dir]);
+
+ /* Prepare and submit Back-End DMA channel */
+ pair->desc[dir] = dmaengine_prep_dma_cyclic(
+ pair->dma_chan[dir], 0xffff, 64, 64, DMA_DEV_TO_DEV, 0);
+ if (!pair->desc[dir]) {
+ dev_err(dev, "failed to prepare slave DMA for Back-End\n");
+ return -ENOMEM;
+ }
+
+ dmaengine_submit(pair->desc[dir]);
+
+ return 0;
+}
+
+static int fsl_asrc_dma_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ ret = fsl_asrc_dma_prepare_and_submit(substream);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(pair->dma_chan[IN]);
+ dma_async_issue_pending(pair->dma_chan[OUT]);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ dmaengine_terminate_all(pair->dma_chan[OUT]);
+ dmaengine_terminate_all(pair->dma_chan[IN]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ enum dma_slave_buswidth buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ struct snd_dmaengine_dai_dma_data *dma_params_fe = NULL;
+ struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc *asrc_priv = pair->asrc_priv;
+ struct dma_slave_config config_fe, config_be;
+ enum asrc_pair_index index = pair->index;
+ struct device *dev = rtd->platform->dev;
+ int stream = substream->stream;
+ struct imx_dma_data *tmp_data;
+ struct snd_soc_dpcm *dpcm;
+ struct dma_chan *tmp_chan;
+ struct device *dev_be;
+ u8 dir = tx ? OUT : IN;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* Fetch the Back-End dma_data from DPCM */
+ list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) {
+ struct snd_soc_pcm_runtime *be = dpcm->be;
+ struct snd_pcm_substream *substream_be;
+ struct snd_soc_dai *dai = be->cpu_dai;
+
+ if (dpcm->fe != rtd)
+ continue;
+
+ substream_be = snd_soc_dpcm_get_substream(be, stream);
+ dma_params_be = snd_soc_dai_get_dma_data(dai, substream_be);
+ dev_be = dai->dev;
+ break;
+ }
+
+ if (!dma_params_be) {
+ dev_err(dev, "failed to get the substream of Back-End\n");
+ return -EINVAL;
+ }
+
+ /* Override dma_data of the Front-End and config its dmaengine */
+ dma_params_fe = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+ dma_params_fe->addr = asrc_priv->paddr + REG_ASRDx(!dir, index);
+ dma_params_fe->maxburst = dma_params_be->maxburst;
+
+ pair->dma_chan[!dir] = fsl_asrc_get_dma_channel(pair, !dir);
+ if (!pair->dma_chan[!dir]) {
+ dev_err(dev, "failed to request DMA channel\n");
+ return -EINVAL;
+ }
+
+ memset(&config_fe, 0, sizeof(config_fe));
+ ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe);
+ if (ret) {
+ dev_err(dev, "failed to prepare DMA config for Front-End\n");
+ return ret;
+ }
+
+ ret = dmaengine_slave_config(pair->dma_chan[!dir], &config_fe);
+ if (ret) {
+ dev_err(dev, "failed to config DMA channel for Front-End\n");
+ return ret;
+ }
+
+ /* Request and config DMA channel for Back-End */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get DMA request of Back-End */
+ tmp_chan = dma_request_slave_channel(dev_be, tx ? "tx" : "rx");
+ tmp_data = tmp_chan->private;
+ pair->dma_data.dma_request = tmp_data->dma_request;
+ dma_release_channel(tmp_chan);
+
+ /* Get DMA request of Front-End */
+ tmp_chan = fsl_asrc_get_dma_channel(pair, dir);
+ tmp_data = tmp_chan->private;
+ pair->dma_data.dma_request2 = tmp_data->dma_request;
+ pair->dma_data.peripheral_type = tmp_data->peripheral_type;
+ pair->dma_data.priority = tmp_data->priority;
+ dma_release_channel(tmp_chan);
+
+ pair->dma_chan[dir] = dma_request_channel(mask, filter, &pair->dma_data);
+ if (!pair->dma_chan[dir]) {
+ dev_err(dev, "failed to request DMA channel for Back-End\n");
+ return -EINVAL;
+ }
+
+ if (asrc_priv->asrc_width == 16)
+ buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ else
+ buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ config_be.direction = DMA_DEV_TO_DEV;
+ config_be.src_addr_width = buswidth;
+ config_be.src_maxburst = dma_params_be->maxburst;
+ config_be.dst_addr_width = buswidth;
+ config_be.dst_maxburst = dma_params_be->maxburst;
+
+ if (tx) {
+ config_be.src_addr = asrc_priv->paddr + REG_ASRDO(index);
+ config_be.dst_addr = dma_params_be->addr;
+ } else {
+ config_be.dst_addr = asrc_priv->paddr + REG_ASRDI(index);
+ config_be.src_addr = dma_params_be->addr;
+ }
+
+ ret = dmaengine_slave_config(pair->dma_chan[dir], &config_be);
+ if (ret) {
+ dev_err(dev, "failed to config DMA channel for Back-End\n");
+ return ret;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+ return 0;
+}
+
+static int fsl_asrc_dma_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+
+ if (pair->dma_chan[IN])
+ dma_release_channel(pair->dma_chan[IN]);
+
+ if (pair->dma_chan[OUT])
+ dma_release_channel(pair->dma_chan[OUT]);
+
+ pair->dma_chan[IN] = NULL;
+ pair->dma_chan[OUT] = NULL;
+
+ return 0;
+}
+
+static int fsl_asrc_dma_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct device *dev = rtd->platform->dev;
+ struct fsl_asrc *asrc_priv = dev_get_drvdata(dev);
+ struct fsl_asrc_pair *pair;
+
+ pair = kzalloc(sizeof(struct fsl_asrc_pair), GFP_KERNEL);
+ if (!pair) {
+ dev_err(dev, "failed to allocate pair\n");
+ return -ENOMEM;
+ }
+
+ pair->asrc_priv = asrc_priv;
+
+ runtime->private_data = pair;
+
+ snd_pcm_hw_constraint_integer(substream->runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+ snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware);
+
+ return 0;
+}
+
+static int fsl_asrc_dma_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+ struct fsl_asrc *asrc_priv;
+
+ if (!pair)
+ return 0;
+
+ asrc_priv = pair->asrc_priv;
+
+ if (asrc_priv->pair[pair->index] == pair)
+ asrc_priv->pair[pair->index] = NULL;
+
+ kfree(pair);
+
+ return 0;
+}
+
+static snd_pcm_uframes_t fsl_asrc_dma_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct fsl_asrc_pair *pair = runtime->private_data;
+
+ return bytes_to_frames(substream->runtime, pair->pos);
+}
+
+static struct snd_pcm_ops fsl_asrc_dma_pcm_ops = {
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = fsl_asrc_dma_hw_params,
+ .hw_free = fsl_asrc_dma_hw_free,
+ .trigger = fsl_asrc_dma_trigger,
+ .open = fsl_asrc_dma_startup,
+ .close = fsl_asrc_dma_shutdown,
+ .pointer = fsl_asrc_dma_pcm_pointer,
+};
+
+static int fsl_asrc_dma_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret, i;
+
+ ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(card->dev, "failed to set DMA mask\n");
+ return ret;
+ }
+
+ for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+ substream = pcm->streams[i].substream;
+ if (!substream)
+ continue;
+
+ ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, pcm->card->dev,
+ FSL_ASRC_DMABUF_SIZE, &substream->dma_buffer);
+ if (ret) {
+ dev_err(card->dev, "failed to allocate DMA buffer\n");
+ goto err;
+ }
+ }
+
+ return 0;
+
+err:
+ if (--i == 0 && pcm->streams[i].substream)
+ snd_dma_free_pages(&pcm->streams[i].substream->dma_buffer);
+
+ return ret;
+}
+
+static void fsl_asrc_dma_pcm_free(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ int i;
+
+ for (i = SNDRV_PCM_STREAM_PLAYBACK; i <= SNDRV_PCM_STREAM_LAST; i++) {
+ substream = pcm->streams[i].substream;
+ if (!substream)
+ continue;
+
+ snd_dma_free_pages(&substream->dma_buffer);
+ substream->dma_buffer.area = NULL;
+ substream->dma_buffer.addr = 0;
+ }
+}
+
+struct snd_soc_platform_driver fsl_asrc_platform = {
+ .ops = &fsl_asrc_dma_pcm_ops,
+ .pcm_new = fsl_asrc_dma_pcm_new,
+ .pcm_free = fsl_asrc_dma_pcm_free,
+};
+EXPORT_SYMBOL_GPL(fsl_asrc_platform);
diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c
index 6bb0ea5..a609aaf 100644
--- a/sound/soc/fsl/fsl_dma.c
+++ b/sound/soc/fsl/fsl_dma.c
@@ -923,8 +923,8 @@ static int fsl_soc_dma_probe(struct platform_device *pdev)
dma->dai.pcm_free = fsl_dma_free_dma_buffers;
/* Store the SSI-specific information that we need */
- dma->ssi_stx_phys = res.start + offsetof(struct ccsr_ssi, stx0);
- dma->ssi_srx_phys = res.start + offsetof(struct ccsr_ssi, srx0);
+ dma->ssi_stx_phys = res.start + CCSR_SSI_STX0;
+ dma->ssi_srx_phys = res.start + CCSR_SSI_SRX0;
iprop = of_get_property(ssi_np, "fsl,fifo-depth", NULL);
if (iprop)
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index d719caf..72d154e 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -624,12 +624,14 @@ static int fsl_esai_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver fsl_esai_dai = {
.probe = fsl_esai_dai_probe,
.playback = {
+ .stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 12,
.rates = FSL_ESAI_RATES,
.formats = FSL_ESAI_FORMATS,
},
.capture = {
+ .stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 8,
.rates = FSL_ESAI_RATES,
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index c5a0e8a..faa0497 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -106,7 +106,7 @@ irq_rx:
xcsr &= ~FSL_SAI_CSR_xF_MASK;
if (flags)
- regmap_write(sai->regmap, FSL_SAI_TCSR, flags | xcsr);
+ regmap_write(sai->regmap, FSL_SAI_RCSR, flags | xcsr);
out:
if (irq_none)
@@ -327,7 +327,7 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- u32 tcsr, rcsr;
+ u32 xcsr, count = 100;
/*
* The transmitter bit clock and frame sync are to be
@@ -338,9 +338,6 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
FSL_SAI_CR2_SYNC);
- regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
- regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
-
/*
* It is recommended that the transmitter is the last enabled
* and the first disabled.
@@ -349,17 +346,16 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if (!(tcsr & FSL_SAI_CSR_FRDE || rcsr & FSL_SAI_CSR_FRDE)) {
- regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
- FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
- regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
- FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
- }
+ regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
+ FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
+
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+ FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE);
regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS);
- regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx),
- FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
@@ -370,11 +366,24 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
FSL_SAI_CSR_xIE_MASK, 0);
/* Check if the opposite FRDE is also disabled */
- if (!(tx ? rcsr & FSL_SAI_CSR_FRDE : tcsr & FSL_SAI_CSR_FRDE)) {
+ regmap_read(sai->regmap, FSL_SAI_xCSR(!tx), &xcsr);
+ if (!(xcsr & FSL_SAI_CSR_FRDE)) {
+ /* Disable both directions and reset their FIFOs */
regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
FSL_SAI_CSR_TERE, 0);
regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
FSL_SAI_CSR_TERE, 0);
+
+ /* TERE will remain set till the end of current frame */
+ do {
+ udelay(10);
+ regmap_read(sai->regmap, FSL_SAI_xCSR(tx), &xcsr);
+ } while (--count && xcsr & FSL_SAI_CSR_TERE);
+
+ regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
+ FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
+ regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
+ FSL_SAI_CSR_FR, FSL_SAI_CSR_FR);
}
break;
default:
@@ -446,12 +455,14 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
static struct snd_soc_dai_driver fsl_sai_dai = {
.probe = fsl_sai_dai_probe,
.playback = {
+ .stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = FSL_SAI_FORMATS,
},
.capture = {
+ .stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index b912d45..70acfe4 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -32,10 +32,13 @@
#define FSL_SPDIF_TXFIFO_WML 0x8
#define FSL_SPDIF_RXFIFO_WML 0x8
-#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
-#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
- INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
- INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL |\
+ INT_URX_OV | INT_QRX_FUL | INT_QRX_OV |\
+ INT_UQ_SYNC | INT_UQ_ERR | INT_RXFIFO_RESYNC |\
+ INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+#define SIE_INTR_FOR(tx) (tx ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE)
/* Index list for the values that has if (DPLL Locked) condition */
static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
@@ -96,7 +99,7 @@ struct fsl_spdif_priv {
struct platform_device *pdev;
struct regmap *regmap;
bool dpll_locked;
- u16 txrate[SPDIF_TXRATE_MAX];
+ u32 txrate[SPDIF_TXRATE_MAX];
u8 txclk_df[SPDIF_TXRATE_MAX];
u8 sysclk_df[SPDIF_TXRATE_MAX];
u8 txclk_src[SPDIF_TXRATE_MAX];
@@ -137,10 +140,9 @@ static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
- if (!spdif_priv->dpll_locked) {
- /* DPLL unlocked seems no audio stream */
+ /* Clear illegal symbol if DPLL unlocked since no audio stream */
+ if (!spdif_priv->dpll_locked)
regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
- }
}
/* U/Q Channel receive register full */
@@ -335,8 +337,8 @@ static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
u32 ch_status;
ch_status = (bitrev8(ctrl->ch_status[0]) << 16) |
- (bitrev8(ctrl->ch_status[1]) << 8) |
- bitrev8(ctrl->ch_status[2]);
+ (bitrev8(ctrl->ch_status[1]) << 8) |
+ bitrev8(ctrl->ch_status[2]);
regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
@@ -390,6 +392,14 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
rate = SPDIF_TXRATE_48000;
csfs = IEC958_AES3_CON_FS_48000;
break;
+ case 96000:
+ rate = SPDIF_TXRATE_96000;
+ csfs = IEC958_AES3_CON_FS_96000;
+ break;
+ case 192000:
+ rate = SPDIF_TXRATE_192000;
+ csfs = IEC958_AES3_CON_FS_192000;
+ break;
default:
dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate);
return -EINVAL;
@@ -433,13 +443,12 @@ clk_set_bypass:
spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
/* select clock source and divisor */
- stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DF(txclk_df);
- mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DF_MASK;
+ stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) |
+ STC_TXCLK_DF(txclk_df) | STC_SYSCLK_DF(sysclk_df);
+ mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK |
+ STC_TXCLK_DF_MASK | STC_SYSCLK_DF_MASK;
regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
- regmap_update_bits(regmap, REG_SPDIF_STC,
- STC_SYSCLK_DF_MASK, STC_SYSCLK_DF(sysclk_df));
-
dev_dbg(&pdev->dev, "set sample rate to %dHz for %dHz playback\n",
spdif_priv->txrate[rate], sample_rate);
@@ -553,7 +562,7 @@ static int fsl_spdif_hw_params(struct snd_pcm_substream *substream,
return ret;
}
spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
- IEC958_AES3_CON_CLOCK_1000PPM);
+ IEC958_AES3_CON_CLOCK_1000PPM);
spdif_write_channel_status(spdif_priv);
} else {
/* Setup rx clock source */
@@ -569,9 +578,9 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
struct regmap *regmap = spdif_priv->regmap;
- int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
- u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+ bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ u32 intr = SIE_INTR_FOR(tx);
+ u32 dmaen = SCR_DMA_xX_EN(tx);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
@@ -662,9 +671,8 @@ static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
u32 cstatus, val;
regmap_read(regmap, REG_SPDIF_SIS, &val);
- if (!(val & INT_CNEW)) {
+ if (!(val & INT_CNEW))
return -EAGAIN;
- }
regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
@@ -693,15 +701,14 @@ static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
unsigned long flags;
- int ret = 0;
+ int ret = -EAGAIN;
spin_lock_irqsave(&ctrl->ctl_lock, flags);
if (ctrl->ready_buf) {
int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
memcpy(&ucontrol->value.iec958.subcode[0],
&ctrl->subcode[idx], SPDIF_UBITS_SIZE);
- } else {
- ret = -EAGAIN;
+ ret = 0;
}
spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
@@ -726,15 +733,14 @@ static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
unsigned long flags;
- int ret = 0;
+ int ret = -EAGAIN;
spin_lock_irqsave(&ctrl->ctl_lock, flags);
if (ctrl->ready_buf) {
int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
memcpy(&ucontrol->value.bytes.data[0],
&ctrl->qsub[idx], SPDIF_QSUB_SIZE);
- } else {
- ret = -EAGAIN;
+ ret = 0;
}
spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
@@ -762,7 +768,7 @@ static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
struct regmap *regmap = spdif_priv->regmap;
u32 val;
- val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+ regmap_read(regmap, REG_SPDIF_SIS, &val);
ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
@@ -799,10 +805,10 @@ static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
- if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
- /* Get bus clock from system */
+
+ /* Get bus clock from system */
+ if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED))
busclk_freq = clk_get_rate(spdif_priv->sysclk);
- }
/* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
tmpval64 = (u64) busclk_freq * freqmeas;
@@ -826,12 +832,12 @@ static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
{
struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
- int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+ int rate = 0;
if (spdif_priv->dpll_locked)
- ucontrol->value.integer.value[0] = rate;
- else
- ucontrol->value.integer.value[0] = 0;
+ rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+ ucontrol->value.integer.value[0] = rate;
return 0;
}
@@ -969,12 +975,14 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver fsl_spdif_dai = {
.probe = &fsl_spdif_dai_probe,
.playback = {
+ .stream_name = "CPU-Playback",
.channels_min = 2,
.channels_max = 2,
.rates = FSL_SPDIF_RATES_PLAYBACK,
.formats = FSL_SPDIF_FORMATS_PLAYBACK,
},
.capture = {
+ .stream_name = "CPU-Capture",
.channels_min = 2,
.channels_max = 2,
.rates = FSL_SPDIF_RATES_CAPTURE,
@@ -1046,7 +1054,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
struct clk *clk, u64 savesub,
enum spdif_txrate index, bool round)
{
- const u32 rate[] = { 32000, 44100, 48000 };
+ const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
bool is_sysclk = clk == spdif_priv->sysclk;
u64 rate_ideal, rate_actual, sub;
u32 sysclk_dfmin, sysclk_dfmax;
@@ -1076,7 +1084,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
goto out;
} else if (arate / rate[index] == 1) {
/* A little bigger than expect */
- sub = (arate - rate[index]) * 100000;
+ sub = (u64)(arate - rate[index]) * 100000;
do_div(sub, rate[index]);
if (sub >= savesub)
continue;
@@ -1086,7 +1094,7 @@ static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
spdif_priv->txrate[index] = arate;
} else if (rate[index] / arate == 1) {
/* A little smaller than expect */
- sub = (rate[index] - arate) * 100000;
+ sub = (u64)(rate[index] - arate) * 100000;
do_div(sub, rate[index]);
if (sub >= savesub)
continue;
@@ -1105,7 +1113,7 @@ out:
static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
enum spdif_txrate index)
{
- const u32 rate[] = { 32000, 44100, 48000 };
+ const u32 rate[] = { 32000, 44100, 48000, 96000, 192000 };
struct platform_device *pdev = spdif_priv->pdev;
struct device *dev = &pdev->dev;
u64 savesub = 100000, ret;
@@ -1238,12 +1246,12 @@ static int fsl_spdif_probe(struct platform_device *pdev)
spin_lock_init(&ctrl->ctl_lock);
/* Init tx channel status default value */
- ctrl->ch_status[0] =
- IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+ ctrl->ch_status[0] = IEC958_AES0_CON_NOT_COPYRIGHT |
+ IEC958_AES0_CON_EMPHASIS_5015;
ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
ctrl->ch_status[2] = 0x00;
- ctrl->ch_status[3] =
- IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+ ctrl->ch_status[3] = IEC958_AES3_CON_FS_44100 |
+ IEC958_AES3_CON_CLOCK_1000PPM;
spdif_priv->dpll_locked = false;
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
index 16fde4b..00bd351 100644
--- a/sound/soc/fsl/fsl_spdif.h
+++ b/sound/soc/fsl/fsl_spdif.h
@@ -93,6 +93,8 @@
#define SCR_USRC_SEL_RECV (0x1 << SCR_USRC_SEL_OFFSET)
#define SCR_USRC_SEL_CHIP (0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_DMA_xX_EN(tx) (tx ? SCR_DMA_TX_EN : SCR_DMA_RX_EN)
+
/* SPDIF CDText control */
#define SRCD_CD_USER_OFFSET 1
#define SRCD_CD_USER (1 << SRCD_CD_USER_OFFSET)
@@ -164,8 +166,10 @@ enum spdif_txrate {
SPDIF_TXRATE_32000 = 0,
SPDIF_TXRATE_44100,
SPDIF_TXRATE_48000,
+ SPDIF_TXRATE_96000,
+ SPDIF_TXRATE_192000,
};
-#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_48000 + 1)
+#define SPDIF_TXRATE_MAX (SPDIF_TXRATE_192000 + 1)
#define SPDIF_CSTATUS_BYTE 6
@@ -175,7 +179,9 @@ enum spdif_txrate {
#define FSL_SPDIF_RATES_PLAYBACK (SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_48000)
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_192000)
#define FSL_SPDIF_RATES_CAPTURE (SNDRV_PCM_RATE_16000 | \
SNDRV_PCM_RATE_32000 | \
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 9bfef55..87eb577 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -590,8 +590,8 @@ static int fsl_ssi_set_bclk(struct snd_pcm_substream *substream,
else
clkrate = clk_round_rate(ssi_private->baudclk, tmprate);
- do_div(clkrate, factor);
- afreq = (u32)clkrate / (i + 1);
+ clkrate /= factor;
+ afreq = clkrate / (i + 1);
if (freq == afreq)
sub = 0;
@@ -1032,12 +1032,14 @@ static const struct snd_soc_dai_ops fsl_ssi_dai_ops = {
static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.probe = fsl_ssi_dai_probe,
.playback = {
+ .stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 2,
.rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS,
},
.capture = {
+ .stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 2,
.rates = FSLSSI_I2S_RATES,
diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c
index 267717a..46f9beb 100644
--- a/sound/soc/fsl/imx-audmux.c
+++ b/sound/soc/fsl/imx-audmux.c
@@ -67,7 +67,7 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
{
ssize_t ret;
char *buf;
- int port = (int)file->private_data;
+ uintptr_t port = (uintptr_t)file->private_data;
u32 pdcr, ptcr;
if (audmux_clk) {
@@ -147,7 +147,7 @@ static const struct file_operations audmux_debugfs_fops = {
static void audmux_debugfs_init(void)
{
- int i;
+ uintptr_t i;
char buf[20];
audmux_debugfs_root = debugfs_create_dir("audmux", NULL);
@@ -157,10 +157,10 @@ static void audmux_debugfs_init(void)
}
for (i = 0; i < MX31_AUDMUX_PORT7_SSI_PINS_7 + 1; i++) {
- snprintf(buf, sizeof(buf), "ssi%d", i);
+ snprintf(buf, sizeof(buf), "ssi%lu", i);
if (!debugfs_create_file(buf, 0444, audmux_debugfs_root,
(void *)i, &audmux_debugfs_fops))
- pr_warning("Failed to create AUDMUX port %d debugfs file\n",
+ pr_warning("Failed to create AUDMUX port %lu debugfs file\n",
i);
}
}
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index 0849b7b..0db94f49 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -59,7 +59,6 @@ int imx_pcm_dma_init(struct platform_device *pdev)
{
return devm_snd_dmaengine_pcm_register(&pdev->dev,
&imx_dmaengine_pcm_config,
- SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
SND_DMAENGINE_PCM_FLAG_COMPAT);
}
EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 03a7fdc..159e517f 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -116,6 +116,7 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
{
struct device_node *node;
struct clk *clk;
+ u32 val;
int ret;
/*
@@ -151,10 +152,8 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
}
dai->sysclk = clk_get_rate(clk);
- } else if (of_property_read_bool(np, "system-clock-frequency")) {
- of_property_read_u32(np,
- "system-clock-frequency",
- &dai->sysclk);
+ } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
+ dai->sysclk = val;
} else {
clk = of_clk_get(node, 0);
if (!IS_ERR(clk))
@@ -303,6 +302,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
{
struct snd_soc_dai_link *dai_link = priv->snd_card.dai_link;
struct simple_dai_props *dai_props = priv->dai_props;
+ u32 val;
int ret;
/* parsing the card name from DT */
@@ -325,8 +325,9 @@ static int asoc_simple_card_parse_of(struct device_node *node,
}
/* Factor to mclk, used in hw_params() */
- of_property_read_u32(node, "simple-audio-card,mclk-fs",
- &priv->mclk_fs);
+ ret = of_property_read_u32(node, "simple-audio-card,mclk-fs", &val);
+ if (ret == 0)
+ priv->mclk_fs = val;
dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
priv->snd_card.name : "");
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index c30fedb..f5b4a9c7 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -58,3 +58,15 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
help
This adds audio driver for Intel Baytrail platform based boards
with the MAX98090 audio codec.
+
+config SND_SOC_INTEL_BROADWELL_MACH
+ tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
+ depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
+ select SND_SOC_INTEL_HASWELL
+ select SND_COMPRESS_OFFLOAD
+ 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
+ If unsure select "N".
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index 4bfca79..7acbfc4 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -24,7 +24,9 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
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
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_BROADWELL_MACH) += snd-soc-sst-broadwell.o
diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c
new file mode 100644
index 0000000..0e550f1
--- /dev/null
+++ b/sound/soc/intel/broadwell.c
@@ -0,0 +1,251 @@
+/*
+ * Intel Broadwell Wildcatpoint SST Audio
+ *
+ * Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * 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/pcm.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "sst-dsp.h"
+#include "sst-haswell-ipc.h"
+
+#include "../codecs/rt286.h"
+
+static const struct snd_soc_dapm_widget broadwell_widgets[] = {
+ SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_MIC("DMIC1", NULL),
+ SND_SOC_DAPM_MIC("DMIC2", NULL),
+ SND_SOC_DAPM_LINE("Line Jack", NULL),
+};
+
+static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
+
+ /* speaker */
+ {"Speaker", NULL, "SPOR"},
+ {"Speaker", NULL, "SPOL"},
+
+ /* HP jack connectors - unknown if we have jack deteck */
+ {"Headphones", NULL, "HPO Pin"},
+
+ /* other jacks */
+ {"MIC1", NULL, "Mic Jack"},
+ {"LINE1", NULL, "Line Jack"},
+
+ /* digital mics */
+ {"DMIC1 Pin", NULL, "DMIC1"},
+ {"DMIC2 Pin", NULL, "DMIC2"},
+
+ /* CODEC BE connections */
+ {"SSP0 CODEC IN", NULL, "AIF1 Capture"},
+ {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
+};
+
+static int broadwell_ssp0_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);
+
+ /* The ADSP will covert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP0 to 16 bit */
+ snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ SNDRV_PCM_FORMAT_S16_LE);
+ return 0;
+}
+
+static int broadwell_rt286_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, RT286_SCLK_S_PLL, 24000000,
+ SND_SOC_CLOCK_IN);
+
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk configuration\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops broadwell_rt286_ops = {
+ .hw_params = broadwell_rt286_hw_params,
+};
+
+static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
+ struct sst_hsw *broadwell = pdata->dsp;
+ int ret;
+
+ /* Set ADSP SSP port settings */
+ ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
+ SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+ SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ if (ret < 0) {
+ dev_err(rtd->dev, "error: failed to set device config\n");
+ return ret;
+ }
+
+ /* always connected - check HP for jack detect */
+ snd_soc_dapm_enable_pin(dapm, "Headphones");
+ snd_soc_dapm_enable_pin(dapm, "Speaker");
+ snd_soc_dapm_enable_pin(dapm, "Mic Jack");
+ snd_soc_dapm_enable_pin(dapm, "Line Jack");
+ snd_soc_dapm_enable_pin(dapm, "DMIC1");
+ snd_soc_dapm_enable_pin(dapm, "DMIC2");
+
+ return 0;
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broadwell_rt286_dais[] = {
+ /* Front End DAI links */
+ {
+ .name = "System PCM",
+ .stream_name = "System Playback",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .init = broadwell_rtd_init,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Offload0",
+ .stream_name = "Offload0 Playback",
+ .cpu_dai_name = "Offload0 Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Offload1",
+ .stream_name = "Offload1 Playback",
+ .cpu_dai_name = "Offload1 Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ },
+ {
+ .name = "Loopback PCM",
+ .stream_name = "Loopback",
+ .cpu_dai_name = "Loopback Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 0,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "Capture PCM",
+ .stream_name = "Capture",
+ .cpu_dai_name = "Capture Pin",
+ .platform_name = "haswell-pcm-audio",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_capture = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .be_id = 0,
+ .cpu_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .codec_name = "i2c-INT343A:00",
+ .codec_dai_name = "rt286-aif1",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .be_hw_params_fixup = broadwell_ssp0_fixup,
+ .ops = &broadwell_rt286_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+/* broadwell audio machine driver for WPT + RT286S */
+static struct snd_soc_card broadwell_rt286 = {
+ .name = "broadwell-rt286",
+ .owner = THIS_MODULE,
+ .dai_link = broadwell_rt286_dais,
+ .num_links = ARRAY_SIZE(broadwell_rt286_dais),
+ .dapm_widgets = broadwell_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
+ .dapm_routes = broadwell_rt286_map,
+ .num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
+ .fully_routed = true,
+};
+
+static int broadwell_audio_probe(struct platform_device *pdev)
+{
+ broadwell_rt286.dev = &pdev->dev;
+
+ return snd_soc_register_card(&broadwell_rt286);
+}
+
+static int broadwell_audio_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_card(&broadwell_rt286);
+ return 0;
+}
+
+static struct platform_driver broadwell_audio = {
+ .probe = broadwell_audio_probe,
+ .remove = broadwell_audio_remove,
+ .driver = {
+ .name = "broadwell-audio",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(broadwell_audio)
+
+/* Module information */
+MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
+MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:broadwell-audio");
diff --git a/sound/soc/intel/byt-max98090.c b/sound/soc/intel/byt-max98090.c
index 5fc98c6..b8b8af5 100644
--- a/sound/soc/intel/byt-max98090.c
+++ b/sound/soc/intel/byt-max98090.c
@@ -39,8 +39,7 @@ static const struct snd_soc_dapm_widget byt_max98090_widgets[] = {
static const struct snd_soc_dapm_route byt_max98090_audio_map[] = {
{"IN34", NULL, "Headset Mic"},
- {"IN34", NULL, "MICBIAS"},
- {"MICBIAS", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "MICBIAS"},
{"DMICL", NULL, "Int Mic"},
{"Headphone", NULL, "HPL"},
{"Headphone", NULL, "HPR"},
@@ -64,14 +63,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
- {
- .pin = "Ext Spk",
- .mask = SND_JACK_LINEOUT,
- },
- {
- .pin = "Int Mic",
- .mask = SND_JACK_LINEIN,
- },
};
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
@@ -84,7 +75,8 @@ static struct snd_soc_jack_gpio hs_jack_gpios[] = {
{
.name = "mic-gpio",
.idx = 1,
- .report = SND_JACK_MICROPHONE | SND_JACK_LINEIN,
+ .invert = 1,
+ .report = SND_JACK_MICROPHONE,
.debounce_time = 200,
},
};
@@ -108,7 +100,8 @@ static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
}
/* Enable jack detection */
- ret = snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, jack);
+ ret = snd_soc_jack_new(codec, "Headset",
+ SND_JACK_LINEOUT | SND_JACK_HEADSET, jack);
if (ret)
return ret;
@@ -117,13 +110,9 @@ static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime)
if (ret)
return ret;
- ret = snd_soc_jack_add_gpiods(card->dev->parent, jack,
- ARRAY_SIZE(hs_jack_gpios),
- hs_jack_gpios);
- if (ret)
- return ret;
-
- return max98090_mic_detect(codec, jack);
+ return snd_soc_jack_add_gpiods(card->dev->parent, jack,
+ ARRAY_SIZE(hs_jack_gpios),
+ hs_jack_gpios);
}
static struct snd_soc_dai_link byt_max98090_dais[] = {
diff --git a/sound/soc/intel/byt-rt5640.c b/sound/soc/intel/byt-rt5640.c
index 53d160d..234a58d 100644
--- a/sound/soc/intel/byt-rt5640.c
+++ b/sound/soc/intel/byt-rt5640.c
@@ -34,6 +34,7 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
};
static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
+ {"Headset Mic", NULL, "MICBIAS1"},
{"IN2P", NULL, "Headset Mic"},
{"IN2N", NULL, "Headset Mic"},
{"DMIC1", NULL, "Internal Mic"},
diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h
new file mode 100644
index 0000000..14063ab
--- /dev/null
+++ b/sound/soc/intel/sst-atom-controls.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013-14 Intel Corp
+ * Author: Ramesh Babu <ramesh.babu.koul@intel.com>
+ * Omair M Abdullah <omair.m.abdullah@intel.com>
+ * Samreen Nilofer <samreen.nilofer@intel.com>
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ */
+
+#ifndef __SST_CONTROLS_V2_H__
+#define __SST_CONTROLS_V2_H__
+
+enum {
+ MERR_DPCM_AUDIO = 0,
+ MERR_DPCM_COMPR,
+};
+
+
+#endif
diff --git a/sound/soc/intel/sst-baytrail-ipc.c b/sound/soc/intel/sst-baytrail-ipc.c
index d207b22..67673a2 100644
--- a/sound/soc/intel/sst-baytrail-ipc.c
+++ b/sound/soc/intel/sst-baytrail-ipc.c
@@ -122,6 +122,26 @@ struct sst_byt_tstamp {
u32 channel_peak[8];
} __packed;
+struct sst_byt_fw_version {
+ u8 build;
+ u8 minor;
+ u8 major;
+ u8 type;
+} __packed;
+
+struct sst_byt_fw_build_info {
+ u8 date[16];
+ u8 time[16];
+} __packed;
+
+struct sst_byt_fw_init {
+ struct sst_byt_fw_version fw_version;
+ struct sst_byt_fw_build_info build_info;
+ u16 result;
+ u8 module_id;
+ u8 debug_info;
+} __packed;
+
/* driver internal IPC message structure */
struct ipc_message {
struct list_head list;
@@ -868,6 +888,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt;
struct sst_fw *byt_sst_fw;
+ struct sst_byt_fw_init init;
int err;
dev_dbg(dev, "initialising Byt DSP IPC\n");
@@ -929,6 +950,15 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
goto boot_err;
}
+ /* show firmware information */
+ sst_dsp_inbox_read(byt->dsp, &init, sizeof(init));
+ dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n",
+ init.fw_version.major, init.fw_version.minor,
+ init.fw_version.build, init.fw_version.type);
+ dev_info(byt->dev, "Build type: %x\n", init.fw_version.type);
+ dev_info(byt->dev, "Build date: %s %s\n",
+ init.build_info.date, init.build_info.time);
+
pdata->dsp = byt;
byt->fw = byt_sst_fw;
diff --git a/sound/soc/intel/sst-baytrail-pcm.c b/sound/soc/intel/sst-baytrail-pcm.c
index 8eab973..599401c 100644
--- a/sound/soc/intel/sst-baytrail-pcm.c
+++ b/sound/soc/intel/sst-baytrail-pcm.c
@@ -32,7 +32,7 @@ static const struct snd_pcm_hardware sst_byt_pcm_hardware = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FORMAT_S24_LE,
+ SNDRV_PCM_FMTBIT_S24_LE,
.period_bytes_min = 384,
.period_bytes_max = 48000,
.periods_min = 2,
diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c
index 0b715b2..cd23060 100644
--- a/sound/soc/intel/sst-dsp.c
+++ b/sound/soc/intel/sst-dsp.c
@@ -224,19 +224,23 @@ EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
void sst_dsp_dump(struct sst_dsp *sst)
{
- sst->ops->dump(sst);
+ if (sst->ops->dump)
+ sst->ops->dump(sst);
}
EXPORT_SYMBOL_GPL(sst_dsp_dump);
void sst_dsp_reset(struct sst_dsp *sst)
{
- sst->ops->reset(sst);
+ if (sst->ops->reset)
+ sst->ops->reset(sst);
}
EXPORT_SYMBOL_GPL(sst_dsp_reset);
int sst_dsp_boot(struct sst_dsp *sst)
{
- sst->ops->boot(sst);
+ if (sst->ops->boot)
+ sst->ops->boot(sst);
+
return 0;
}
EXPORT_SYMBOL_GPL(sst_dsp_boot);
diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h
index e44423b..3165dfa 100644
--- a/sound/soc/intel/sst-dsp.h
+++ b/sound/soc/intel/sst-dsp.h
@@ -52,7 +52,11 @@
#define SST_CLKCTL 0x78
#define SST_CSR2 0x80
#define SST_LTRC 0xE0
-#define SST_HDMC 0xE8
+#define SST_HMDC 0xE8
+
+#define SST_SHIM_BEGIN SST_CSR
+#define SST_SHIM_END SST_HDMC
+
#define SST_DBGO 0xF0
#define SST_SHIM_SIZE 0x100
@@ -73,6 +77,8 @@
#define SST_CSR_S0IOCS (0x1 << 21)
#define SST_CSR_S1IOCS (0x1 << 23)
#define SST_CSR_LPCS (0x1 << 31)
+#define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS)
+#define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1)
#define SST_BYT_CSR_RST (0x1 << 0)
#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
#define SST_BYT_CSR_STALL (0x1 << 2)
@@ -92,6 +98,14 @@
#define SST_IMRX_DONE (0x1 << 0)
#define SST_BYT_IMRX_REQUEST (0x1 << 1)
+/* IMRD / IMD */
+#define SST_IMRD_DONE (0x1 << 0)
+#define SST_IMRD_BUSY (0x1 << 1)
+#define SST_IMRD_SSP0 (0x1 << 16)
+#define SST_IMRD_DMAC0 (0x1 << 21)
+#define SST_IMRD_DMAC1 (0x1 << 22)
+#define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1)
+
/* IPCX / IPCC */
#define SST_IPCX_DONE (0x1 << 30)
#define SST_IPCX_BUSY (0x1 << 31)
@@ -118,9 +132,21 @@
/* LTRC */
#define SST_LTRC_VAL(x) (x << 0)
-/* HDMC */
-#define SST_HDMC_HDDA0(x) (x << 0)
-#define SST_HDMC_HDDA1(x) (x << 7)
+/* HMDC */
+#define SST_HMDC_HDDA0(x) (x << 0)
+#define SST_HMDC_HDDA1(x) (x << 7)
+#define SST_HMDC_HDDA_E0_CH0 1
+#define SST_HMDC_HDDA_E0_CH1 2
+#define SST_HMDC_HDDA_E0_CH2 4
+#define SST_HMDC_HDDA_E0_CH3 8
+#define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0)
+#define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1)
+#define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2)
+#define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3)
+#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \
+ SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3)
+#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \
+ SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3)
/* SST Vendor Defined Registers and bits */
@@ -130,11 +156,16 @@
#define SST_VDRTCTL3 0xaC
/* VDRTCTL0 */
+#define SST_VDRTCL0_APLLSE_MASK 1
#define SST_VDRTCL0_DSRAMPGE_SHIFT 16
#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
#define SST_VDRTCL0_ISRAMPGE_SHIFT 6
#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
+/* PMCS */
+#define SST_PMCS 0x84
+#define SST_PMCS_PS_MASK 0x3
+
struct sst_dsp;
/*
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
index 535f517..4b6c163 100644
--- a/sound/soc/intel/sst-haswell-dsp.c
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -28,9 +28,6 @@
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
-#include <linux/acpi.h>
-#include <acpi/acpi_bus.h>
-
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
#include "sst-haswell-ipc.h"
@@ -272,9 +269,9 @@ static void hsw_boot(struct sst_dsp *sst)
SST_CSR2_SDFD_SSP1);
/* enable DMA engine 0,1 all channels to access host memory */
- sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC,
- SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff),
- SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff));
+ sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
+ SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
+ SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
/* disable all clock gating */
writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
@@ -313,9 +310,7 @@ static const struct sst_adsp_memregion lp_region[] = {
/* wild cat point ADSP mem regions */
static const struct sst_adsp_memregion wpt_region[] = {
- {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
- {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
- {0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */
+ {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
{0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
};
@@ -339,26 +334,56 @@ static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
return 0;
}
+struct sst_sram_shift {
+ u32 dev_id; /* SST Device IDs */
+ u32 iram_shift;
+ u32 dram_shift;
+};
+
+static const struct sst_sram_shift sram_shift[] = {
+ {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
+ {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
+};
static u32 hsw_block_get_bit(struct sst_mem_block *block)
{
- u32 bit = 0, shift = 0;
+ u32 bit = 0, shift = 0, index;
+ struct sst_dsp *sst = block->dsp;
- switch (block->type) {
- case SST_MEM_DRAM:
- shift = 16;
- break;
- case SST_MEM_IRAM:
- shift = 6;
- break;
- default:
- return 0;
+ for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
+ if (sram_shift[index].dev_id == sst->id)
+ break;
}
+ if (index < ARRAY_SIZE(sram_shift)) {
+ switch (block->type) {
+ case SST_MEM_DRAM:
+ shift = sram_shift[index].dram_shift;
+ break;
+ case SST_MEM_IRAM:
+ shift = sram_shift[index].iram_shift;
+ break;
+ default:
+ shift = 0;
+ }
+ } else
+ shift = 0;
+
bit = 1 << (block->index + shift);
return bit;
}
+/*dummy read a SRAM block.*/
+static void sst_mem_block_dummy_read(struct sst_mem_block *block)
+{
+ u32 size;
+ u8 tmp_buf[4];
+ struct sst_dsp *sst = block->dsp;
+
+ size = block->size > 4 ? 4 : block->size;
+ memcpy_fromio(tmp_buf, sst->addr.lpe + block->offset, size);
+}
+
/* enable 32kB memory block - locks held by caller */
static int hsw_block_enable(struct sst_mem_block *block)
{
@@ -378,6 +403,8 @@ static int hsw_block_enable(struct sst_mem_block *block)
/* wait 18 DSP clock ticks */
udelay(10);
+ /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
+ sst_mem_block_dummy_read(block);
return 0;
}
@@ -488,8 +515,9 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
}
}
- /* set default power gating mask */
- writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0);
+ /* set default power gating control, enable power gating control for all blocks. that is,
+ can't be accessed, please enable each block before accessing. */
+ writel(0xffffffff, sst->addr.pci_cfg + SST_VDRTCTL0);
return 0;
}
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index 4342363..b629151 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -183,7 +183,7 @@ struct sst_hsw_ipc_fw_ready {
u32 inbox_size;
u32 outbox_size;
u32 fw_info_size;
- u8 fw_info[1];
+ u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
} __attribute__((packed));
struct ipc_message {
@@ -457,9 +457,10 @@ static void ipc_tx_msgs(struct kthread_work *work)
return;
}
- /* if the DSP is busy we will TX messages after IRQ */
+ /* if the DSP is busy, we will TX messages after IRQ.
+ * also postpone if we are in the middle of procesing completion irq*/
ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
- if (ipcx & SST_IPCX_BUSY) {
+ if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return;
}
@@ -502,6 +503,7 @@ static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
ipc_shim_dbg(hsw, "message timeout");
trace_ipc_error("error message timeout for", msg->header);
+ list_del(&msg->list);
ret = -ETIMEDOUT;
} else {
@@ -569,6 +571,9 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
{
struct sst_hsw_ipc_fw_ready fw_ready;
u32 offset;
+ u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
+ char *tmp[5], *pinfo;
+ int i = 0;
offset = (header & 0x1FFFFFFF) << 3;
@@ -589,6 +594,19 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
fw_ready.inbox_offset, fw_ready.inbox_size);
dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
fw_ready.outbox_offset, fw_ready.outbox_size);
+ if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
+ fw_ready.fw_info[fw_ready.fw_info_size] = 0;
+ dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
+
+ /* log the FW version info got from the mailbox here. */
+ memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
+ pinfo = &fw_info[0];
+ for (i = 0; i < sizeof(tmp) / sizeof(char *); i++)
+ tmp[i] = strsep(&pinfo, " ");
+ dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
+ "version: %s.%s, build %s, source commit id: %s\n",
+ tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
+ }
}
static void hsw_notification_work(struct work_struct *work)
@@ -671,7 +689,9 @@ static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
switch (stream_msg) {
case IPC_STR_STAGE_MESSAGE:
case IPC_STR_NOTIFICATION:
+ break;
case IPC_STR_RESET:
+ trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
break;
case IPC_STR_PAUSE:
stream->running = false;
@@ -762,7 +782,8 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
}
/* update any stream states */
- hsw_stream_update(hsw, msg);
+ if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
+ hsw_stream_update(hsw, msg);
/* wake up and return the error if we have waiters on this message ? */
list_del(&msg->list);
@@ -1628,7 +1649,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
{
u32 header, state_;
- int ret;
+ int ret, item;
header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
state_ = state;
@@ -1642,6 +1663,13 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
return ret;
}
+ for (item = 0; item < dx->entries_no; item++) {
+ dev_dbg(hsw->dev,
+ "Item[%d] offset[%x] - size[%x] - source[%x]\n",
+ item, dx->mem_info[item].offset,
+ dx->mem_info[item].size,
+ dx->mem_info[item].source);
+ }
dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
dx->entries_no, state);
@@ -1775,8 +1803,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
/* get the FW version */
sst_hsw_fw_get_version(hsw, &version);
- dev_info(hsw->dev, "FW loaded: type %d - version: %d.%d build %d\n",
- version.type, version.major, version.minor, version.build);
/* get the globalmixer */
ret = sst_hsw_mixer_get_info(hsw);
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c
index 058efb1..61bf6da 100644
--- a/sound/soc/intel/sst-haswell-pcm.c
+++ b/sound/soc/intel/sst-haswell-pcm.c
@@ -80,7 +80,7 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
- .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE |
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
.period_bytes_min = PAGE_SIZE,
.period_bytes_max = (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE,
@@ -400,7 +400,15 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16);
break;
case SNDRV_PCM_FORMAT_S24_LE:
- bits = SST_HSW_DEPTH_24BIT;
+ bits = SST_HSW_DEPTH_32BIT;
+ sst_hsw_stream_set_valid(hsw, pcm_data->stream, 24);
+ break;
+ case SNDRV_PCM_FORMAT_S8:
+ bits = SST_HSW_DEPTH_8BIT;
+ sst_hsw_stream_set_valid(hsw, pcm_data->stream, 8);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ bits = SST_HSW_DEPTH_32BIT;
sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32);
break;
default:
@@ -685,8 +693,9 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
#define HSW_FORMATS \
- (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S32_LE)
+ (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S8)
static struct snd_soc_dai_driver hsw_dais[] = {
{
@@ -696,7 +705,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
@@ -727,8 +736,8 @@ static struct snd_soc_dai_driver hsw_dais[] = {
.stream_name = "Loopback Capture",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = HSW_FORMATS,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
},
},
{
@@ -737,8 +746,8 @@ static struct snd_soc_dai_driver hsw_dais[] = {
.stream_name = "Analog Capture",
.channels_min = 2,
.channels_max = 2,
- .rates = SNDRV_PCM_RATE_8000_192000,
- .formats = HSW_FORMATS,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
},
},
};
diff --git a/sound/soc/intel/sst-mfld-dsp.h b/sound/soc/intel/sst-mfld-dsp.h
index 8d482d7..4257263 100644
--- a/sound/soc/intel/sst-mfld-dsp.h
+++ b/sound/soc/intel/sst-mfld-dsp.h
@@ -3,7 +3,7 @@
/*
* sst_mfld_dsp.h - Intel SST Driver for audio engine
*
- * Copyright (C) 2008-12 Intel Corporation
+ * Copyright (C) 2008-14 Intel Corporation
* Authors: Vinod Koul <vinod.koul@linux.intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
@@ -19,6 +19,142 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
+#define SST_MAX_BIN_BYTES 1024
+
+#define MAX_DBG_RW_BYTES 80
+#define MAX_NUM_SCATTER_BUFFERS 8
+#define MAX_LOOP_BACK_DWORDS 8
+/* IPC base address and mailbox, timestamp offsets */
+#define SST_MAILBOX_SIZE 0x0400
+#define SST_MAILBOX_SEND 0x0000
+#define SST_TIME_STAMP 0x1800
+#define SST_TIME_STAMP_MRFLD 0x800
+#define SST_RESERVED_OFFSET 0x1A00
+#define SST_SCU_LPE_MAILBOX 0x1000
+#define SST_LPE_SCU_MAILBOX 0x1400
+#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16)
+#define PROCESS_MSG 0x80
+
+/* Message ID's for IPC messages */
+/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */
+
+/* I2L Firmware/Codec Download msgs */
+#define IPC_IA_PREP_LIB_DNLD 0x01
+#define IPC_IA_LIB_DNLD_CMPLT 0x02
+#define IPC_IA_GET_FW_VERSION 0x04
+#define IPC_IA_GET_FW_BUILD_INF 0x05
+#define IPC_IA_GET_FW_INFO 0x06
+#define IPC_IA_GET_FW_CTXT 0x07
+#define IPC_IA_SET_FW_CTXT 0x08
+#define IPC_IA_PREPARE_SHUTDOWN 0x31
+/* I2L Codec Config/control msgs */
+#define IPC_PREP_D3 0x10
+#define IPC_IA_SET_CODEC_PARAMS 0x10
+#define IPC_IA_GET_CODEC_PARAMS 0x11
+#define IPC_IA_SET_PPP_PARAMS 0x12
+#define IPC_IA_GET_PPP_PARAMS 0x13
+#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA
+#define IPC_IA_ALG_PARAMS 0x1A
+#define IPC_IA_TUNING_PARAMS 0x1B
+#define IPC_IA_SET_RUNTIME_PARAMS 0x1C
+#define IPC_IA_SET_PARAMS 0x1
+#define IPC_IA_GET_PARAMS 0x2
+
+#define IPC_EFFECTS_CREATE 0xE
+#define IPC_EFFECTS_DESTROY 0xF
+
+/* I2L Stream config/control msgs */
+#define IPC_IA_ALLOC_STREAM_MRFLD 0x2
+#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */
+#define IPC_IA_FREE_STREAM_MRFLD 0x03
+#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */
+#define IPC_IA_SET_STREAM_PARAMS 0x22
+#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12
+#define IPC_IA_GET_STREAM_PARAMS 0x23
+#define IPC_IA_PAUSE_STREAM 0x24
+#define IPC_IA_PAUSE_STREAM_MRFLD 0x4
+#define IPC_IA_RESUME_STREAM 0x25
+#define IPC_IA_RESUME_STREAM_MRFLD 0x5
+#define IPC_IA_DROP_STREAM 0x26
+#define IPC_IA_DROP_STREAM_MRFLD 0x07
+#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */
+#define IPC_IA_DRAIN_STREAM_MRFLD 0x8
+#define IPC_IA_CONTROL_ROUTING 0x29
+#define IPC_IA_VTSV_UPDATE_MODULES 0x20
+#define IPC_IA_VTSV_DETECTED 0x21
+
+#define IPC_IA_START_STREAM_MRFLD 0X06
+#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */
+
+#define IPC_IA_SET_GAIN_MRFLD 0x21
+/* Debug msgs */
+#define IPC_IA_DBG_MEM_READ 0x40
+#define IPC_IA_DBG_MEM_WRITE 0x41
+#define IPC_IA_DBG_LOOP_BACK 0x42
+#define IPC_IA_DBG_LOG_ENABLE 0x45
+#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47
+
+/* L2I Firmware/Codec Download msgs */
+#define IPC_IA_FW_INIT_CMPLT 0x81
+#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01
+#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11
+
+/* L2I Codec Config/control msgs */
+#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */
+
+#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */
+#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */
+#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */
+#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */
+#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */
+#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */
+
+#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */
+/* L2S messages */
+#define IPC_SC_DDR_LINK_UP 0xC0
+#define IPC_SC_DDR_LINK_DOWN 0xC1
+#define IPC_SC_SET_LPECLK_REQ 0xC2
+#define IPC_SC_SSP_BIT_BANG 0xC3
+
+/* L2I Error reporting msgs */
+#define IPC_IA_MEM_ALLOC_FAIL 0xE0
+#define IPC_IA_PROC_ERR 0xE1 /* error in processing a
+ stream can be used by playback and
+ capture modules */
+
+/* L2I Debug msgs */
+#define IPC_IA_PRINT_STRING 0xF0
+
+/* Buffer under-run */
+#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B
+
+/* Mrfld specific defines:
+ * For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR)
+ * received from FW, the format is:
+ * - IPC High: pvt_id is set to zero. Always short message.
+ * - msg_id is in lower 16-bits of IPC low payload.
+ * - pipe_id is in higher 16-bits of IPC low payload for period_elapsed.
+ * - error id is in higher 16-bits of IPC low payload for async errors.
+ */
+#define SST_ASYNC_DRV_ID 0
+
+/* Command Response or Acknowledge message to any IPC message will have
+ * same message ID and stream ID information which is sent.
+ * There is no specific Ack message ID. The data field is used as response
+ * meaning.
+ */
+enum ackData {
+ IPC_ACK_SUCCESS = 0,
+ IPC_ACK_FAILURE,
+};
+
+enum ipc_ia_msg_id {
+ IPC_CMD = 1, /*!< Task Control message ID */
+ IPC_SET_PARAMS = 2,/*!< Task Set param message ID */
+ IPC_GET_PARAMS = 3, /*!< Task Get param message ID */
+ IPC_INVALID = 0xFF, /*!<Task Get param message ID */
+};
+
enum sst_codec_types {
/* AUDIO/MUSIC CODEC Type Definitions */
SST_CODEC_TYPE_UNKNOWN = 0,
@@ -35,14 +171,157 @@ enum stream_type {
SST_STREAM_TYPE_MUSIC = 1,
};
+enum sst_error_codes {
+ /* Error code,response to msgId: Description */
+ /* Common error codes */
+ SST_SUCCESS = 0, /* Success */
+ SST_ERR_INVALID_STREAM_ID = 1,
+ SST_ERR_INVALID_MSG_ID = 2,
+ SST_ERR_INVALID_STREAM_OP = 3,
+ SST_ERR_INVALID_PARAMS = 4,
+ SST_ERR_INVALID_CODEC = 5,
+ SST_ERR_INVALID_MEDIA_TYPE = 6,
+ SST_ERR_STREAM_ERR = 7,
+
+ SST_ERR_STREAM_IN_USE = 15,
+};
+
+struct ipc_dsp_hdr {
+ u16 mod_index_id:8; /*!< DSP Command ID specific to tasks */
+ u16 pipe_id:8; /*!< instance of the module in the pipeline */
+ u16 mod_id; /*!< Pipe_id */
+ u16 cmd_id; /*!< Module ID = lpe_algo_types_t */
+ u16 length; /*!< Length of the payload only */
+} __packed;
+
+union ipc_header_high {
+ struct {
+ u32 msg_id:8; /* Message ID - Max 256 Message Types */
+ u32 task_id:4; /* Task ID associated with this comand */
+ u32 drv_id:4; /* Identifier for the driver to track*/
+ u32 rsvd1:8; /* Reserved */
+ u32 result:4; /* Reserved */
+ u32 res_rqd:1; /* Response rqd */
+ u32 large:1; /* Large Message if large = 1 */
+ u32 done:1; /* bit 30 - Done bit */
+ u32 busy:1; /* bit 31 - busy bit*/
+ } part;
+ u32 full;
+} __packed;
+/* IPC header */
+union ipc_header_mrfld {
+ struct {
+ u32 header_low_payload;
+ union ipc_header_high header_high;
+ } p;
+ u64 full;
+} __packed;
+/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/
+
+/* IPC Header */
+union ipc_header {
+ struct {
+ u32 msg_id:8; /* Message ID - Max 256 Message Types */
+ u32 str_id:5;
+ u32 large:1; /* Large Message if large = 1 */
+ u32 reserved:2; /* Reserved for future use */
+ u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */
+ u32 done:1; /* bit 30 */
+ u32 busy:1; /* bit 31 */
+ } part;
+ u32 full;
+} __packed;
+
+/* Firmware build info */
+struct sst_fw_build_info {
+ unsigned char date[16]; /* Firmware build date */
+ unsigned char time[16]; /* Firmware build time */
+} __packed;
+
+/* Firmware Version info */
+struct snd_sst_fw_version {
+ u8 build; /* build number*/
+ u8 minor; /* minor number*/
+ u8 major; /* major number*/
+ u8 type; /* build type */
+};
+
+struct ipc_header_fw_init {
+ struct snd_sst_fw_version fw_version;/* Firmware version details */
+ struct sst_fw_build_info build_info;
+ u16 result; /* Fw init result */
+ u8 module_id; /* Module ID in case of error */
+ u8 debug_info; /* Debug info from Module ID in case of fail */
+} __packed;
+
+struct snd_sst_tstamp {
+ u64 ring_buffer_counter; /* PB/CP: Bytes copied from/to DDR. */
+ u64 hardware_counter; /* PB/CP: Bytes DMAed to/from SSP. */
+ u64 frames_decoded;
+ u64 bytes_decoded;
+ u64 bytes_copied;
+ u32 sampling_frequency;
+ u32 channel_peak[8];
+} __packed;
+
+/* Stream type params struture for Alloc stream */
+struct snd_sst_str_type {
+ u8 codec_type; /* Codec type */
+ u8 str_type; /* 1 = voice 2 = music */
+ u8 operation; /* Playback or Capture */
+ u8 protected_str; /* 0=Non DRM, 1=DRM */
+ u8 time_slots;
+ u8 reserved; /* Reserved */
+ u16 result; /* Result used for acknowledgment */
+} __packed;
+
+/* Library info structure */
+struct module_info {
+ u32 lib_version;
+ u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/
+ u32 media_type;
+ u8 lib_name[12];
+ u32 lib_caps;
+ unsigned char b_date[16]; /* Lib build date */
+ unsigned char b_time[16]; /* Lib build time */
+} __packed;
+
+/* Library slot info */
+struct lib_slot_info {
+ u8 slot_num; /* 1 or 2 */
+ u8 reserved1;
+ u16 reserved2;
+ u32 iram_size; /* slot size in IRAM */
+ u32 dram_size; /* slot size in DRAM */
+ u32 iram_offset; /* starting offset of slot in IRAM */
+ u32 dram_offset; /* starting offset of slot in DRAM */
+} __packed;
+
+struct snd_ppp_mixer_params {
+ __u32 type; /*Type of the parameter */
+ __u32 size;
+ __u32 input_stream_bitmap; /*Input stream Bit Map*/
+} __packed;
+
+struct snd_sst_lib_download {
+ struct module_info lib_info; /* library info type, capabilities etc */
+ struct lib_slot_info slot_info; /* slot info to be downloaded */
+ u32 mod_entry_pt;
+};
+
+struct snd_sst_lib_download_info {
+ struct snd_sst_lib_download dload_lib;
+ u16 result; /* Result used for acknowledgment */
+ u8 pvt_id; /* Private ID */
+ u8 reserved; /* for alignment */
+};
struct snd_pcm_params {
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
- u32 reserved; /* Bitrate in bits per second */
- u32 sfreq; /* Sampling rate in Hz */
- u8 use_offload_path;
+ u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces
+ 1-PCM stream via compressed interface */
u8 reserved2;
- u16 reserved3;
+ u32 sfreq; /* Sampling rate in Hz */
u8 channel_map[8];
} __packed;
@@ -76,6 +355,7 @@ struct snd_aac_params {
struct snd_wma_params {
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
+ u16 reserved1;
u32 brate; /* Use the hard coded value. */
u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
u32 channel_mask; /* Channel Mask */
@@ -101,26 +381,153 @@ struct sst_address_info {
};
struct snd_sst_alloc_params_ext {
- struct sst_address_info ring_buf_info[8];
- u8 sg_count;
- u8 reserved;
- u16 reserved2;
- u32 frag_size; /*Number of samples after which period elapsed
+ __u16 sg_count;
+ __u16 reserved;
+ __u32 frag_size; /*Number of samples after which period elapsed
message is sent valid only if path = 0*/
-} __packed;
+ struct sst_address_info ring_buf_info[8];
+};
struct snd_sst_stream_params {
union snd_sst_codec_params uc;
} __packed;
struct snd_sst_params {
+ u32 result;
u32 stream_id;
u8 codec;
u8 ops;
u8 stream_type;
u8 device_type;
+ u8 task;
struct snd_sst_stream_params sparams;
struct snd_sst_alloc_params_ext aparams;
};
+struct snd_sst_alloc_mrfld {
+ u16 codec_type;
+ u8 operation;
+ u8 sg_count;
+ struct sst_address_info ring_buf_info[8];
+ u32 frag_size;
+ u32 ts;
+ struct snd_sst_stream_params codec_params;
+} __packed;
+
+/* Alloc stream params structure */
+struct snd_sst_alloc_params {
+ struct snd_sst_str_type str_type;
+ struct snd_sst_stream_params stream_params;
+ struct snd_sst_alloc_params_ext alloc_params;
+} __packed;
+
+/* Alloc stream response message */
+struct snd_sst_alloc_response {
+ struct snd_sst_str_type str_type; /* Stream type for allocation */
+ struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */
+};
+
+/* Drop response */
+struct snd_sst_drop_response {
+ u32 result;
+ u32 bytes;
+};
+
+struct snd_sst_async_msg {
+ u32 msg_id; /* Async msg id */
+ u32 payload[0];
+};
+
+struct snd_sst_async_err_msg {
+ u32 fw_resp; /* Firmware Result */
+ u32 lib_resp; /*Library result */
+} __packed;
+
+struct snd_sst_vol {
+ u32 stream_id;
+ s32 volume;
+ u32 ramp_duration;
+ u32 ramp_type; /* Ramp type, default=0 */
+};
+
+/* Gain library parameters for mrfld
+ * based on DSP command spec v0.82
+ */
+struct snd_sst_gain_v2 {
+ u16 gain_cell_num; /* num of gain cells to modify*/
+ u8 cell_nbr_idx; /* instance index*/
+ u8 cell_path_idx; /* pipe-id */
+ u16 module_id; /*module id */
+ u16 left_cell_gain; /* left gain value in dB*/
+ u16 right_cell_gain; /* right gain value in dB*/
+ u16 gain_time_const; /* gain time constant*/
+} __packed;
+
+struct snd_sst_mute {
+ u32 stream_id;
+ u32 mute;
+};
+
+struct snd_sst_runtime_params {
+ u8 type;
+ u8 str_id;
+ u8 size;
+ u8 rsvd;
+ void *addr;
+} __packed;
+
+enum stream_param_type {
+ SST_SET_TIME_SLOT = 0,
+ SST_SET_CHANNEL_INFO = 1,
+ OTHERS = 2, /*reserved for future params*/
+};
+
+/* CSV Voice call routing structure */
+struct snd_sst_control_routing {
+ u8 control; /* 0=start, 1=Stop */
+ u8 reserved[3]; /* Reserved- for 32 bit alignment */
+};
+
+struct ipc_post {
+ struct list_head node;
+ union ipc_header header; /* driver specific */
+ bool is_large;
+ bool is_process_reply;
+ union ipc_header_mrfld mrfld_header;
+ char *mailbox_data;
+};
+
+struct snd_sst_ctxt_params {
+ u32 address; /* Physical Address in DDR where the context is stored */
+ u32 size; /* size of the context */
+};
+
+struct snd_sst_lpe_log_params {
+ u8 dbg_type;
+ u8 module_id;
+ u8 log_level;
+ u8 reserved;
+} __packed;
+
+enum snd_sst_bytes_type {
+ SND_SST_BYTES_SET = 0x1,
+ SND_SST_BYTES_GET = 0x2,
+};
+
+struct snd_sst_bytes_v2 {
+ u8 type;
+ u8 ipc_msg;
+ u8 block;
+ u8 task_id;
+ u8 pipe_id;
+ u8 rsvd;
+ u16 len;
+ char bytes[0];
+};
+
+#define MAX_VTSV_FILES 2
+struct snd_sst_vtsv_info {
+ struct sst_address_info vfiles[MAX_VTSV_FILES];
+} __packed;
+
#endif /* __SST_MFLD_DSP_H__ */
diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c
index 02abd19..29c059c 100644
--- a/sound/soc/intel/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/sst-mfld-platform-compress.c
@@ -100,14 +100,19 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
int retval;
struct snd_sst_params str_params;
struct sst_compress_cb cb;
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
stream = cstream->runtime->private_data;
/* construct fw structure for this*/
memset(&str_params, 0, sizeof(str_params));
- str_params.ops = STREAM_OPS_PLAYBACK;
- str_params.stream_type = SST_STREAM_TYPE_MUSIC;
- str_params.device_type = SND_SST_DEVICE_COMPRESS;
+ /* fill the device type and stream id to pass to SST driver */
+ retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
+ pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
+ if (retval < 0)
+ return retval;
switch (params->codec.id) {
case SND_AUDIOCODEC_MP3: {
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c
index 7c790f5..706212a 100644
--- a/sound/soc/intel/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/sst-mfld-platform-pcm.c
@@ -1,7 +1,7 @@
/*
* sst_mfld_platform.c - Intel MID Platform driver
*
- * Copyright (C) 2010-2013 Intel Corp
+ * Copyright (C) 2010-2014 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -27,7 +27,9 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
#include "sst-mfld-platform.h"
+#include "sst-atom-controls.h"
struct sst_device *sst;
static DEFINE_MUTEX(sst_lock);
@@ -92,6 +94,13 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = {
.fifo_size = SST_FIFO_SIZE,
};
+static struct sst_dev_stream_map dpcm_strm_map[] = {
+ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */
+ {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
+ {MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
+ {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
+};
+
/* MFLD - MSIC */
static struct snd_soc_dai_driver sst_platform_dai[] = {
{
@@ -143,58 +152,142 @@ static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
return state;
}
+static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
+ struct snd_sst_alloc_params_ext *alloc_param)
+{
+ unsigned int channels;
+ snd_pcm_uframes_t period_size;
+ ssize_t periodbytes;
+ ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
+ u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);
+
+ channels = substream->runtime->channels;
+ period_size = substream->runtime->period_size;
+ periodbytes = samples_to_bytes(substream->runtime, period_size);
+ alloc_param->ring_buf_info[0].addr = buffer_addr;
+ alloc_param->ring_buf_info[0].size = buffer_bytes;
+ alloc_param->sg_count = 1;
+ alloc_param->reserved = 0;
+ alloc_param->frag_size = periodbytes * channels;
+
+}
static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
- struct sst_pcm_params *param)
+ struct snd_sst_stream_params *param)
{
+ param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
+ param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
+ param->uc.pcm_params.sfreq = substream->runtime->rate;
+
+ /* PCM stream via ALSA interface */
+ param->uc.pcm_params.use_offload_path = 0;
+ param->uc.pcm_params.reserved2 = 0;
+ memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));
- param->num_chan = (u8) substream->runtime->channels;
- param->pcm_wd_sz = substream->runtime->sample_bits;
- param->reserved = 0;
- param->sfreq = substream->runtime->rate;
- param->ring_buffer_size = snd_pcm_lib_buffer_bytes(substream);
- param->period_count = substream->runtime->period_size;
- param->ring_buffer_addr = virt_to_phys(substream->dma_buffer.area);
- pr_debug("period_cnt = %d\n", param->period_count);
- pr_debug("sfreq= %d, wd_sz = %d\n", param->sfreq, param->pcm_wd_sz);
}
-static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
+static int sst_get_stream_mapping(int dev, int sdev, int dir,
+ struct sst_dev_stream_map *map, int size)
+{
+ int i;
+
+ if (map == NULL)
+ return -EINVAL;
+
+
+ /* index 0 is not used in stream map */
+ for (i = 1; i < size; i++) {
+ if ((map[i].dev_num == dev) && (map[i].direction == dir))
+ return i;
+ }
+ return 0;
+}
+
+int sst_fill_stream_params(void *substream,
+ const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)
+{
+ int map_size;
+ int index;
+ struct sst_dev_stream_map *map;
+ struct snd_pcm_substream *pstream = NULL;
+ struct snd_compr_stream *cstream = NULL;
+
+ map = ctx->pdata->pdev_strm_map;
+ map_size = ctx->pdata->strm_map_size;
+
+ if (is_compress == true)
+ cstream = (struct snd_compr_stream *)substream;
+ else
+ pstream = (struct snd_pcm_substream *)substream;
+
+ str_params->stream_type = SST_STREAM_TYPE_MUSIC;
+
+ /* For pcm streams */
+ if (pstream) {
+ index = sst_get_stream_mapping(pstream->pcm->device,
+ pstream->number, pstream->stream,
+ map, map_size);
+ if (index <= 0)
+ return -EINVAL;
+
+ str_params->stream_id = index;
+ str_params->device_type = map[index].device_id;
+ str_params->task = map[index].task_id;
+
+ str_params->ops = (u8)pstream->stream;
+ }
+
+ if (cstream) {
+ index = sst_get_stream_mapping(cstream->device->device,
+ 0, cstream->direction,
+ map, map_size);
+ if (index <= 0)
+ return -EINVAL;
+ str_params->stream_id = index;
+ str_params->device_type = map[index].device_id;
+ str_params->task = map[index].task_id;
+
+ str_params->ops = (u8)cstream->direction;
+ }
+ return 0;
+}
+
+static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
+ struct snd_soc_platform *platform)
{
struct sst_runtime_stream *stream =
substream->runtime->private_data;
- struct sst_pcm_params param = {0};
- struct sst_stream_params str_params = {0};
- int ret_val;
+ struct snd_sst_stream_params param = {{{0,},},};
+ struct snd_sst_params str_params = {0};
+ struct snd_sst_alloc_params_ext alloc_params = {0};
+ int ret_val = 0;
+ struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
/* set codec params and inform SST driver the same */
sst_fill_pcm_params(substream, &param);
+ sst_fill_alloc_params(substream, &alloc_params);
substream->runtime->dma_area = substream->dma_buffer.area;
str_params.sparams = param;
- str_params.codec = param.codec;
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- str_params.ops = STREAM_OPS_PLAYBACK;
- str_params.device_type = substream->pcm->device + 1;
- pr_debug("Playbck stream,Device %d\n",
- substream->pcm->device);
- } else {
- str_params.ops = STREAM_OPS_CAPTURE;
- str_params.device_type = SND_SST_DEVICE_CAPTURE;
- pr_debug("Capture stream,Device %d\n",
- substream->pcm->device);
- }
- ret_val = stream->ops->open(&str_params);
- pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
+ str_params.aparams = alloc_params;
+ str_params.codec = SST_CODEC_TYPE_PCM;
+
+ /* fill the device type and stream id to pass to SST driver */
+ ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
if (ret_val < 0)
return ret_val;
- stream->stream_info.str_id = ret_val;
- pr_debug("str id : %d\n", stream->stream_info.str_id);
+ stream->stream_info.str_id = str_params.stream_id;
+
+ ret_val = stream->ops->open(&str_params);
+ if (ret_val <= 0)
+ return ret_val;
+
+
return ret_val;
}
-static void sst_period_elapsed(void *mad_substream)
+static void sst_period_elapsed(void *arg)
{
- struct snd_pcm_substream *substream = mad_substream;
+ struct snd_pcm_substream *substream = arg;
struct sst_runtime_stream *stream;
int status;
@@ -218,7 +311,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
pr_debug("setting buffer ptr param\n");
sst_set_stream_status(stream, SST_PLATFORM_INIT);
stream->stream_info.period_elapsed = sst_period_elapsed;
- stream->stream_info.mad_substream = substream;
+ stream->stream_info.arg = substream;
stream->stream_info.buffer_ptr = 0;
stream->stream_info.sfreq = substream->runtime->rate;
ret_val = stream->ops->device_control(
@@ -230,19 +323,12 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
}
/* end -- helper functions */
-static int sst_platform_open(struct snd_pcm_substream *substream)
+static int sst_media_open(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
+ int ret_val = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct sst_runtime_stream *stream;
- int ret_val;
-
- pr_debug("sst_platform_open called\n");
-
- snd_soc_set_runtime_hwparams(substream, &sst_platform_pcm_hw);
- ret_val = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret_val < 0)
- return ret_val;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
@@ -251,50 +337,69 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
/* get the sst ops */
mutex_lock(&sst_lock);
- if (!sst) {
+ if (!sst ||
+ !try_module_get(sst->dev->driver->owner)) {
pr_err("no device available to run\n");
- mutex_unlock(&sst_lock);
- kfree(stream);
- return -ENODEV;
- }
- if (!try_module_get(sst->dev->driver->owner)) {
- mutex_unlock(&sst_lock);
- kfree(stream);
- return -ENODEV;
+ ret_val = -ENODEV;
+ goto out_ops;
}
stream->ops = sst->ops;
mutex_unlock(&sst_lock);
stream->stream_info.str_id = 0;
- sst_set_stream_status(stream, SST_PLATFORM_INIT);
- stream->stream_info.mad_substream = substream;
+
+ stream->stream_info.arg = substream;
/* allocate memory for SST API set */
runtime->private_data = stream;
- return 0;
+ /* Make sure, that the period size is always even */
+ snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIODS, 2);
+
+ return snd_pcm_hw_constraint_integer(runtime,
+ SNDRV_PCM_HW_PARAM_PERIODS);
+out_ops:
+ kfree(stream);
+ mutex_unlock(&sst_lock);
+ return ret_val;
}
-static int sst_platform_close(struct snd_pcm_substream *substream)
+static void sst_media_close(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
- pr_debug("sst_platform_close called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (str_id)
ret_val = stream->ops->close(str_id);
module_put(sst->dev->driver->owner);
kfree(stream);
- return ret_val;
}
-static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
+static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform,
+ struct snd_pcm_substream *substream)
+{
+ struct sst_data *sst = snd_soc_platform_get_drvdata(platform);
+ struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
+ struct sst_runtime_stream *stream =
+ substream->runtime->private_data;
+ u32 str_id = stream->stream_info.str_id;
+ unsigned int pipe_id;
+ pipe_id = map[str_id].device_id;
+
+ pr_debug("%s: got pipe_id = %#x for str_id = %d\n",
+ __func__, pipe_id, str_id);
+ return pipe_id;
+}
+
+static int sst_media_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
- pr_debug("sst_platform_pcm_prepare called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (stream->stream_info.str_id) {
@@ -303,8 +408,8 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
return ret_val;
}
- ret_val = sst_platform_alloc_stream(substream);
- if (ret_val < 0)
+ ret_val = sst_platform_alloc_stream(substream, dai->platform);
+ if (ret_val <= 0)
return ret_val;
snprintf(substream->pcm->id, sizeof(substream->pcm->id),
"%d", stream->stream_info.str_id);
@@ -316,6 +421,41 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
return ret_val;
}
+static int sst_media_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
+ return 0;
+}
+
+static int sst_media_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static struct snd_soc_dai_ops sst_media_dai_ops = {
+ .startup = sst_media_open,
+ .shutdown = sst_media_close,
+ .prepare = sst_media_prepare,
+ .hw_params = sst_media_hw_params,
+ .hw_free = sst_media_hw_free,
+};
+
+static int sst_platform_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime;
+
+ if (substream->pcm->internal)
+ return 0;
+
+ runtime = substream->runtime;
+ runtime->hw = sst_platform_pcm_hw;
+ return 0;
+}
+
static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
@@ -331,7 +471,7 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
pr_debug("sst: Trigger Start\n");
str_cmd = SST_SND_START;
status = SST_PLATFORM_RUNNING;
- stream->stream_info.mad_substream = substream;
+ stream->stream_info.arg = substream;
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("sst: in stop\n");
@@ -377,32 +517,15 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
pr_err("sst: error code = %d\n", ret_val);
return ret_val;
}
- return stream->stream_info.buffer_ptr;
-}
-
-static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
-
- return 0;
-}
-
-static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- return snd_pcm_lib_free_pages(substream);
+ substream->runtime->delay = str_info->pcm_delay;
+ return str_info->buffer_ptr;
}
static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open,
- .close = sst_platform_close,
.ioctl = snd_pcm_lib_ioctl,
- .prepare = sst_platform_pcm_prepare,
.trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer,
- .hw_params = sst_platform_pcm_hw_params,
- .hw_free = sst_platform_pcm_hw_free,
};
static void sst_pcm_free(struct snd_pcm *pcm)
@@ -413,15 +536,15 @@ static void sst_pcm_free(struct snd_pcm *pcm)
static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int retval = 0;
- pr_debug("sst_pcm_new called\n");
- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
- pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ if (dai->driver->playback.channels_min ||
+ dai->driver->capture.channels_min) {
retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
+ snd_dma_continuous_data(GFP_DMA),
SST_MIN_BUFFER, SST_MAX_BUFFER);
if (retval) {
pr_err("dma buffer allocationf fail\n");
@@ -445,10 +568,28 @@ static const struct snd_soc_component_driver sst_component = {
static int sst_platform_probe(struct platform_device *pdev)
{
+ struct sst_data *drv;
int ret;
+ struct sst_platform_data *pdata;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (drv == NULL) {
+ pr_err("kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (pdata == NULL) {
+ pr_err("kzalloc failed for pdata\n");
+ return -ENOMEM;
+ }
+
+ pdata->pdev_strm_map = dpcm_strm_map;
+ pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
+ drv->pdata = pdata;
+ mutex_init(&drv->lock);
+ dev_set_drvdata(&pdev->dev, drv);
- pr_debug("sst_platform_probe called\n");
- sst = NULL;
ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
if (ret) {
pr_err("registering soc platform failed\n");
diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h
index 6c5e7dc..6c6a42c 100644
--- a/sound/soc/intel/sst-mfld-platform.h
+++ b/sound/soc/intel/sst-mfld-platform.h
@@ -39,9 +39,10 @@ extern struct sst_device *sst;
struct pcm_stream_info {
int str_id;
- void *mad_substream;
- void (*period_elapsed) (void *mad_substream);
+ void *arg;
+ void (*period_elapsed) (void *arg);
unsigned long long buffer_ptr;
+ unsigned long long pcm_delay;
int sfreq;
};
@@ -62,7 +63,9 @@ enum sst_controls {
SST_SND_BUFFER_POINTER = 0x05,
SST_SND_STREAM_INIT = 0x06,
SST_SND_START = 0x07,
- SST_MAX_CONTROLS = 0x07,
+ SST_SET_BYTE_STREAM = 0x100A,
+ SST_GET_BYTE_STREAM = 0x100B,
+ SST_MAX_CONTROLS = SST_GET_BYTE_STREAM,
};
enum sst_stream_ops {
@@ -124,8 +127,9 @@ struct compress_sst_ops {
};
struct sst_ops {
- int (*open) (struct sst_stream_params *str_param);
+ int (*open) (struct snd_sst_params *str_param);
int (*device_control) (int cmd, void *arg);
+ int (*set_generic_params)(enum sst_controls cmd, void *arg);
int (*close) (unsigned int str_id);
};
@@ -143,10 +147,27 @@ struct sst_device {
char *name;
struct device *dev;
struct sst_ops *ops;
+ struct platform_device *pdev;
struct compress_sst_ops *compr_ops;
};
+struct sst_data;
void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
+int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
+ struct snd_sst_params *str_params, bool is_compress);
+
+struct sst_algo_int_control_v2 {
+ struct soc_mixer_control mc;
+ u16 module_id; /* module identifieer */
+ u16 pipe_id; /* location info: pipe_id + instance_id */
+ u16 instance_id;
+ unsigned int value; /* Value received is stored here */
+};
+struct sst_data {
+ struct platform_device *pdev;
+ struct sst_platform_data *pdata;
+ struct mutex lock;
+};
int sst_register_dsp(struct sst_device *sst);
int sst_unregister_dsp(struct sst_device *sst);
#endif
diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c
index 0cc41f9..8c9cc64 100644
--- a/sound/soc/omap/ams-delta.c
+++ b/sound/soc/omap/ams-delta.c
@@ -301,7 +301,7 @@ static int cx81801_open(struct tty_struct *tty)
static void cx81801_close(struct tty_struct *tty)
{
struct snd_soc_codec *codec = tty->disc_data;
- struct snd_soc_dapm_context *dapm = &codec->card->dapm;
+ struct snd_soc_dapm_context *dapm = &codec->component.card->dapm;
del_timer_sync(&cx81801_timer);
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index 6acb225..2434b6d 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -11,6 +11,7 @@ config SND_PXA2XX_SOC
config SND_MMP_SOC
bool "Soc Audio for Marvell MMP chips"
depends on ARCH_MMP
+ select MMP_SRAM
select SND_SOC_GENERIC_DMAENGINE_PCM
select SND_ARM
help
@@ -40,7 +41,7 @@ config SND_MMP_SOC_SSPA
config SND_PXA2XX_SOC_CORGI
tristate "SoC Audio support for Sharp Zaurus SL-C7x0"
- depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx
+ depends on SND_PXA2XX_SOC && PXA_SHARP_C7xx && I2C
select SND_PXA2XX_SOC_I2S
select SND_SOC_WM8731
help
@@ -49,7 +50,7 @@ config SND_PXA2XX_SOC_CORGI
config SND_PXA2XX_SOC_SPITZ
tristate "SoC Audio support for Sharp Zaurus SL-Cxx00"
- depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00
+ depends on SND_PXA2XX_SOC && PXA_SHARP_Cxx00 && I2C
select SND_PXA2XX_SOC_I2S
select SND_SOC_WM8750
help
@@ -58,7 +59,7 @@ config SND_PXA2XX_SOC_SPITZ
config SND_PXA2XX_SOC_Z2
tristate "SoC Audio support for Zipit Z2"
- depends on SND_PXA2XX_SOC && MACH_ZIPIT2
+ depends on SND_PXA2XX_SOC && MACH_ZIPIT2 && I2C
select SND_PXA2XX_SOC_I2S
select SND_SOC_WM8750
help
@@ -66,7 +67,7 @@ config SND_PXA2XX_SOC_Z2
config SND_PXA2XX_SOC_POODLE
tristate "SoC Audio support for Poodle"
- depends on SND_PXA2XX_SOC && MACH_POODLE
+ depends on SND_PXA2XX_SOC && MACH_POODLE && I2C
select SND_PXA2XX_SOC_I2S
select SND_SOC_WM8731
help
@@ -181,7 +182,7 @@ config SND_PXA2XX_SOC_HX4700
config SND_PXA2XX_SOC_MAGICIAN
tristate "SoC Audio support for HTC Magician"
- depends on SND_PXA2XX_SOC && MACH_MAGICIAN
+ depends on SND_PXA2XX_SOC && MACH_MAGICIAN && I2C
select SND_PXA2XX_SOC_I2S
select SND_PXA_SOC_SSP
select SND_SOC_UDA1380
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c
index 7eba797..1c8d011 100644
--- a/sound/soc/s6000/s6000-i2s.c
+++ b/sound/soc/s6000/s6000-i2s.c
@@ -570,7 +570,7 @@ err_release_none:
return ret;
}
-static void s6000_i2s_remove(struct platform_device *pdev)
+static int s6000_i2s_remove(struct platform_device *pdev)
{
struct s6000_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
struct resource *region;
@@ -597,6 +597,8 @@ static void s6000_i2s_remove(struct platform_device *pdev)
iounmap(mmio);
region = platform_get_resource(pdev, IORESOURCE_IO, 0);
release_mem_region(region->start, resource_size(region));
+
+ return 0;
}
static struct platform_driver s6000_i2s_driver = {
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 2ac76fa..d2533db 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -68,6 +68,8 @@ struct i2s_dai {
#define DAI_OPENED (1 << 0) /* Dai is opened */
#define DAI_MANAGER (1 << 1) /* Dai is the manager */
unsigned mode;
+ /* CDCLK pin direction: 0 - input, 1 - output */
+ unsigned int cdclk_out:1;
/* Driver for this DAI */
struct snd_soc_dai_driver i2s_dai_drv;
/* DMA parameters */
@@ -737,6 +739,9 @@ static int i2s_startup(struct snd_pcm_substream *substream,
spin_unlock_irqrestore(&lock, flags);
+ if (!is_opened(other) && i2s->cdclk_out)
+ i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
+ 0, SND_SOC_CLOCK_OUT);
return 0;
}
@@ -752,9 +757,13 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
i2s->mode &= ~DAI_OPENED;
i2s->mode &= ~DAI_MANAGER;
- if (is_opened(other))
+ if (is_opened(other)) {
other->mode |= DAI_MANAGER;
-
+ } else {
+ u32 mod = readl(i2s->addr + I2SMOD);
+ i2s->cdclk_out = !(mod & MOD_CDCLKCON);
+ other->cdclk_out = i2s->cdclk_out;
+ }
/* Reset any constraint on RFS and BFS */
i2s->rfs = 0;
i2s->bfs = 0;
@@ -920,11 +929,9 @@ static int i2s_suspend(struct snd_soc_dai *dai)
{
struct i2s_dai *i2s = to_info(dai);
- if (dai->active) {
- i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
- i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
- i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
- }
+ i2s->suspend_i2smod = readl(i2s->addr + I2SMOD);
+ i2s->suspend_i2scon = readl(i2s->addr + I2SCON);
+ i2s->suspend_i2spsr = readl(i2s->addr + I2SPSR);
return 0;
}
@@ -933,11 +940,9 @@ static int i2s_resume(struct snd_soc_dai *dai)
{
struct i2s_dai *i2s = to_info(dai);
- if (dai->active) {
- writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
- writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
- writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
- }
+ writel(i2s->suspend_i2scon, i2s->addr + I2SCON);
+ writel(i2s->suspend_i2smod, i2s->addr + I2SMOD);
+ writel(i2s->suspend_i2spsr, i2s->addr + I2SPSR);
return 0;
}
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index b43fdf0..80245b6 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -37,7 +37,7 @@ config SND_SOC_SH4_SIU
config SND_SOC_RCAR
tristate "R-Car series SRU/SCU/SSIU/SSI support"
select SND_SIMPLE_CARD
- select REGMAP
+ select REGMAP_MMIO
help
This option enables R-Car SUR/SCU/SSIU/SSI sound support
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 710a079..a57eb96 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -232,11 +232,7 @@ struct fsi_stream {
* these are for DMAEngine
*/
struct dma_chan *chan;
- struct work_struct work;
- dma_addr_t dma;
int dma_id;
- int loop_cnt;
- int additional_pos;
};
struct fsi_clk {
@@ -1042,6 +1038,26 @@ static int fsi_clk_set_rate_cpg(struct device *dev,
return ret;
}
+static void fsi_pointer_update(struct fsi_stream *io, int size)
+{
+ io->buff_sample_pos += size;
+
+ if (io->buff_sample_pos >=
+ io->period_samples * (io->period_pos + 1)) {
+ struct snd_pcm_substream *substream = io->substream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ io->period_pos++;
+
+ if (io->period_pos >= runtime->periods) {
+ io->buff_sample_pos = 0;
+ io->period_pos = 0;
+ }
+
+ snd_pcm_period_elapsed(substream);
+ }
+}
+
/*
* pio data transfer handler
*/
@@ -1108,31 +1124,11 @@ static int fsi_pio_transfer(struct fsi_priv *fsi, struct fsi_stream *io,
void (*run32)(struct fsi_priv *fsi, u8 *buf, int samples),
int samples)
{
- struct snd_pcm_runtime *runtime;
- struct snd_pcm_substream *substream;
u8 *buf;
- int over_period;
if (!fsi_stream_is_working(fsi, io))
return -EINVAL;
- over_period = 0;
- substream = io->substream;
- runtime = substream->runtime;
-
- /* FSI FIFO has limit.
- * So, this driver can not send periods data at a time
- */
- if (io->buff_sample_pos >=
- io->period_samples * (io->period_pos + 1)) {
-
- over_period = 1;
- io->period_pos = (io->period_pos + 1) % runtime->periods;
-
- if (0 == io->period_pos)
- io->buff_sample_pos = 0;
- }
-
buf = fsi_pio_get_area(fsi, io);
switch (io->sample_width) {
@@ -1146,11 +1142,7 @@ static int fsi_pio_transfer(struct fsi_priv *fsi, struct fsi_stream *io,
return -EINVAL;
}
- /* update buff_sample_pos */
- io->buff_sample_pos += samples;
-
- if (over_period)
- snd_pcm_period_elapsed(substream);
+ fsi_pointer_update(io, samples);
return 0;
}
@@ -1279,11 +1271,6 @@ static irqreturn_t fsi_interrupt(int irq, void *data)
*/
static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
{
- struct snd_pcm_runtime *runtime = io->substream->runtime;
- struct snd_soc_dai *dai = fsi_get_dai(io->substream);
- enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE;
-
/*
* 24bit data : 24bit bus / package in back
* 16bit data : 16bit bus / stream mode
@@ -1291,107 +1278,48 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
- io->loop_cnt = 2; /* push 1st, 2nd period first, then 3rd, 4th... */
- io->additional_pos = 0;
- io->dma = dma_map_single(dai->dev, runtime->dma_area,
- snd_pcm_lib_buffer_bytes(io->substream), dir);
return 0;
}
-static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
-{
- struct snd_soc_dai *dai = fsi_get_dai(io->substream);
- enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE;
-
- dma_unmap_single(dai->dev, io->dma,
- snd_pcm_lib_buffer_bytes(io->substream), dir);
- return 0;
-}
-
-static dma_addr_t fsi_dma_get_area(struct fsi_stream *io, int additional)
-{
- struct snd_pcm_runtime *runtime = io->substream->runtime;
- int period = io->period_pos + additional;
-
- if (period >= runtime->periods)
- period = 0;
-
- return io->dma + samples_to_bytes(runtime, period * io->period_samples);
-}
-
static void fsi_dma_complete(void *data)
{
struct fsi_stream *io = (struct fsi_stream *)data;
struct fsi_priv *fsi = fsi_stream_to_priv(io);
- struct snd_pcm_runtime *runtime = io->substream->runtime;
- struct snd_soc_dai *dai = fsi_get_dai(io->substream);
- enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
- DMA_TO_DEVICE : DMA_FROM_DEVICE;
- dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io, 0),
- samples_to_bytes(runtime, io->period_samples), dir);
-
- io->buff_sample_pos += io->period_samples;
- io->period_pos++;
-
- if (io->period_pos >= runtime->periods) {
- io->period_pos = 0;
- io->buff_sample_pos = 0;
- }
+ fsi_pointer_update(io, io->period_samples);
fsi_count_fifo_err(fsi);
- fsi_stream_transfer(io);
-
- snd_pcm_period_elapsed(io->substream);
}
-static void fsi_dma_do_work(struct work_struct *work)
+static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
{
- struct fsi_stream *io = container_of(work, struct fsi_stream, work);
- struct fsi_priv *fsi = fsi_stream_to_priv(io);
- struct snd_soc_dai *dai;
+ struct snd_soc_dai *dai = fsi_get_dai(io->substream);
+ struct snd_pcm_substream *substream = io->substream;
struct dma_async_tx_descriptor *desc;
- struct snd_pcm_runtime *runtime;
- enum dma_data_direction dir;
int is_play = fsi_stream_is_play(fsi, io);
- int len, i;
- dma_addr_t buf;
-
- if (!fsi_stream_is_working(fsi, io))
- return;
-
- dai = fsi_get_dai(io->substream);
- runtime = io->substream->runtime;
- dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
- len = samples_to_bytes(runtime, io->period_samples);
-
- for (i = 0; i < io->loop_cnt; i++) {
- buf = fsi_dma_get_area(io, io->additional_pos);
-
- dma_sync_single_for_device(dai->dev, buf, len, dir);
-
- desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc) {
- dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
- return;
- }
-
- desc->callback = fsi_dma_complete;
- desc->callback_param = io;
-
- if (dmaengine_submit(desc) < 0) {
- dev_err(dai->dev, "tx_submit() fail\n");
- return;
- }
+ enum dma_data_direction dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+ int ret = -EIO;
+
+ desc = dmaengine_prep_dma_cyclic(io->chan,
+ substream->runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!desc) {
+ dev_err(dai->dev, "dmaengine_prep_dma_cyclic() fail\n");
+ goto fsi_dma_transfer_err;
+ }
- dma_async_issue_pending(io->chan);
+ desc->callback = fsi_dma_complete;
+ desc->callback_param = io;
- io->additional_pos = 1;
+ if (dmaengine_submit(desc) < 0) {
+ dev_err(dai->dev, "tx_submit() fail\n");
+ goto fsi_dma_transfer_err;
}
- io->loop_cnt = 1;
+ dma_async_issue_pending(io->chan);
/*
* FIXME
@@ -1408,13 +1336,11 @@ static void fsi_dma_do_work(struct work_struct *work)
fsi_reg_write(fsi, DIFF_ST, 0);
}
}
-}
-static int fsi_dma_transfer(struct fsi_priv *fsi, struct fsi_stream *io)
-{
- schedule_work(&io->work);
+ ret = 0;
- return 0;
+fsi_dma_transfer_err:
+ return ret;
}
static int fsi_dma_push_start_stop(struct fsi_priv *fsi, struct fsi_stream *io,
@@ -1475,15 +1401,11 @@ static int fsi_dma_probe(struct fsi_priv *fsi, struct fsi_stream *io, struct dev
return fsi_stream_probe(fsi, dev);
}
- INIT_WORK(&io->work, fsi_dma_do_work);
-
return 0;
}
static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io)
{
- cancel_work_sync(&io->work);
-
fsi_stream_stop(fsi, io);
if (io->chan)
@@ -1495,7 +1417,6 @@ static int fsi_dma_remove(struct fsi_priv *fsi, struct fsi_stream *io)
static struct fsi_stream_handler fsi_dma_push_handler = {
.init = fsi_dma_init,
- .quit = fsi_dma_quit,
.probe = fsi_dma_probe,
.transfer = fsi_dma_transfer,
.remove = fsi_dma_remove,
@@ -1657,9 +1578,9 @@ static int fsi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
if (!ret)
ret = fsi_hw_startup(fsi, io, dai->dev);
if (!ret)
- ret = fsi_stream_transfer(io);
+ ret = fsi_stream_start(fsi, io);
if (!ret)
- fsi_stream_start(fsi, io);
+ ret = fsi_stream_transfer(io);
break;
case SNDRV_PCM_TRIGGER_STOP:
if (!ret)
@@ -1850,16 +1771,10 @@ static void fsi_pcm_free(struct snd_pcm *pcm)
static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_pcm *pcm = rtd->pcm;
-
- /*
- * dont use SNDRV_DMA_TYPE_DEV, since it will oops the SH kernel
- * in MMAP mode (i.e. aplay -M)
- */
return snd_pcm_lib_preallocate_pages_for_all(
- pcm,
- SNDRV_DMA_TYPE_CONTINUOUS,
- snd_dma_continuous_data(GFP_KERNEL),
+ rtd->pcm,
+ SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 9188015..19f7896 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -138,6 +138,17 @@ char *rsnd_mod_name(struct rsnd_mod *mod)
return mod->ops->name;
}
+char *rsnd_mod_dma_name(struct rsnd_mod *mod)
+{
+ if (!mod || !mod->ops)
+ return "unknown";
+
+ if (!mod->ops->dma_name)
+ return mod->ops->name;
+
+ return mod->ops->dma_name(mod);
+}
+
void rsnd_mod_init(struct rsnd_priv *priv,
struct rsnd_mod *mod,
struct rsnd_mod_ops *ops,
@@ -153,26 +164,8 @@ void rsnd_mod_init(struct rsnd_priv *priv,
/*
* rsnd_dma functions
*/
-static void __rsnd_dma_start(struct rsnd_dma *dma);
-static void rsnd_dma_continue(struct rsnd_dma *dma)
-{
- /* push next A or B plane */
- dma->submit_loop = 1;
- schedule_work(&dma->work);
-}
-
-void rsnd_dma_start(struct rsnd_dma *dma)
-{
- /* push both A and B plane*/
- dma->offset = 0;
- dma->submit_loop = 2;
- __rsnd_dma_start(dma);
-}
-
void rsnd_dma_stop(struct rsnd_dma *dma)
{
- dma->submit_loop = 0;
- cancel_work_sync(&dma->work);
dmaengine_terminate_all(dma->chan);
}
@@ -180,11 +173,7 @@ static void rsnd_dma_complete(void *data)
{
struct rsnd_dma *dma = (struct rsnd_dma *)data;
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
- struct rsnd_priv *priv = rsnd_mod_to_priv(rsnd_dma_to_mod(dma));
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- unsigned long flags;
-
- rsnd_lock(priv, flags);
/*
* Renesas sound Gen1 needs 1 DMAC,
@@ -197,57 +186,41 @@ static void rsnd_dma_complete(void *data)
* rsnd_dai_pointer_update() will be called twice,
* ant it will breaks io->byte_pos
*/
- if (dma->submit_loop)
- rsnd_dma_continue(dma);
-
- rsnd_unlock(priv, flags);
rsnd_dai_pointer_update(io, io->byte_per_period);
}
-static void __rsnd_dma_start(struct rsnd_dma *dma)
+void rsnd_dma_start(struct rsnd_dma *dma)
{
struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct snd_pcm_substream *substream = io->substream;
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_async_tx_descriptor *desc;
- dma_addr_t buf;
- size_t len = io->byte_per_period;
- int i;
- for (i = 0; i < dma->submit_loop; i++) {
-
- buf = runtime->dma_addr +
- rsnd_dai_pointer_offset(io, dma->offset + len);
- dma->offset = len;
-
- desc = dmaengine_prep_slave_single(
- dma->chan, buf, len, dma->dir,
- DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- if (!desc) {
- dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
- return;
- }
+ desc = dmaengine_prep_dma_cyclic(dma->chan,
+ (dma->addr) ? dma->addr :
+ substream->runtime->dma_addr,
+ snd_pcm_lib_buffer_bytes(substream),
+ snd_pcm_lib_period_bytes(substream),
+ dma->dir,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
- desc->callback = rsnd_dma_complete;
- desc->callback_param = dma;
+ if (!desc) {
+ dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
+ return;
+ }
- if (dmaengine_submit(desc) < 0) {
- dev_err(dev, "dmaengine_submit() fail\n");
- return;
- }
+ desc->callback = rsnd_dma_complete;
+ desc->callback_param = dma;
- dma_async_issue_pending(dma->chan);
+ if (dmaengine_submit(desc) < 0) {
+ dev_err(dev, "dmaengine_submit() fail\n");
+ return;
}
-}
-
-static void rsnd_dma_do_work(struct work_struct *work)
-{
- struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work);
- __rsnd_dma_start(dma);
+ dma_async_issue_pending(dma->chan);
}
int rsnd_dma_available(struct rsnd_dma *dma)
@@ -261,14 +234,27 @@ static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod)
{
if (mod)
return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d",
- rsnd_mod_name(mod), rsnd_mod_id(mod));
+ rsnd_mod_dma_name(mod), rsnd_mod_id(mod));
else
return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem");
}
-static void rsnd_dma_of_name(struct rsnd_dma *dma,
- int is_play, char *dma_name)
+static void rsnd_dma_of_name(struct rsnd_mod *mod_from,
+ struct rsnd_mod *mod_to,
+ char *dma_name)
+{
+ int index = 0;
+
+ index = _rsnd_dma_of_name(dma_name + index, mod_from);
+ *(dma_name + index++) = '_';
+ index = _rsnd_dma_of_name(dma_name + index, mod_to);
+}
+
+static void rsnd_dma_of_path(struct rsnd_dma *dma,
+ int is_play,
+ struct rsnd_mod **mod_from,
+ struct rsnd_mod **mod_to)
{
struct rsnd_mod *this = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(this);
@@ -276,7 +262,6 @@ static void rsnd_dma_of_name(struct rsnd_dma *dma,
struct rsnd_mod *src = rsnd_io_to_mod_src(io);
struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
struct rsnd_mod *mod[MOD_MAX];
- struct rsnd_mod *src_mod, *dst_mod;
int i, index;
@@ -297,31 +282,34 @@ static void rsnd_dma_of_name(struct rsnd_dma *dma,
for (i = 1; i < MOD_MAX; i++) {
if (!src) {
mod[i] = ssi;
- break;
} else if (!dvc) {
mod[i] = src;
src = NULL;
} else {
- mod[i] = dvc;
+ if ((!is_play) && (this == src))
+ this = dvc;
+
+ mod[i] = (is_play) ? src : dvc;
+ i++;
+ mod[i] = (is_play) ? dvc : src;
+ src = NULL;
dvc = NULL;
}
if (mod[i] == this)
index = i;
+
+ if (mod[i] == ssi)
+ break;
}
if (is_play) {
- src_mod = mod[index - 1];
- dst_mod = mod[index];
+ *mod_from = mod[index - 1];
+ *mod_to = mod[index];
} else {
- src_mod = mod[index];
- dst_mod = mod[index + 1];
+ *mod_from = mod[index];
+ *mod_to = mod[index - 1];
}
-
- index = 0;
- index = _rsnd_dma_of_name(dma_name + index, src_mod);
- *(dma_name + index++) = '_';
- index = _rsnd_dma_of_name(dma_name + index, dst_mod);
}
int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
@@ -329,6 +317,8 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
{
struct device *dev = rsnd_priv_to_dev(priv);
struct dma_slave_config cfg;
+ struct rsnd_mod *mod_from;
+ struct rsnd_mod *mod_to;
char dma_name[DMA_NAME_SIZE];
dma_cap_mask_t mask;
int ret;
@@ -341,13 +331,18 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- if (dev->of_node)
- rsnd_dma_of_name(dma, is_play, dma_name);
- else
- snprintf(dma_name, DMA_NAME_SIZE,
- is_play ? "tx" : "rx");
+ rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to);
+ rsnd_dma_of_name(mod_from, mod_to, dma_name);
- dev_dbg(dev, "dma name : %s\n", dma_name);
+ cfg.slave_id = id;
+ cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1);
+ cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0);
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+ dev_dbg(dev, "dma : %s %pad -> %pad\n",
+ dma_name, &cfg.src_addr, &cfg.dst_addr);
dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter,
(void *)id, dev,
@@ -357,14 +352,12 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
return -EIO;
}
- rsnd_gen_dma_addr(priv, dma, &cfg, is_play, id);
-
ret = dmaengine_slave_config(dma->chan, &cfg);
if (ret < 0)
goto rsnd_dma_init_err;
- dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
- INIT_WORK(&dma->work, rsnd_dma_do_work);
+ dma->addr = is_play ? cfg.src_addr : cfg.dst_addr;
+ dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
return 0;
@@ -631,40 +624,41 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return -EINVAL;
}
- /* set clock inversion */
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_IF:
- rdai->bit_clk_inv = 0;
- rdai->frm_clk_inv = 1;
- break;
- case SND_SOC_DAIFMT_IB_NF:
- rdai->bit_clk_inv = 1;
- rdai->frm_clk_inv = 0;
- break;
- case SND_SOC_DAIFMT_IB_IF:
- rdai->bit_clk_inv = 1;
- rdai->frm_clk_inv = 1;
- break;
- case SND_SOC_DAIFMT_NB_NF:
- default:
- rdai->bit_clk_inv = 0;
- rdai->frm_clk_inv = 0;
- break;
- }
-
/* set format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
rdai->sys_delay = 0;
rdai->data_alignment = 0;
+ rdai->frm_clk_inv = 0;
break;
case SND_SOC_DAIFMT_LEFT_J:
rdai->sys_delay = 1;
rdai->data_alignment = 0;
+ rdai->frm_clk_inv = 1;
break;
case SND_SOC_DAIFMT_RIGHT_J:
rdai->sys_delay = 1;
rdai->data_alignment = 1;
+ rdai->frm_clk_inv = 1;
+ break;
+ }
+
+ /* set clock inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ rdai->bit_clk_inv = rdai->bit_clk_inv;
+ rdai->frm_clk_inv = !rdai->frm_clk_inv;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ rdai->bit_clk_inv = !rdai->bit_clk_inv;
+ rdai->frm_clk_inv = rdai->frm_clk_inv;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ rdai->bit_clk_inv = !rdai->bit_clk_inv;
+ rdai->frm_clk_inv = !rdai->frm_clk_inv;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ default:
break;
}
@@ -734,12 +728,13 @@ static void rsnd_of_parse_dai(struct platform_device *pdev,
struct device_node *dai_node, *dai_np;
struct device_node *ssi_node, *ssi_np;
struct device_node *src_node, *src_np;
+ struct device_node *dvc_node, *dvc_np;
struct device_node *playback, *capture;
struct rsnd_dai_platform_info *dai_info;
struct rcar_snd_info *info = rsnd_priv_to_info(priv);
struct device *dev = &pdev->dev;
int nr, i;
- int dai_i, ssi_i, src_i;
+ int dai_i, ssi_i, src_i, dvc_i;
if (!of_data)
return;
@@ -765,6 +760,7 @@ static void rsnd_of_parse_dai(struct platform_device *pdev,
ssi_node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi");
src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src");
+ dvc_node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
#define mod_parse(name) \
if (name##_node) { \
@@ -800,6 +796,7 @@ if (name##_node) { \
mod_parse(ssi);
mod_parse(src);
+ mod_parse(dvc);
if (playback)
of_node_put(playback);
@@ -948,19 +945,17 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
- struct rsnd_priv *priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
- struct rsnd_dai *rdai;
- int i, ret;
+ struct snd_soc_dai *dai = rtd->cpu_dai;
+ struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+ int ret;
- for_each_rsnd_dai(rdai, priv, i) {
- ret = rsnd_dai_call(pcm_new, &rdai->playback, rdai, rtd);
- if (ret)
- return ret;
+ ret = rsnd_dai_call(pcm_new, &rdai->playback, rdai, rtd);
+ if (ret)
+ return ret;
- ret = rsnd_dai_call(pcm_new, &rdai->capture, rdai, rtd);
- if (ret)
- return ret;
- }
+ ret = rsnd_dai_call(pcm_new, &rdai->capture, rdai, rtd);
+ if (ret)
+ return ret;
return snd_pcm_lib_preallocate_pages_for_all(
rtd->pcm,
@@ -1047,11 +1042,11 @@ static int rsnd_probe(struct platform_device *pdev)
for_each_rsnd_dai(rdai, priv, i) {
ret = rsnd_dai_call(probe, &rdai->playback, rdai);
if (ret)
- return ret;
+ goto exit_snd_probe;
ret = rsnd_dai_call(probe, &rdai->capture, rdai);
if (ret)
- return ret;
+ goto exit_snd_probe;
}
/*
@@ -1079,6 +1074,11 @@ static int rsnd_probe(struct platform_device *pdev)
exit_snd_soc:
snd_soc_unregister_platform(dev);
+exit_snd_probe:
+ for_each_rsnd_dai(rdai, priv, i) {
+ rsnd_dai_call(remove, &rdai->playback, rdai);
+ rsnd_dai_call(remove, &rdai->capture, rdai);
+ }
return ret;
}
@@ -1087,21 +1087,16 @@ static int rsnd_remove(struct platform_device *pdev)
{
struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
struct rsnd_dai *rdai;
- int ret, i;
+ int ret = 0, i;
pm_runtime_disable(&pdev->dev);
for_each_rsnd_dai(rdai, priv, i) {
- ret = rsnd_dai_call(remove, &rdai->playback, rdai);
- if (ret)
- return ret;
-
- ret = rsnd_dai_call(remove, &rdai->capture, rdai);
- if (ret)
- return ret;
+ ret |= rsnd_dai_call(remove, &rdai->playback, rdai);
+ ret |= rsnd_dai_call(remove, &rdai->capture, rdai);
}
- return 0;
+ return ret;
}
static struct platform_driver rsnd_driver = {
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index ed00070..3f44393 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -20,7 +20,8 @@ struct rsnd_dvc {
struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
struct rsnd_mod mod;
struct clk *clk;
- long volume[RSND_DVC_VOLUME_NUM];
+ u8 volume[RSND_DVC_VOLUME_NUM];
+ u8 mute[RSND_DVC_VOLUME_NUM];
};
#define rsnd_mod_to_dvc(_mod) \
@@ -37,13 +38,18 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
u32 max = (0x00800000 - 1);
u32 vol[RSND_DVC_VOLUME_NUM];
+ u32 mute = 0;
int i;
- for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
+ for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
+ mute |= (!!dvc->mute[i]) << i;
+ }
rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+
+ rsnd_mod_write(mod, DVC_ZCMCR, mute);
}
static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
@@ -96,8 +102,8 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
- /* enable Volume */
- rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x100);
+ /* enable Volume / Mute */
+ rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
/* ch0/ch1 Volume */
rsnd_dvc_volume_update(dvc_mod);
@@ -140,10 +146,20 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_info *uinfo)
{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+ struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+ u8 *val = (u8 *)kctrl->private_value;
+
uinfo->count = RSND_DVC_VOLUME_NUM;
uinfo->value.integer.min = 0;
- uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
+
+ if (val == dvc->volume) {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
+ } else {
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ uinfo->value.integer.max = 1;
+ }
return 0;
}
@@ -151,12 +167,11 @@ static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
{
- struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
- struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+ u8 *val = (u8 *)kctrl->private_value;
int i;
for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
- ucontrol->value.integer.value[i] = dvc->volume[i];
+ ucontrol->value.integer.value[i] = val[i];
return 0;
}
@@ -165,51 +180,38 @@ static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
struct snd_ctl_elem_value *ucontrol)
{
struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
- struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+ u8 *val = (u8 *)kctrl->private_value;
int i, change = 0;
for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
- if (ucontrol->value.integer.value[i] < 0 ||
- ucontrol->value.integer.value[i] > RSND_DVC_VOLUME_MAX)
- return -EINVAL;
-
- change |= (ucontrol->value.integer.value[i] != dvc->volume[i]);
+ change |= (ucontrol->value.integer.value[i] != val[i]);
+ val[i] = ucontrol->value.integer.value[i];
}
- if (change) {
- for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
- dvc->volume[i] = ucontrol->value.integer.value[i];
-
+ if (change)
rsnd_dvc_volume_update(mod);
- }
return change;
}
-static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
- struct rsnd_dai *rdai,
- struct snd_soc_pcm_runtime *rtd)
+static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ u8 *private)
{
- struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
- struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
- struct device *dev = rsnd_priv_to_dev(priv);
struct snd_card *card = rtd->card->snd_card;
struct snd_kcontrol *kctrl;
- static struct snd_kcontrol_new knew = {
+ struct snd_kcontrol_new knew = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Playback Volume",
+ .name = name,
.info = rsnd_dvc_volume_info,
.get = rsnd_dvc_volume_get,
.put = rsnd_dvc_volume_put,
+ .private_value = (unsigned long)private,
};
int ret;
- if (!rsnd_dai_is_play(rdai, io)) {
- dev_err(dev, "DVC%d is connected to Capture DAI\n",
- rsnd_mod_id(mod));
- return -EINVAL;
- }
-
kctrl = snd_ctl_new1(&knew, mod);
if (!kctrl)
return -ENOMEM;
@@ -221,6 +223,33 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
return 0;
}
+static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
+ struct rsnd_dai *rdai,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
+ int ret;
+
+ /* Volume */
+ ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+ rsnd_dai_is_play(rdai, io) ?
+ "DVC Out Playback Volume" : "DVC In Capture Volume",
+ dvc->volume);
+ if (ret < 0)
+ return ret;
+
+ /* Mute */
+ ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+ rsnd_dai_is_play(rdai, io) ?
+ "DVC Out Mute Switch" : "DVC In Mute Switch",
+ dvc->mute);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static struct rsnd_mod_ops rsnd_dvc_ops = {
.name = DVC_NAME,
.probe = rsnd_dvc_probe_gen2,
@@ -239,6 +268,42 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
}
+static void rsnd_of_parse_dvc(struct platform_device *pdev,
+ const struct rsnd_of_data *of_data,
+ struct rsnd_priv *priv)
+{
+ struct device_node *node;
+ struct rsnd_dvc_platform_info *dvc_info;
+ struct rcar_snd_info *info = rsnd_priv_to_info(priv);
+ struct device *dev = &pdev->dev;
+ int nr;
+
+ if (!of_data)
+ return;
+
+ node = of_get_child_by_name(dev->of_node, "rcar_sound,dvc");
+ if (!node)
+ return;
+
+ nr = of_get_child_count(node);
+ if (!nr)
+ goto rsnd_of_parse_dvc_end;
+
+ dvc_info = devm_kzalloc(dev,
+ sizeof(struct rsnd_dvc_platform_info) * nr,
+ GFP_KERNEL);
+ if (!dvc_info) {
+ dev_err(dev, "dvc info allocation error\n");
+ goto rsnd_of_parse_dvc_end;
+ }
+
+ info->dvc_info = dvc_info;
+ info->dvc_info_nr = nr;
+
+rsnd_of_parse_dvc_end:
+ of_node_put(node);
+}
+
int rsnd_dvc_probe(struct platform_device *pdev,
const struct rsnd_of_data *of_data,
struct rsnd_priv *priv)
@@ -250,6 +315,8 @@ int rsnd_dvc_probe(struct platform_device *pdev,
char name[RSND_DVC_NAME_SIZE];
int i, nr;
+ rsnd_of_parse_dvc(pdev, of_data, priv);
+
nr = info->dvc_info_nr;
if (!nr)
return 0;
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 1dd2b7d..3fdf3be 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -15,63 +15,35 @@ struct rsnd_gen {
struct rsnd_gen_ops *ops;
- struct regmap *regmap;
+ struct regmap *regmap[RSND_BASE_MAX];
struct regmap_field *regs[RSND_REG_MAX];
};
#define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen)
-#define RSND_REG_SET(gen, id, reg_id, offset, _id_offset, _id_size) \
- [id] = { \
- .reg = (unsigned int)gen->base[reg_id] + offset, \
- .lsb = 0, \
- .msb = 31, \
- .id_size = _id_size, \
- .id_offset = _id_offset, \
- }
-
-/*
- * basic function
- */
-static int rsnd_regmap_write32(void *context, const void *_data, size_t count)
-{
- struct rsnd_priv *priv = context;
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 *data = (u32 *)_data;
- u32 val = data[1];
- void __iomem *reg = (void *)data[0];
-
- iowrite32(val, reg);
-
- dev_dbg(dev, "w %p : %08x\n", reg, val);
-
- return 0;
-}
-
-static int rsnd_regmap_read32(void *context,
- const void *_data, size_t reg_size,
- void *_val, size_t val_size)
-{
- struct rsnd_priv *priv = context;
- struct device *dev = rsnd_priv_to_dev(priv);
- u32 *data = (u32 *)_data;
- u32 *val = (u32 *)_val;
- void __iomem *reg = (void *)data[0];
-
- *val = ioread32(reg);
-
- dev_dbg(dev, "r %p : %08x\n", reg, *val);
+struct rsnd_regmap_field_conf {
+ int idx;
+ unsigned int reg_offset;
+ unsigned int id_offset;
+};
- return 0;
+#define RSND_REG_SET(id, offset, _id_offset) \
+{ \
+ .idx = id, \
+ .reg_offset = offset, \
+ .id_offset = _id_offset, \
}
+/* single address mapping */
+#define RSND_GEN_S_REG(id, offset) \
+ RSND_REG_SET(RSND_REG_##id, offset, 0)
-static struct regmap_bus rsnd_regmap_bus = {
- .write = rsnd_regmap_write32,
- .read = rsnd_regmap_read32,
- .reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
- .val_format_endian_default = REGMAP_ENDIAN_NATIVE,
-};
+/* multi address mapping */
+#define RSND_GEN_M_REG(id, offset, _id_offset) \
+ RSND_REG_SET(RSND_REG_##id, offset, _id_offset)
+/*
+ * basic function
+ */
static int rsnd_is_accessible_reg(struct rsnd_priv *priv,
struct rsnd_gen *gen, enum rsnd_reg reg)
{
@@ -88,6 +60,7 @@ static int rsnd_is_accessible_reg(struct rsnd_priv *priv,
u32 rsnd_read(struct rsnd_priv *priv,
struct rsnd_mod *mod, enum rsnd_reg reg)
{
+ struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
u32 val;
@@ -96,6 +69,8 @@ u32 rsnd_read(struct rsnd_priv *priv,
regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+ dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
+
return val;
}
@@ -103,17 +78,21 @@ void rsnd_write(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg, u32 data)
{
+ struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
if (!rsnd_is_accessible_reg(priv, gen, reg))
return;
regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
+
+ dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
}
void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
enum rsnd_reg reg, u32 mask, u32 data)
{
+ struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
if (!rsnd_is_accessible_reg(priv, gen, reg))
@@ -121,35 +100,63 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
mask, data);
+
+ dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
+ rsnd_mod_name(mod), reg, data, mask);
}
-static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
- struct rsnd_gen *gen,
- struct reg_field *regf)
+#define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \
+ _rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf))
+static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
+ int id_size,
+ int reg_id,
+ struct rsnd_regmap_field_conf *conf,
+ int conf_size)
{
- int i;
+ struct platform_device *pdev = rsnd_priv_to_pdev(priv);
+ struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
struct device *dev = rsnd_priv_to_dev(priv);
+ struct resource *res;
struct regmap_config regc;
+ struct regmap_field *regs;
+ struct regmap *regmap;
+ struct reg_field regf;
+ void __iomem *base;
+ int i;
memset(&regc, 0, sizeof(regc));
regc.reg_bits = 32;
regc.val_bits = 32;
+ regc.reg_stride = 4;
- gen->regmap = devm_regmap_init(dev, &rsnd_regmap_bus, priv, &regc);
- if (IS_ERR(gen->regmap)) {
- dev_err(dev, "regmap error %ld\n", PTR_ERR(gen->regmap));
- return PTR_ERR(gen->regmap);
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id);
+ if (!res)
+ return -ENODEV;
- for (i = 0; i < RSND_REG_MAX; i++) {
- gen->regs[i] = NULL;
- if (!regf[i].reg)
- continue;
+ base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
- gen->regs[i] = devm_regmap_field_alloc(dev, gen->regmap, regf[i]);
- if (IS_ERR(gen->regs[i]))
- return PTR_ERR(gen->regs[i]);
+ regmap = devm_regmap_init_mmio(dev, base, &regc);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ gen->base[reg_id] = base;
+ gen->regmap[reg_id] = regmap;
+
+ for (i = 0; i < conf_size; i++) {
+
+ regf.reg = conf[i].reg_offset;
+ regf.id_offset = conf[i].id_offset;
+ regf.lsb = 0;
+ regf.msb = 31;
+ regf.id_size = id_size;
+
+ regs = devm_regmap_field_alloc(dev, regmap, regf);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ gen->regs[conf[i].idx] = regs;
}
return 0;
@@ -165,15 +172,19 @@ static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
*
* ex) R-Car H2 case
* mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out
- * SSI : 0xec541000 / 0xec241008 / 0xec24100c / 0xec400000 / 0xec400000
+ * SSI : 0xec541000 / 0xec241008 / 0xec24100c
+ * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000
* SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000
- * CMD : 0xec500000 / 0xec008000 0xec308000
+ * CMD : 0xec500000 / / 0xec008000 0xec308000
*/
#define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8)
#define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc)
-#define RDMA_SSI_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
-#define RDMA_SSI_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
+#define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i))
+
+#define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
+#define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i))
#define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i))
#define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i))
@@ -184,14 +195,13 @@ static int rsnd_gen_regmap_init(struct rsnd_priv *priv,
#define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i))
#define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i))
-void rsnd_gen_dma_addr(struct rsnd_priv *priv,
- struct rsnd_dma *dma,
- struct dma_slave_config *cfg,
- int is_play, int slave_id)
+static dma_addr_t
+rsnd_gen2_dma_addr(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ int is_play, int is_from)
{
struct platform_device *pdev = rsnd_priv_to_pdev(priv);
struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_mod *mod = rsnd_dma_to_mod(dma);
struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
dma_addr_t ssi_reg = platform_get_resource(pdev,
IORESOURCE_MEM, RSND_GEN2_SSI)->start;
@@ -202,170 +212,152 @@ void rsnd_gen_dma_addr(struct rsnd_priv *priv,
int use_dvc = !!rsnd_io_to_mod_dvc(io);
int id = rsnd_mod_id(mod);
struct dma_addr {
- dma_addr_t src_addr;
- dma_addr_t dst_addr;
- } dma_addrs[2][2][3] = {
- { /* SRC */
- /* Capture */
- {{ 0, 0 },
- { RDMA_SRC_O_N(src, id), 0 },
- { RDMA_CMD_O_N(src, id), 0 }},
- /* Playback */
- {{ 0, 0, },
- { 0, RDMA_SRC_I_N(src, id) },
- { 0, RDMA_SRC_I_N(src, id) }}
- }, { /* SSI */
- /* Capture */
- {{ RDMA_SSI_O_N(ssi, id), 0 },
- { RDMA_SSI_O_P(ssi, id), RDMA_SRC_I_P(src, id) },
- { RDMA_SSI_O_P(ssi, id), RDMA_SRC_I_P(src, id) }},
- /* Playback */
- {{ 0, RDMA_SSI_I_N(ssi, id) },
- { RDMA_SRC_O_P(src, id), RDMA_SSI_I_P(ssi, id) },
- { RDMA_CMD_O_P(src, id), RDMA_SSI_I_P(ssi, id) }}
- }
+ dma_addr_t out_addr;
+ dma_addr_t in_addr;
+ } dma_addrs[3][2][3] = {
+ /* SRC */
+ {{{ 0, 0 },
+ /* Capture */
+ { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) },
+ { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } },
+ /* Playback */
+ {{ 0, 0, },
+ { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) },
+ { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } }
+ },
+ /* SSI */
+ /* Capture */
+ {{{ RDMA_SSI_O_N(ssi, id), 0 },
+ { RDMA_SSIU_O_P(ssi, id), 0 },
+ { RDMA_SSIU_O_P(ssi, id), 0 } },
+ /* Playback */
+ {{ 0, RDMA_SSI_I_N(ssi, id) },
+ { 0, RDMA_SSIU_I_P(ssi, id) },
+ { 0, RDMA_SSIU_I_P(ssi, id) } }
+ },
+ /* SSIU */
+ /* Capture */
+ {{{ RDMA_SSIU_O_N(ssi, id), 0 },
+ { RDMA_SSIU_O_P(ssi, id), 0 },
+ { RDMA_SSIU_O_P(ssi, id), 0 } },
+ /* Playback */
+ {{ 0, RDMA_SSIU_I_N(ssi, id) },
+ { 0, RDMA_SSIU_I_P(ssi, id) },
+ { 0, RDMA_SSIU_I_P(ssi, id) } } },
};
- cfg->slave_id = slave_id;
- cfg->src_addr = 0;
- cfg->dst_addr = 0;
- cfg->direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM;
+ /* it shouldn't happen */
+ if (use_dvc & !use_src)
+ dev_err(dev, "DVC is selected without SRC\n");
+
+ /* use SSIU or SSI ? */
+ if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu")))
+ is_ssi++;
+
+ return (is_from) ?
+ dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr :
+ dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr;
+}
+dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ int is_play, int is_from)
+{
/*
* gen1 uses default DMA addr
*/
if (rsnd_is_gen1(priv))
- return;
-
- /* it shouldn't happen */
- if (use_dvc & !use_src) {
- dev_err(dev, "DVC is selected without SRC\n");
- return;
- }
+ return 0;
- cfg->src_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].src_addr;
- cfg->dst_addr = dma_addrs[is_ssi][is_play][use_src + use_dvc].dst_addr;
+ if (!mod)
+ return 0;
- dev_dbg(dev, "dma%d addr - src : %x / dst : %x\n",
- id, cfg->src_addr, cfg->dst_addr);
+ return rsnd_gen2_dma_addr(priv, mod, is_play, is_from);
}
/*
* Gen2
*/
-
-/* single address mapping */
-#define RSND_GEN2_S_REG(gen, reg, id, offset) \
- RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, 0, 10)
-
-/* multi address mapping */
-#define RSND_GEN2_M_REG(gen, reg, id, offset, _id_offset) \
- RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN2_##reg, offset, _id_offset, 10)
-
-static int rsnd_gen2_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
-{
- struct reg_field regf[RSND_REG_MAX] = {
- RSND_GEN2_S_REG(gen, SSIU, SSI_MODE0, 0x800),
- RSND_GEN2_S_REG(gen, SSIU, SSI_MODE1, 0x804),
- /* FIXME: it needs SSI_MODE2/3 in the future */
- RSND_GEN2_M_REG(gen, SSIU, SSI_BUSIF_MODE, 0x0, 0x80),
- RSND_GEN2_M_REG(gen, SSIU, SSI_BUSIF_ADINR,0x4, 0x80),
- RSND_GEN2_M_REG(gen, SSIU, SSI_CTRL, 0x10, 0x80),
- RSND_GEN2_M_REG(gen, SSIU, INT_ENABLE, 0x18, 0x80),
-
- RSND_GEN2_M_REG(gen, SCU, SRC_BUSIF_MODE, 0x0, 0x20),
- RSND_GEN2_M_REG(gen, SCU, SRC_ROUTE_MODE0,0xc, 0x20),
- RSND_GEN2_M_REG(gen, SCU, SRC_CTRL, 0x10, 0x20),
- RSND_GEN2_M_REG(gen, SCU, CMD_ROUTE_SLCT, 0x18c, 0x20),
- RSND_GEN2_M_REG(gen, SCU, CMD_CTRL, 0x190, 0x20),
- RSND_GEN2_M_REG(gen, SCU, SRC_SWRSR, 0x200, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_SRCIR, 0x204, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_ADINR, 0x214, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_IFSCR, 0x21c, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_IFSVR, 0x220, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_SRCCR, 0x224, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_BSDSR, 0x22c, 0x40),
- RSND_GEN2_M_REG(gen, SCU, SRC_BSISR, 0x238, 0x40),
- RSND_GEN2_M_REG(gen, SCU, DVC_SWRSR, 0xe00, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_DVUIR, 0xe04, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_ADINR, 0xe08, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_DVUCR, 0xe10, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_ZCMCR, 0xe14, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_VOL0R, 0xe28, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_VOL1R, 0xe2c, 0x100),
- RSND_GEN2_M_REG(gen, SCU, DVC_DVUER, 0xe48, 0x100),
-
- RSND_GEN2_S_REG(gen, ADG, BRRA, 0x00),
- RSND_GEN2_S_REG(gen, ADG, BRRB, 0x04),
- RSND_GEN2_S_REG(gen, ADG, SSICKR, 0x08),
- RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c),
- RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10),
- RSND_GEN2_S_REG(gen, ADG, AUDIO_CLK_SEL2, 0x14),
- RSND_GEN2_S_REG(gen, ADG, DIV_EN, 0x30),
- RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL0, 0x34),
- RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL1, 0x38),
- RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL2, 0x3c),
- RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL3, 0x40),
- RSND_GEN2_S_REG(gen, ADG, SRCIN_TIMSEL4, 0x44),
- RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL0, 0x48),
- RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL1, 0x4c),
- RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL2, 0x50),
- RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL3, 0x54),
- RSND_GEN2_S_REG(gen, ADG, SRCOUT_TIMSEL4, 0x58),
- RSND_GEN2_S_REG(gen, ADG, CMDOUT_TIMSEL, 0x5c),
-
- RSND_GEN2_M_REG(gen, SSI, SSICR, 0x00, 0x40),
- RSND_GEN2_M_REG(gen, SSI, SSISR, 0x04, 0x40),
- RSND_GEN2_M_REG(gen, SSI, SSITDR, 0x08, 0x40),
- RSND_GEN2_M_REG(gen, SSI, SSIRDR, 0x0c, 0x40),
- RSND_GEN2_M_REG(gen, SSI, SSIWSR, 0x20, 0x40),
- };
-
- return rsnd_gen_regmap_init(priv, gen, regf);
-}
-
static int rsnd_gen2_probe(struct platform_device *pdev,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
- struct resource *scu_res;
- struct resource *adg_res;
- struct resource *ssiu_res;
- struct resource *ssi_res;
- int ret;
-
- /*
- * map address
- */
- scu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SCU);
- adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_ADG);
- ssiu_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSIU);
- ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN2_SSI);
-
- gen->base[RSND_GEN2_SCU] = devm_ioremap_resource(dev, scu_res);
- gen->base[RSND_GEN2_ADG] = devm_ioremap_resource(dev, adg_res);
- gen->base[RSND_GEN2_SSIU] = devm_ioremap_resource(dev, ssiu_res);
- gen->base[RSND_GEN2_SSI] = devm_ioremap_resource(dev, ssi_res);
- if (IS_ERR(gen->base[RSND_GEN2_SCU]) ||
- IS_ERR(gen->base[RSND_GEN2_ADG]) ||
- IS_ERR(gen->base[RSND_GEN2_SSIU]) ||
- IS_ERR(gen->base[RSND_GEN2_SSI]))
- return -ENODEV;
-
- ret = rsnd_gen2_regmap_init(priv, gen);
- if (ret < 0)
- return ret;
-
- dev_dbg(dev, "Gen2 device probed\n");
- dev_dbg(dev, "SCU : %pap => %p\n", &scu_res->start,
- gen->base[RSND_GEN2_SCU]);
- dev_dbg(dev, "ADG : %pap => %p\n", &adg_res->start,
- gen->base[RSND_GEN2_ADG]);
- dev_dbg(dev, "SSIU : %pap => %p\n", &ssiu_res->start,
- gen->base[RSND_GEN2_SSIU]);
- dev_dbg(dev, "SSI : %pap => %p\n", &ssi_res->start,
- gen->base[RSND_GEN2_SSI]);
+ struct rsnd_regmap_field_conf conf_ssiu[] = {
+ RSND_GEN_S_REG(SSI_MODE0, 0x800),
+ RSND_GEN_S_REG(SSI_MODE1, 0x804),
+ /* FIXME: it needs SSI_MODE2/3 in the future */
+ RSND_GEN_M_REG(SSI_BUSIF_MODE, 0x0, 0x80),
+ RSND_GEN_M_REG(SSI_BUSIF_ADINR, 0x4, 0x80),
+ RSND_GEN_M_REG(BUSIF_DALIGN, 0x8, 0x80),
+ RSND_GEN_M_REG(SSI_CTRL, 0x10, 0x80),
+ RSND_GEN_M_REG(INT_ENABLE, 0x18, 0x80),
+ };
+ struct rsnd_regmap_field_conf conf_scu[] = {
+ RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x0, 0x20),
+ RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0xc, 0x20),
+ RSND_GEN_M_REG(SRC_CTRL, 0x10, 0x20),
+ RSND_GEN_M_REG(CMD_ROUTE_SLCT, 0x18c, 0x20),
+ RSND_GEN_M_REG(CMD_CTRL, 0x190, 0x20),
+ RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
+ RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
+ RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
+ RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40),
+ RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40),
+ RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
+ RSND_GEN_M_REG(SRC_BSDSR, 0x22c, 0x40),
+ RSND_GEN_M_REG(SRC_BSISR, 0x238, 0x40),
+ RSND_GEN_M_REG(DVC_SWRSR, 0xe00, 0x100),
+ RSND_GEN_M_REG(DVC_DVUIR, 0xe04, 0x100),
+ RSND_GEN_M_REG(DVC_ADINR, 0xe08, 0x100),
+ RSND_GEN_M_REG(DVC_DVUCR, 0xe10, 0x100),
+ RSND_GEN_M_REG(DVC_ZCMCR, 0xe14, 0x100),
+ RSND_GEN_M_REG(DVC_VOL0R, 0xe28, 0x100),
+ RSND_GEN_M_REG(DVC_VOL1R, 0xe2c, 0x100),
+ RSND_GEN_M_REG(DVC_DVUER, 0xe48, 0x100),
+ };
+ struct rsnd_regmap_field_conf conf_adg[] = {
+ RSND_GEN_S_REG(BRRA, 0x00),
+ RSND_GEN_S_REG(BRRB, 0x04),
+ RSND_GEN_S_REG(SSICKR, 0x08),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL2, 0x14),
+ RSND_GEN_S_REG(DIV_EN, 0x30),
+ RSND_GEN_S_REG(SRCIN_TIMSEL0, 0x34),
+ RSND_GEN_S_REG(SRCIN_TIMSEL1, 0x38),
+ RSND_GEN_S_REG(SRCIN_TIMSEL2, 0x3c),
+ RSND_GEN_S_REG(SRCIN_TIMSEL3, 0x40),
+ RSND_GEN_S_REG(SRCIN_TIMSEL4, 0x44),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL0, 0x48),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL1, 0x4c),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL2, 0x50),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL3, 0x54),
+ RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
+ RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
+ };
+ struct rsnd_regmap_field_conf conf_ssi[] = {
+ RSND_GEN_M_REG(SSICR, 0x00, 0x40),
+ RSND_GEN_M_REG(SSISR, 0x04, 0x40),
+ RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
+ RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
+ RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
+ };
+ int ret_ssiu;
+ int ret_scu;
+ int ret_adg;
+ int ret_ssi;
+
+ ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu);
+ ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, conf_scu);
+ ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, conf_adg);
+ ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, conf_ssi);
+ if (ret_ssiu < 0 ||
+ ret_scu < 0 ||
+ ret_adg < 0 ||
+ ret_ssi < 0)
+ return ret_ssiu | ret_scu | ret_adg | ret_ssi;
+
+ dev_dbg(dev, "Gen2 is probed\n");
return 0;
}
@@ -374,92 +366,60 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
* Gen1
*/
-/* single address mapping */
-#define RSND_GEN1_S_REG(gen, reg, id, offset) \
- RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, 0, 9)
-
-/* multi address mapping */
-#define RSND_GEN1_M_REG(gen, reg, id, offset, _id_offset) \
- RSND_REG_SET(gen, RSND_REG_##id, RSND_GEN1_##reg, offset, _id_offset, 9)
-
-static int rsnd_gen1_regmap_init(struct rsnd_priv *priv, struct rsnd_gen *gen)
-{
- struct reg_field regf[RSND_REG_MAX] = {
- RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_SEL, 0x00),
- RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL0, 0x08),
- RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL1, 0x0c),
- RSND_GEN1_S_REG(gen, SRU, SRC_TMG_SEL2, 0x10),
- RSND_GEN1_S_REG(gen, SRU, SRC_ROUTE_CTRL, 0xc0),
- RSND_GEN1_S_REG(gen, SRU, SSI_MODE0, 0xD0),
- RSND_GEN1_S_REG(gen, SRU, SSI_MODE1, 0xD4),
- RSND_GEN1_M_REG(gen, SRU, SRC_BUSIF_MODE, 0x20, 0x4),
- RSND_GEN1_M_REG(gen, SRU, SRC_ROUTE_MODE0,0x50, 0x8),
- RSND_GEN1_M_REG(gen, SRU, SRC_SWRSR, 0x200, 0x40),
- RSND_GEN1_M_REG(gen, SRU, SRC_SRCIR, 0x204, 0x40),
- RSND_GEN1_M_REG(gen, SRU, SRC_ADINR, 0x214, 0x40),
- RSND_GEN1_M_REG(gen, SRU, SRC_IFSCR, 0x21c, 0x40),
- RSND_GEN1_M_REG(gen, SRU, SRC_IFSVR, 0x220, 0x40),
- RSND_GEN1_M_REG(gen, SRU, SRC_SRCCR, 0x224, 0x40),
- RSND_GEN1_M_REG(gen, SRU, SRC_MNFSR, 0x228, 0x40),
-
- RSND_GEN1_S_REG(gen, ADG, BRRA, 0x00),
- RSND_GEN1_S_REG(gen, ADG, BRRB, 0x04),
- RSND_GEN1_S_REG(gen, ADG, SSICKR, 0x08),
- RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL0, 0x0c),
- RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL1, 0x10),
- RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL3, 0x18),
- RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL4, 0x1c),
- RSND_GEN1_S_REG(gen, ADG, AUDIO_CLK_SEL5, 0x20),
-
- RSND_GEN1_M_REG(gen, SSI, SSICR, 0x00, 0x40),
- RSND_GEN1_M_REG(gen, SSI, SSISR, 0x04, 0x40),
- RSND_GEN1_M_REG(gen, SSI, SSITDR, 0x08, 0x40),
- RSND_GEN1_M_REG(gen, SSI, SSIRDR, 0x0c, 0x40),
- RSND_GEN1_M_REG(gen, SSI, SSIWSR, 0x20, 0x40),
- };
-
- return rsnd_gen_regmap_init(priv, gen, regf);
-}
-
static int rsnd_gen1_probe(struct platform_device *pdev,
struct rsnd_priv *priv)
{
struct device *dev = rsnd_priv_to_dev(priv);
- struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
- struct resource *sru_res;
- struct resource *adg_res;
- struct resource *ssi_res;
- int ret;
-
- /*
- * map address
- */
- sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
- adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
- ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI);
-
- gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
- gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
- gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res);
- if (IS_ERR(gen->base[RSND_GEN1_SRU]) ||
- IS_ERR(gen->base[RSND_GEN1_ADG]) ||
- IS_ERR(gen->base[RSND_GEN1_SSI]))
- return -ENODEV;
+ struct rsnd_regmap_field_conf conf_sru[] = {
+ RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00),
+ RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08),
+ RSND_GEN_S_REG(SRC_TMG_SEL1, 0x0c),
+ RSND_GEN_S_REG(SRC_TMG_SEL2, 0x10),
+ RSND_GEN_S_REG(SRC_ROUTE_CTRL, 0xc0),
+ RSND_GEN_S_REG(SSI_MODE0, 0xD0),
+ RSND_GEN_S_REG(SSI_MODE1, 0xD4),
+ RSND_GEN_M_REG(SRC_BUSIF_MODE, 0x20, 0x4),
+ RSND_GEN_M_REG(SRC_ROUTE_MODE0, 0x50, 0x8),
+ RSND_GEN_M_REG(SRC_SWRSR, 0x200, 0x40),
+ RSND_GEN_M_REG(SRC_SRCIR, 0x204, 0x40),
+ RSND_GEN_M_REG(SRC_ADINR, 0x214, 0x40),
+ RSND_GEN_M_REG(SRC_IFSCR, 0x21c, 0x40),
+ RSND_GEN_M_REG(SRC_IFSVR, 0x220, 0x40),
+ RSND_GEN_M_REG(SRC_SRCCR, 0x224, 0x40),
+ RSND_GEN_M_REG(SRC_MNFSR, 0x228, 0x40),
+ };
+ struct rsnd_regmap_field_conf conf_adg[] = {
+ RSND_GEN_S_REG(BRRA, 0x00),
+ RSND_GEN_S_REG(BRRB, 0x04),
+ RSND_GEN_S_REG(SSICKR, 0x08),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL0, 0x0c),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL1, 0x10),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL3, 0x18),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL4, 0x1c),
+ RSND_GEN_S_REG(AUDIO_CLK_SEL5, 0x20),
+ };
+ struct rsnd_regmap_field_conf conf_ssi[] = {
+ RSND_GEN_M_REG(SSICR, 0x00, 0x40),
+ RSND_GEN_M_REG(SSISR, 0x04, 0x40),
+ RSND_GEN_M_REG(SSITDR, 0x08, 0x40),
+ RSND_GEN_M_REG(SSIRDR, 0x0c, 0x40),
+ RSND_GEN_M_REG(SSIWSR, 0x20, 0x40),
+ };
+ int ret_sru;
+ int ret_adg;
+ int ret_ssi;
- ret = rsnd_gen1_regmap_init(priv, gen);
- if (ret < 0)
- return ret;
+ ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, conf_sru);
+ ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, conf_adg);
+ ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, conf_ssi);
+ if (ret_sru < 0 ||
+ ret_adg < 0 ||
+ ret_ssi < 0)
+ return ret_sru | ret_adg | ret_ssi;
- dev_dbg(dev, "Gen1 device probed\n");
- dev_dbg(dev, "SRU : %pap => %p\n", &sru_res->start,
- gen->base[RSND_GEN1_SRU]);
- dev_dbg(dev, "ADG : %pap => %p\n", &adg_res->start,
- gen->base[RSND_GEN1_ADG]);
- dev_dbg(dev, "SSI : %pap => %p\n", &ssi_res->start,
- gen->base[RSND_GEN1_SSI]);
+ dev_dbg(dev, "Gen1 is probed\n");
return 0;
-
}
/*
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 39d98af..d119adf 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -90,6 +90,7 @@ enum rsnd_reg {
RSND_REG_SHARE19,
RSND_REG_SHARE20,
RSND_REG_SHARE21,
+ RSND_REG_SHARE22,
RSND_REG_MAX,
};
@@ -127,6 +128,7 @@ enum rsnd_reg {
#define RSND_REG_AUDIO_CLK_SEL2 RSND_REG_SHARE19
#define RSND_REG_CMD_CTRL RSND_REG_SHARE20
#define RSND_REG_CMDOUT_TIMSEL RSND_REG_SHARE21
+#define RSND_REG_BUSIF_DALIGN RSND_REG_SHARE22
struct rsnd_of_data;
struct rsnd_priv;
@@ -156,12 +158,9 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod);
*/
struct rsnd_dma {
struct sh_dmae_slave slave;
- struct work_struct work;
struct dma_chan *chan;
- enum dma_data_direction dir;
-
- int submit_loop;
- int offset; /* it cares A/B plane */
+ enum dma_transfer_direction dir;
+ dma_addr_t addr;
};
void rsnd_dma_start(struct rsnd_dma *dma);
@@ -185,6 +184,7 @@ enum rsnd_mod_type {
struct rsnd_mod_ops {
char *name;
+ char* (*dma_name)(struct rsnd_mod *mod);
int (*probe)(struct rsnd_mod *mod,
struct rsnd_dai *rdai);
int (*remove)(struct rsnd_mod *mod,
@@ -224,6 +224,7 @@ void rsnd_mod_init(struct rsnd_priv *priv,
enum rsnd_mod_type type,
int id);
char *rsnd_mod_name(struct rsnd_mod *mod);
+char *rsnd_mod_dma_name(struct rsnd_mod *mod);
/*
* R-Car sound DAI
@@ -281,10 +282,9 @@ int rsnd_gen_probe(struct platform_device *pdev,
void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
struct rsnd_mod *mod,
enum rsnd_reg reg);
-void rsnd_gen_dma_addr(struct rsnd_priv *priv,
- struct rsnd_dma *dma,
- struct dma_slave_config *cfg,
- int is_play, int slave_id);
+dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv,
+ struct rsnd_mod *mod,
+ int is_play, int is_from);
#define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1)
#define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2)
@@ -391,8 +391,12 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
struct rsnd_dai_stream *io,
struct snd_pcm_runtime *runtime);
-int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
- struct rsnd_dai *rdai);
+int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai,
+ int use_busif);
+int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai,
+ int use_busif);
int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
struct rsnd_dai *rdai);
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 200eda0..9183e01 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -106,18 +106,19 @@ struct rsnd_src {
/*
* Gen1/Gen2 common functions
*/
-int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
- struct rsnd_dai *rdai)
+int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai,
+ int use_busif)
{
struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod);
- struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
int ssi_id = rsnd_mod_id(ssi_mod);
/*
* SSI_MODE0
*/
rsnd_mod_bset(ssi_mod, SSI_MODE0, (1 << ssi_id),
- src_mod ? 0 : (1 << ssi_id));
+ !use_busif << ssi_id);
/*
* SSI_MODE1
@@ -143,6 +144,46 @@ int rsnd_src_ssi_mode_init(struct rsnd_mod *ssi_mod,
0x2 << shift : 0x1 << shift);
}
+ /*
+ * DMA settings for SSIU
+ */
+ if (use_busif) {
+ u32 val = 0x76543210;
+ u32 mask = ~0;
+
+ rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
+ rsnd_get_adinr(ssi_mod));
+ rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
+ rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
+
+ mask <<= runtime->channels * 4;
+ val = val & mask;
+
+ switch (runtime->sample_bits) {
+ case 16:
+ val |= 0x67452301 & ~mask;
+ break;
+ case 32:
+ val |= 0x76543210 & ~mask;
+ break;
+ }
+ rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val);
+
+ }
+
+ return 0;
+}
+
+int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
+ struct rsnd_dai *rdai,
+ int use_busif)
+{
+ /*
+ * DMA settings for SSIU
+ */
+ if (use_busif)
+ rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
+
return 0;
}
@@ -461,18 +502,45 @@ static struct rsnd_mod_ops rsnd_src_gen1_ops = {
static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
struct rsnd_dai *rdai)
{
+ struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ struct rsnd_src *src = rsnd_mod_to_src(mod);
+ uint ratio;
int ret;
+ /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
+ if (!rsnd_src_convert_rate(src))
+ ratio = 0;
+ else if (rsnd_src_convert_rate(src) > runtime->rate)
+ ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
+ else
+ ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
+
+ if (ratio > 600) {
+ dev_err(dev, "FSO/FSI ratio error\n");
+ return -EINVAL;
+ }
+
ret = rsnd_src_set_convert_rate(mod, rdai);
if (ret < 0)
return ret;
- rsnd_mod_write(mod, SSI_BUSIF_ADINR, rsnd_get_adinr(mod));
- rsnd_mod_write(mod, SSI_BUSIF_MODE, 1);
-
rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
- rsnd_mod_write(mod, SRC_BSDSR, 0x01800000);
+ switch (rsnd_mod_id(mod)) {
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ rsnd_mod_write(mod, SRC_BSDSR, 0x02400000);
+ break;
+ default:
+ rsnd_mod_write(mod, SRC_BSDSR, 0x01800000);
+ break;
+ }
+
rsnd_mod_write(mod, SRC_BSISR, 0x00100060);
return 0;
@@ -554,7 +622,6 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
rsnd_dma_start(rsnd_mod_to_dma(&src->mod));
- rsnd_mod_write(mod, SSI_CTRL, 0x1);
rsnd_mod_write(mod, SRC_CTRL, val);
return rsnd_src_start(mod, rdai);
@@ -565,7 +632,6 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
{
struct rsnd_src *src = rsnd_mod_to_src(mod);
- rsnd_mod_write(mod, SSI_CTRL, 0);
rsnd_mod_write(mod, SRC_CTRL, 0);
rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 2df723df..34e8400 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -90,6 +90,20 @@ struct rsnd_ssi {
#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
+static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
+{
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+ int use_busif = 0;
+
+ if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
+ use_busif = 1;
+ if (rsnd_io_to_mod_src(io))
+ use_busif = 1;
+
+ return use_busif;
+}
+
static void rsnd_ssi_status_check(struct rsnd_mod *mod,
u32 bit)
{
@@ -289,8 +303,6 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
ssi->cr_own = cr;
ssi->err = -1; /* ignore 1st error */
- rsnd_src_ssi_mode_init(mod, rdai);
-
return 0;
}
@@ -389,6 +401,8 @@ static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
/* enable PIO IRQ */
ssi->cr_etc = UIEN | OIEN | DIEN;
+ rsnd_src_ssiu_start(mod, rdai, 0);
+
rsnd_src_enable_ssi_irq(mod, rdai);
rsnd_ssi_hw_start(ssi, rdai, io);
@@ -405,6 +419,8 @@ static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
rsnd_ssi_hw_stop(ssi, rdai);
+ rsnd_src_ssiu_stop(mod, rdai, 0);
+
return 0;
}
@@ -457,6 +473,8 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
/* enable DMA transfer */
ssi->cr_etc = DMEN;
+ rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+
rsnd_dma_start(dma);
rsnd_ssi_hw_start(ssi, ssi->rdai, io);
@@ -482,11 +500,19 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
rsnd_dma_stop(dma);
+ rsnd_src_ssiu_stop(mod, rdai, 1);
+
return 0;
}
+static char *rsnd_ssi_dma_name(struct rsnd_mod *mod)
+{
+ return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME;
+}
+
static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
.name = SSI_NAME,
+ .dma_name = rsnd_ssi_dma_name,
.probe = rsnd_ssi_dma_probe,
.remove = rsnd_ssi_dma_remove,
.init = rsnd_ssi_init,
@@ -595,6 +621,9 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
*/
ssi_info->dma_id = of_get_property(np, "pio-transfer", NULL) ?
0 : 1;
+
+ if (of_get_property(np, "no-busif", NULL))
+ ssi_info->flags |= RSND_SSI_NO_BUSIF;
}
rsnd_of_parse_ssi_end:
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index 00e70b6..a9f82b5 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -78,7 +78,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
mutex_init(&codec->cache_rw_mutex);
dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
- codec->name);
+ codec->component.name);
if (codec_drv->reg_cache_default)
codec->reg_cache = kmemdup(codec_drv->reg_cache_default,
@@ -98,8 +98,7 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
int snd_soc_cache_exit(struct snd_soc_codec *codec)
{
dev_dbg(codec->dev, "ASoC: Destroying cache for %s codec\n",
- codec->name);
-
+ codec->component.name);
kfree(codec->reg_cache);
codec->reg_cache = NULL;
return 0;
@@ -192,7 +191,7 @@ int snd_soc_cache_sync(struct snd_soc_codec *codec)
return 0;
dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
- codec->name);
+ codec->component.name);
trace_snd_soc_cache_sync(codec, name, "start");
ret = snd_soc_flat_cache_sync(codec);
if (!ret)
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 10f7f1d..27c06ac 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -37,7 +37,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
ret = platform->driver->compr_ops->open(cstream);
if (ret < 0) {
- pr_err("compress asoc: can't open platform %s\n", platform->name);
+ pr_err("compress asoc: can't open platform %s\n",
+ platform->component.name);
goto out;
}
}
@@ -84,7 +85,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
ret = platform->driver->compr_ops->open(cstream);
if (ret < 0) {
- pr_err("compress asoc: can't open platform %s\n", platform->name);
+ pr_err("compress asoc: can't open platform %s\n",
+ platform->component.name);
goto out;
}
}
@@ -627,6 +629,11 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
char new_name[64];
int ret = 0, direction = 0;
+ if (rtd->num_codecs > 1) {
+ dev_err(rtd->card->dev, "Multicodec not supported for compressed stream\n");
+ return -EINVAL;
+ }
+
/* check client and interface hw capabilities */
snprintf(new_name, sizeof(new_name), "%s %s-%d",
rtd->dai_link->stream_name, codec_dai->name, num);
@@ -680,7 +687,7 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
if (ret < 0) {
pr_err("compress asoc: can't create compress for codec %s\n",
- codec->name);
+ codec->component.name);
goto compr_err;
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index b87d7d8..2d6c8b8 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -270,12 +270,33 @@ static const struct file_operations codec_reg_fops = {
.llseek = default_llseek,
};
+static struct dentry *soc_debugfs_create_dir(struct dentry *parent,
+ const char *fmt, ...)
+{
+ struct dentry *de;
+ va_list ap;
+ char *s;
+
+ va_start(ap, fmt);
+ s = kvasprintf(GFP_KERNEL, fmt, ap);
+ va_end(ap);
+
+ if (!s)
+ return NULL;
+
+ de = debugfs_create_dir(s, parent);
+ kfree(s);
+
+ return de;
+}
+
static void soc_init_codec_debugfs(struct snd_soc_codec *codec)
{
- struct dentry *debugfs_card_root = codec->card->debugfs_card_root;
+ struct dentry *debugfs_card_root = codec->component.card->debugfs_card_root;
- codec->debugfs_codec_root = debugfs_create_dir(codec->name,
- debugfs_card_root);
+ codec->debugfs_codec_root = soc_debugfs_create_dir(debugfs_card_root,
+ "codec:%s",
+ codec->component.name);
if (!codec->debugfs_codec_root) {
dev_warn(codec->dev,
"ASoC: Failed to create codec debugfs directory\n");
@@ -304,17 +325,18 @@ static void soc_cleanup_codec_debugfs(struct snd_soc_codec *codec)
static void soc_init_platform_debugfs(struct snd_soc_platform *platform)
{
- struct dentry *debugfs_card_root = platform->card->debugfs_card_root;
+ struct dentry *debugfs_card_root = platform->component.card->debugfs_card_root;
- platform->debugfs_platform_root = debugfs_create_dir(platform->name,
- debugfs_card_root);
+ platform->debugfs_platform_root = soc_debugfs_create_dir(debugfs_card_root,
+ "platform:%s",
+ platform->component.name);
if (!platform->debugfs_platform_root) {
dev_warn(platform->dev,
"ASoC: Failed to create platform debugfs directory\n");
return;
}
- snd_soc_dapm_debugfs_init(&platform->dapm,
+ snd_soc_dapm_debugfs_init(&platform->component.dapm,
platform->debugfs_platform_root);
}
@@ -335,7 +357,7 @@ static ssize_t codec_list_read_file(struct file *file, char __user *user_buf,
list_for_each_entry(codec, &codec_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
- codec->name);
+ codec->component.name);
if (len >= 0)
ret += len;
if (ret > PAGE_SIZE) {
@@ -406,7 +428,7 @@ static ssize_t platform_list_read_file(struct file *file,
list_for_each_entry(platform, &platform_list, list) {
len = snprintf(buf + ret, PAGE_SIZE - ret, "%s\n",
- platform->name);
+ platform->component.name);
if (len >= 0)
ret += len;
if (ret > PAGE_SIZE) {
@@ -524,11 +546,12 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
int err;
codec->ac97->dev.bus = &ac97_bus_type;
- codec->ac97->dev.parent = codec->card->dev;
+ codec->ac97->dev.parent = codec->component.card->dev;
codec->ac97->dev.release = soc_ac97_device_release;
dev_set_name(&codec->ac97->dev, "%d-%d:%s",
- codec->card->snd_card->number, 0, codec->name);
+ codec->component.card->snd_card->number, 0,
+ codec->component.name);
err = device_register(&codec->ac97->dev);
if (err < 0) {
dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
@@ -554,7 +577,7 @@ int snd_soc_suspend(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
struct snd_soc_codec *codec;
- int i;
+ int i, j;
/* If the initialization of this soc device failed, there is no codec
* associated with it. Just bail out in this case.
@@ -574,14 +597,17 @@ int snd_soc_suspend(struct device *dev)
/* mute any active DACs */
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *dai = card->rtd[i].codec_dai;
- struct snd_soc_dai_driver *drv = dai->driver;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, 1);
+ for (j = 0; j < card->rtd[i].num_codecs; j++) {
+ struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+ struct snd_soc_dai_driver *drv = dai->driver;
+
+ if (drv->ops->digital_mute && dai->playback_active)
+ drv->ops->digital_mute(dai, 1);
+ }
}
/* suspend all pcms */
@@ -612,8 +638,12 @@ int snd_soc_suspend(struct device *dev)
/* close any waiting streams and save state */
for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_dai **codec_dais = card->rtd[i].codec_dais;
flush_delayed_work(&card->rtd[i].delayed_work);
- card->rtd[i].codec->dapm.suspend_bias_level = card->rtd[i].codec->dapm.bias_level;
+ for (j = 0; j < card->rtd[i].num_codecs; j++) {
+ codec_dais[j]->codec->dapm.suspend_bias_level =
+ codec_dais[j]->codec->dapm.bias_level;
+ }
}
for (i = 0; i < card->num_rtd; i++) {
@@ -697,7 +727,7 @@ static void soc_resume_deferred(struct work_struct *work)
struct snd_soc_card *card =
container_of(work, struct snd_soc_card, deferred_resume_work);
struct snd_soc_codec *codec;
- int i;
+ int i, j;
/* our power state is still SNDRV_CTL_POWER_D3hot from suspend time,
* so userspace apps are blocked from touching us
@@ -758,14 +788,17 @@ static void soc_resume_deferred(struct work_struct *work)
/* unmute any active DACs */
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *dai = card->rtd[i].codec_dai;
- struct snd_soc_dai_driver *drv = dai->driver;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, 0);
+ for (j = 0; j < card->rtd[i].num_codecs; j++) {
+ struct snd_soc_dai *dai = card->rtd[i].codec_dais[j];
+ struct snd_soc_dai_driver *drv = dai->driver;
+
+ if (drv->ops->digital_mute && dai->playback_active)
+ drv->ops->digital_mute(dai, 0);
+ }
}
for (i = 0; i < card->num_rtd; i++) {
@@ -810,12 +843,19 @@ int snd_soc_resume(struct device *dev)
/* activate pins from sleep state */
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
+ struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ struct snd_soc_dai **codec_dais = rtd->codec_dais;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int j;
+
if (cpu_dai->active)
pinctrl_pm_select_default_state(cpu_dai->dev);
- if (codec_dai->active)
- pinctrl_pm_select_default_state(codec_dai->dev);
+
+ for (j = 0; j < rtd->num_codecs; j++) {
+ struct snd_soc_dai *codec_dai = codec_dais[j];
+ if (codec_dai->active)
+ pinctrl_pm_select_default_state(codec_dai->dev);
+ }
}
/* AC97 devices might have other drivers hanging off them so
@@ -847,8 +887,9 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
static const struct snd_soc_dai_ops null_dai_ops = {
};
-static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_node,
- const char *codec_name)
+static struct snd_soc_codec *soc_find_codec(
+ const struct device_node *codec_of_node,
+ const char *codec_name)
{
struct snd_soc_codec *codec;
@@ -857,7 +898,7 @@ static struct snd_soc_codec *soc_find_codec(const struct device_node *codec_of_n
if (codec->dev->of_node != codec_of_node)
continue;
} else {
- if (strcmp(codec->name, codec_name))
+ if (strcmp(codec->component.name, codec_name))
continue;
}
@@ -886,9 +927,12 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_component *component;
+ struct snd_soc_dai_link_component *codecs = dai_link->codecs;
+ struct snd_soc_dai **codec_dais = rtd->codec_dais;
struct snd_soc_platform *platform;
struct snd_soc_dai *cpu_dai;
const char *platform_name;
+ int i;
dev_dbg(card->dev, "ASoC: binding %s at idx %d\n", dai_link->name, num);
@@ -915,24 +959,30 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
return -EPROBE_DEFER;
}
- /* Find CODEC from registered list */
- rtd->codec = soc_find_codec(dai_link->codec_of_node,
- dai_link->codec_name);
- if (!rtd->codec) {
- dev_err(card->dev, "ASoC: CODEC %s not registered\n",
- dai_link->codec_name);
- return -EPROBE_DEFER;
- }
+ rtd->num_codecs = dai_link->num_codecs;
- /* Find CODEC DAI from registered list */
- rtd->codec_dai = soc_find_codec_dai(rtd->codec,
- dai_link->codec_dai_name);
- if (!rtd->codec_dai) {
- dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
- dai_link->codec_dai_name);
- return -EPROBE_DEFER;
+ /* Find CODEC from registered CODECs */
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_codec *codec;
+ codec = soc_find_codec(codecs[i].of_node, codecs[i].name);
+ if (!codec) {
+ dev_err(card->dev, "ASoC: CODEC %s not registered\n",
+ codecs[i].name);
+ return -EPROBE_DEFER;
+ }
+
+ codec_dais[i] = soc_find_codec_dai(codec, codecs[i].dai_name);
+ if (!codec_dais[i]) {
+ dev_err(card->dev, "ASoC: CODEC DAI %s not registered\n",
+ codecs[i].dai_name);
+ return -EPROBE_DEFER;
+ }
}
+ /* Single codec links expect codec and codec_dai in runtime data */
+ rtd->codec_dai = codec_dais[0];
+ rtd->codec = rtd->codec_dai->codec;
+
/* if there's no platform we match on the empty platform */
platform_name = dai_link->platform_name;
if (!platform_name && !dai_link->platform_of_node)
@@ -945,7 +995,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card, int num)
dai_link->platform_of_node)
continue;
} else {
- if (strcmp(platform->name, platform_name))
+ if (strcmp(platform->component.name, platform_name))
continue;
}
@@ -974,11 +1024,10 @@ static int soc_remove_platform(struct snd_soc_platform *platform)
}
/* Make sure all DAPM widgets are freed */
- snd_soc_dapm_free(&platform->dapm);
+ snd_soc_dapm_free(&platform->component.dapm);
soc_cleanup_platform_debugfs(platform);
platform->probed = 0;
- list_del(&platform->card_list);
module_put(platform->dev->driver->owner);
return 0;
@@ -1023,8 +1072,8 @@ static void soc_remove_codec_dai(struct snd_soc_dai *codec_dai, int order)
static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
- struct snd_soc_dai *codec_dai = rtd->codec_dai, *cpu_dai = rtd->cpu_dai;
- int err;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int i, err;
/* unregister the rtd device */
if (rtd->dev_registered) {
@@ -1035,7 +1084,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
}
/* remove the CODEC DAI */
- soc_remove_codec_dai(codec_dai, order);
+ for (i = 0; i < rtd->num_codecs; i++)
+ soc_remove_codec_dai(rtd->codec_dais[i], order);
/* remove the cpu_dai */
if (cpu_dai && cpu_dai->probed &&
@@ -1048,11 +1098,8 @@ static void soc_remove_link_dais(struct snd_soc_card *card, int num, int order)
cpu_dai->name, err);
}
cpu_dai->probed = 0;
-
- if (!cpu_dai->codec) {
- snd_soc_dapm_free(&cpu_dai->dapm);
+ if (!cpu_dai->codec)
module_put(cpu_dai->dev->driver->owner);
- }
}
}
@@ -1061,9 +1108,9 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_codec *codec;
+ int i;
/* remove the platform */
if (platform && platform->probed &&
@@ -1072,8 +1119,8 @@ static void soc_remove_link_components(struct snd_soc_card *card, int num,
}
/* remove the CODEC-side CODEC */
- if (codec_dai) {
- codec = codec_dai->codec;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec = rtd->codec_dais[i]->codec;
if (codec && codec->probed &&
codec->driver->remove_order == order)
soc_remove_codec(codec);
@@ -1108,7 +1155,7 @@ static void soc_remove_dai_links(struct snd_soc_card *card)
}
static void soc_set_name_prefix(struct snd_soc_card *card,
- struct snd_soc_codec *codec)
+ struct snd_soc_component *component)
{
int i;
@@ -1117,11 +1164,11 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
for (i = 0; i < card->num_configs; i++) {
struct snd_soc_codec_conf *map = &card->codec_conf[i];
- if (map->of_node && codec->dev->of_node != map->of_node)
+ if (map->of_node && component->dev->of_node != map->of_node)
continue;
- if (map->dev_name && strcmp(codec->name, map->dev_name))
+ if (map->dev_name && strcmp(component->name, map->dev_name))
continue;
- codec->name_prefix = map->name_prefix;
+ component->name_prefix = map->name_prefix;
break;
}
}
@@ -1133,9 +1180,9 @@ static int soc_probe_codec(struct snd_soc_card *card,
const struct snd_soc_codec_driver *driver = codec->driver;
struct snd_soc_dai *dai;
- codec->card = card;
+ codec->component.card = card;
codec->dapm.card = card;
- soc_set_name_prefix(card, codec);
+ soc_set_name_prefix(card, &codec->component);
if (!try_module_get(codec->dev->driver->owner))
return -ENODEV;
@@ -1177,7 +1224,7 @@ static int soc_probe_codec(struct snd_soc_card *card,
WARN(codec->dapm.idle_bias_off &&
codec->dapm.bias_level != SND_SOC_BIAS_OFF,
"codec %s can not start from non-off bias with idle_bias_off==1\n",
- codec->name);
+ codec->component.name);
}
if (driver->controls)
@@ -1209,8 +1256,8 @@ static int soc_probe_platform(struct snd_soc_card *card,
struct snd_soc_component *component;
struct snd_soc_dai *dai;
- platform->card = card;
- platform->dapm.card = card;
+ platform->component.card = card;
+ platform->component.dapm.card = card;
if (!try_module_get(platform->dev->driver->owner))
return -ENODEV;
@@ -1218,7 +1265,7 @@ static int soc_probe_platform(struct snd_soc_card *card,
soc_init_platform_debugfs(platform);
if (driver->dapm_widgets)
- snd_soc_dapm_new_controls(&platform->dapm,
+ snd_soc_dapm_new_controls(&platform->component.dapm,
driver->dapm_widgets, driver->num_dapm_widgets);
/* Create DAPM widgets for each DAI stream */
@@ -1226,10 +1273,11 @@ static int soc_probe_platform(struct snd_soc_card *card,
if (component->dev != platform->dev)
continue;
list_for_each_entry(dai, &component->dai_list, list)
- snd_soc_dapm_new_dai_widgets(&platform->dapm, dai);
+ snd_soc_dapm_new_dai_widgets(&platform->component.dapm,
+ dai);
}
- platform->dapm.idle_bias_off = 1;
+ platform->component.dapm.idle_bias_off = 1;
if (driver->probe) {
ret = driver->probe(platform);
@@ -1244,13 +1292,12 @@ static int soc_probe_platform(struct snd_soc_card *card,
snd_soc_add_platform_controls(platform, driver->controls,
driver->num_controls);
if (driver->dapm_routes)
- snd_soc_dapm_add_routes(&platform->dapm, driver->dapm_routes,
- driver->num_dapm_routes);
+ snd_soc_dapm_add_routes(&platform->component.dapm,
+ driver->dapm_routes, driver->num_dapm_routes);
/* mark platform as probed and add to card platform list */
platform->probed = 1;
- list_add(&platform->card_list, &card->platform_dev_list);
- list_add(&platform->dapm.list, &card->dapm_list);
+ list_add(&platform->component.dapm.list, &card->dapm_list);
return 0;
@@ -1266,83 +1313,17 @@ static void rtd_release(struct device *dev)
kfree(dev);
}
-static int soc_aux_dev_init(struct snd_soc_card *card,
- struct snd_soc_codec *codec,
- int num)
-{
- struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
- struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
- int ret;
-
- rtd->card = card;
-
- /* do machine specific initialization */
- if (aux_dev->init) {
- ret = aux_dev->init(&codec->dapm);
- if (ret < 0)
- return ret;
- }
-
- rtd->codec = codec;
-
- return 0;
-}
-
-static int soc_dai_link_init(struct snd_soc_card *card,
- struct snd_soc_codec *codec,
- int num)
-{
- struct snd_soc_dai_link *dai_link = &card->dai_link[num];
- struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
- int ret;
-
- rtd->card = card;
-
- /* do machine specific initialization */
- if (dai_link->init) {
- ret = dai_link->init(rtd);
- if (ret < 0)
- return ret;
- }
-
- rtd->codec = codec;
-
- return 0;
-}
-
-static int soc_post_component_init(struct snd_soc_card *card,
- struct snd_soc_codec *codec,
- int num, int dailess)
+static int soc_post_component_init(struct snd_soc_pcm_runtime *rtd,
+ const char *name)
{
- struct snd_soc_dai_link *dai_link = NULL;
- struct snd_soc_aux_dev *aux_dev = NULL;
- struct snd_soc_pcm_runtime *rtd;
- const char *name;
int ret = 0;
- if (!dailess) {
- dai_link = &card->dai_link[num];
- rtd = &card->rtd[num];
- name = dai_link->name;
- ret = soc_dai_link_init(card, codec, num);
- } else {
- aux_dev = &card->aux_dev[num];
- rtd = &card->rtd_aux[num];
- name = aux_dev->name;
- ret = soc_aux_dev_init(card, codec, num);
- }
-
- if (ret < 0) {
- dev_err(card->dev, "ASoC: failed to init %s: %d\n", name, ret);
- return ret;
- }
-
/* register the rtd device */
rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!rtd->dev)
return -ENOMEM;
device_initialize(rtd->dev);
- rtd->dev->parent = card->dev;
+ rtd->dev->parent = rtd->card->dev;
rtd->dev->release = rtd_release;
rtd->dev->init_name = name;
dev_set_drvdata(rtd->dev, rtd);
@@ -1355,7 +1336,7 @@ static int soc_post_component_init(struct snd_soc_card *card,
if (ret < 0) {
/* calling put_device() here to free the rtd->dev */
put_device(rtd->dev);
- dev_err(card->dev,
+ dev_err(rtd->card->dev,
"ASoC: failed to register runtime device: %d\n", ret);
return ret;
}
@@ -1364,26 +1345,15 @@ static int soc_post_component_init(struct snd_soc_card *card,
/* add DAPM sysfs entries for this codec */
ret = snd_soc_dapm_sys_add(rtd->dev);
if (ret < 0)
- dev_err(codec->dev,
+ dev_err(rtd->dev,
"ASoC: failed to add codec dapm sysfs entries: %d\n", ret);
/* add codec sysfs entries */
ret = device_create_file(rtd->dev, &dev_attr_codec_reg);
if (ret < 0)
- dev_err(codec->dev,
+ dev_err(rtd->dev,
"ASoC: failed to add codec sysfs files: %d\n", ret);
-#ifdef CONFIG_DEBUG_FS
- /* add DPCM sysfs entries */
- if (!dailess && !dai_link->dynamic)
- goto out;
-
- ret = soc_dpcm_debugfs_add(rtd);
- if (ret < 0)
- dev_err(rtd->dev, "ASoC: failed to add dpcm sysfs entries: %d\n", ret);
-
-out:
-#endif
return 0;
}
@@ -1392,9 +1362,8 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
{
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_platform *platform = rtd->platform;
- int ret;
+ int i, ret;
/* probe the CPU-side component, if it is a CODEC */
if (cpu_dai->codec &&
@@ -1405,12 +1374,14 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
return ret;
}
- /* probe the CODEC-side component */
- if (!codec_dai->codec->probed &&
- codec_dai->codec->driver->probe_order == order) {
- ret = soc_probe_codec(card, codec_dai->codec);
- if (ret < 0)
- return ret;
+ /* probe the CODEC-side components */
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if (!rtd->codec_dais[i]->codec->probed &&
+ rtd->codec_dais[i]->codec->driver->probe_order == order) {
+ ret = soc_probe_codec(card, rtd->codec_dais[i]->codec);
+ if (ret < 0)
+ return ret;
+ }
}
/* probe the platform */
@@ -1450,12 +1421,16 @@ static int soc_probe_codec_dai(struct snd_soc_card *card,
static int soc_link_dai_widgets(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link,
- struct snd_soc_dai *cpu_dai,
- struct snd_soc_dai *codec_dai)
+ struct snd_soc_pcm_runtime *rtd)
{
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dapm_widget *play_w, *capture_w;
int ret;
+ if (rtd->num_codecs > 1)
+ dev_warn(card->dev, "ASoC: Multiple codecs not supported yet\n");
+
/* link the DAI widgets */
play_w = codec_dai->playback_widget;
capture_w = cpu_dai->capture_widget;
@@ -1488,19 +1463,18 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
- struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret;
+ int i, ret;
dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
card->name, num, order);
/* config components */
cpu_dai->platform = platform;
- codec_dai->card = card;
cpu_dai->card = card;
+ for (i = 0; i < rtd->num_codecs; i++)
+ rtd->codec_dais[i]->card = card;
/* set default power off timeout */
rtd->pmdown_time = pmdown_time;
@@ -1509,11 +1483,8 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
if (!cpu_dai->probed &&
cpu_dai->driver->probe_order == order) {
if (!cpu_dai->codec) {
- cpu_dai->dapm.card = card;
if (!try_module_get(cpu_dai->dev->driver->owner))
return -ENODEV;
-
- list_add(&cpu_dai->dapm.list, &card->dapm_list);
}
if (cpu_dai->driver->probe) {
@@ -1530,18 +1501,43 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
}
/* probe the CODEC DAI */
- ret = soc_probe_codec_dai(card, codec_dai, order);
- if (ret)
- return ret;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+ if (ret)
+ return ret;
+ }
/* complete DAI probe during last probe */
if (order != SND_SOC_COMP_ORDER_LAST)
return 0;
- ret = soc_post_component_init(card, codec, num, 0);
+ /* do machine specific initialization */
+ if (dai_link->init) {
+ ret = dai_link->init(rtd);
+ if (ret < 0) {
+ dev_err(card->dev, "ASoC: failed to init %s: %d\n",
+ dai_link->name, ret);
+ return ret;
+ }
+ }
+
+ ret = soc_post_component_init(rtd, dai_link->name);
if (ret)
return ret;
+#ifdef CONFIG_DEBUG_FS
+ /* add DPCM sysfs entries */
+ if (dai_link->dynamic) {
+ ret = soc_dpcm_debugfs_add(rtd);
+ if (ret < 0) {
+ dev_err(rtd->dev,
+ "ASoC: failed to add dpcm sysfs entries: %d\n",
+ ret);
+ return ret;
+ }
+ }
+#endif
+
ret = device_create_file(rtd->dev, &dev_attr_pmdown_time);
if (ret < 0)
dev_warn(rtd->dev, "ASoC: failed to add pmdown_time sysfs: %d\n",
@@ -1570,16 +1566,18 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
codec2codec_close_delayed_work);
/* link the DAI widgets */
- ret = soc_link_dai_widgets(card, dai_link,
- cpu_dai, codec_dai);
+ ret = soc_link_dai_widgets(card, dai_link, rtd);
if (ret)
return ret;
}
}
/* add platform data for AC97 devices */
- if (rtd->codec_dai->driver->ac97_control)
- snd_ac97_dev_add_pdata(codec->ac97, rtd->cpu_dai->ac97_pdata);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if (rtd->codec_dais[i]->driver->ac97_control)
+ snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
+ rtd->cpu_dai->ac97_pdata);
+ }
return 0;
}
@@ -1617,11 +1615,6 @@ static int soc_register_ac97_codec(struct snd_soc_codec *codec,
return 0;
}
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
- return soc_register_ac97_codec(rtd->codec, rtd->codec_dai);
-}
-
static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
{
if (codec->ac97_registered) {
@@ -1630,74 +1623,77 @@ static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
}
}
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
+static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
{
- soc_unregister_ac97_codec(rtd->codec);
-}
-#endif
+ int i, ret;
-static struct snd_soc_codec *soc_find_matching_codec(struct snd_soc_card *card,
- int num)
-{
- struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
- struct snd_soc_codec *codec;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
- /* find CODEC from registered CODECs */
- list_for_each_entry(codec, &codec_list, list) {
- if (aux_dev->codec_of_node &&
- (codec->dev->of_node != aux_dev->codec_of_node))
- continue;
- if (aux_dev->codec_name && strcmp(codec->name, aux_dev->codec_name))
- continue;
- return codec;
+ ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
+ if (ret) {
+ while (--i >= 0)
+ soc_unregister_ac97_codec(codec_dai->codec);
+ return ret;
+ }
}
- return NULL;
+ return 0;
}
-static int soc_check_aux_dev(struct snd_soc_card *card, int num)
+static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
- const char *codecname = aux_dev->codec_name;
- struct snd_soc_codec *codec = soc_find_matching_codec(card, num);
-
- if (codec)
- return 0;
- if (aux_dev->codec_of_node)
- codecname = of_node_full_name(aux_dev->codec_of_node);
+ int i;
- dev_err(card->dev, "ASoC: %s not registered\n", codecname);
- return -EPROBE_DEFER;
+ for (i = 0; i < rtd->num_codecs; i++)
+ soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
}
+#endif
-static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
+static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
{
+ struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
const char *codecname = aux_dev->codec_name;
- int ret = -ENODEV;
- struct snd_soc_codec *codec = soc_find_matching_codec(card, num);
- if (!codec) {
+ rtd->codec = soc_find_codec(aux_dev->codec_of_node, codecname);
+ if (!rtd->codec) {
if (aux_dev->codec_of_node)
codecname = of_node_full_name(aux_dev->codec_of_node);
- /* codec not found */
- dev_err(card->dev, "ASoC: codec %s not found", codecname);
+ dev_err(card->dev, "ASoC: %s not registered\n", codecname);
return -EPROBE_DEFER;
}
- if (codec->probed) {
- dev_err(codec->dev, "ASoC: codec already probed");
+ return 0;
+}
+
+static int soc_probe_aux_dev(struct snd_soc_card *card, int num)
+{
+ struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
+ struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
+ int ret;
+
+ if (rtd->codec->probed) {
+ dev_err(rtd->codec->dev, "ASoC: codec already probed\n");
return -EBUSY;
}
- ret = soc_probe_codec(card, codec);
+ ret = soc_probe_codec(card, rtd->codec);
if (ret < 0)
return ret;
- ret = soc_post_component_init(card, codec, num, 1);
+ /* do machine specific initialization */
+ if (aux_dev->init) {
+ ret = aux_dev->init(&rtd->codec->dapm);
+ if (ret < 0) {
+ dev_err(card->dev, "ASoC: failed to init %s: %d\n",
+ aux_dev->name, ret);
+ return ret;
+ }
+ }
- return ret;
+ return soc_post_component_init(rtd, aux_dev->name);
}
static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
@@ -1749,9 +1745,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
goto base_error;
}
- /* check aux_devs too */
+ /* bind aux_devs too */
for (i = 0; i < card->num_aux_devs; i++) {
- ret = soc_check_aux_dev(card, i);
+ ret = soc_bind_aux_dev(card, i);
if (ret != 0)
goto base_error;
}
@@ -1849,16 +1845,23 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
card->num_dapm_routes);
for (i = 0; i < card->num_links; i++) {
+ struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
dai_link = &card->dai_link[i];
dai_fmt = dai_link->dai_fmt;
if (dai_fmt) {
- ret = snd_soc_dai_set_fmt(card->rtd[i].codec_dai,
- dai_fmt);
- if (ret != 0 && ret != -ENOTSUPP)
- dev_warn(card->rtd[i].codec_dai->dev,
- "ASoC: Failed to set DAI format: %d\n",
- ret);
+ struct snd_soc_dai **codec_dais = rtd->codec_dais;
+ int j;
+
+ for (j = 0; j < rtd->num_codecs; j++) {
+ struct snd_soc_dai *codec_dai = codec_dais[j];
+
+ ret = snd_soc_dai_set_fmt(codec_dai, dai_fmt);
+ if (ret != 0 && ret != -ENOTSUPP)
+ dev_warn(codec_dai->dev,
+ "ASoC: Failed to set DAI format: %d\n",
+ ret);
+ }
}
/* If this is a regular CPU link there will be a platform */
@@ -1927,8 +1930,7 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
}
if (card->fully_routed)
- list_for_each_entry(codec, &card->codec_dev_list, card_list)
- snd_soc_dapm_auto_nc_codec_pins(codec);
+ snd_soc_dapm_auto_nc_pins(card);
snd_soc_dapm_new_widgets(card);
@@ -2058,10 +2060,15 @@ int snd_soc_poweroff(struct device *dev)
/* deactivate pins to sleep state */
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
- pinctrl_pm_select_sleep_state(codec_dai->dev);
+ struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int j;
+
pinctrl_pm_select_sleep_state(cpu_dai->dev);
+ for (j = 0; j < rtd->num_codecs; j++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+ pinctrl_pm_select_sleep_state(codec_dai->dev);
+ }
}
return 0;
@@ -2387,6 +2394,25 @@ struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
/**
+ * snd_soc_add_component_controls - Add an array of controls to a component.
+ *
+ * @component: Component to add controls to
+ * @controls: Array of controls to add
+ * @num_controls: Number of elements in the array
+ *
+ * Return: 0 for success, else error.
+ */
+int snd_soc_add_component_controls(struct snd_soc_component *component,
+ const struct snd_kcontrol_new *controls, unsigned int num_controls)
+{
+ struct snd_card *card = component->card->snd_card;
+
+ return snd_soc_add_controls(card, component->dev, controls,
+ num_controls, component->name_prefix, component);
+}
+EXPORT_SYMBOL_GPL(snd_soc_add_component_controls);
+
+/**
* snd_soc_add_codec_controls - add an array of controls to a codec.
* Convenience function to add a list of controls. Many codecs were
* duplicating this code.
@@ -2398,12 +2424,10 @@ EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
* Return 0 for success, else error.
*/
int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
- const struct snd_kcontrol_new *controls, int num_controls)
+ const struct snd_kcontrol_new *controls, unsigned int num_controls)
{
- struct snd_card *card = codec->card->snd_card;
-
- return snd_soc_add_controls(card, codec->dev, controls, num_controls,
- codec->name_prefix, &codec->component);
+ return snd_soc_add_component_controls(&codec->component, controls,
+ num_controls);
}
EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
@@ -2418,12 +2442,10 @@ EXPORT_SYMBOL_GPL(snd_soc_add_codec_controls);
* Return 0 for success, else error.
*/
int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
- const struct snd_kcontrol_new *controls, int num_controls)
+ const struct snd_kcontrol_new *controls, unsigned int num_controls)
{
- struct snd_card *card = platform->card->snd_card;
-
- return snd_soc_add_controls(card, platform->dev, controls, num_controls,
- NULL, &platform->component);
+ return snd_soc_add_component_controls(&platform->component, controls,
+ num_controls);
}
EXPORT_SYMBOL_GPL(snd_soc_add_platform_controls);
@@ -3095,7 +3117,7 @@ EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
int snd_soc_limit_volume(struct snd_soc_codec *codec,
const char *name, int max)
{
- struct snd_card *card = codec->card->snd_card;
+ struct snd_card *card = codec->component.card->snd_card;
struct snd_kcontrol *kctl;
struct soc_mixer_control *mc;
int found = 0;
@@ -3641,6 +3663,9 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
else
snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
+ dai->tx_mask = tx_mask;
+ dai->rx_mask = rx_mask;
+
if (dai->driver && dai->driver->ops->set_tdm_slot)
return dai->driver->ops->set_tdm_slot(dai, tx_mask, rx_mask,
slots, slot_width);
@@ -3713,6 +3738,33 @@ int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
}
EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
+static int snd_soc_init_multicodec(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link)
+{
+ /* Legacy codec/codec_dai link is a single entry in multicodec */
+ if (dai_link->codec_name || dai_link->codec_of_node ||
+ dai_link->codec_dai_name) {
+ dai_link->num_codecs = 1;
+
+ dai_link->codecs = devm_kzalloc(card->dev,
+ sizeof(struct snd_soc_dai_link_component),
+ GFP_KERNEL);
+ if (!dai_link->codecs)
+ return -ENOMEM;
+
+ dai_link->codecs[0].name = dai_link->codec_name;
+ dai_link->codecs[0].of_node = dai_link->codec_of_node;
+ dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
+ }
+
+ if (!dai_link->codecs) {
+ dev_err(card->dev, "ASoC: DAI link has no CODECs\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* snd_soc_register_card - Register a card with the ASoC core
*
@@ -3721,7 +3773,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute);
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
- int i, ret;
+ int i, j, ret;
if (!card->name || !card->dev)
return -EINVAL;
@@ -3729,22 +3781,29 @@ int snd_soc_register_card(struct snd_soc_card *card)
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
- /*
- * Codec must be specified by 1 of name or OF node,
- * not both or neither.
- */
- if (!!link->codec_name == !!link->codec_of_node) {
- dev_err(card->dev,
- "ASoC: Neither/both codec name/of_node are set for %s\n",
- link->name);
- return -EINVAL;
+ ret = snd_soc_init_multicodec(card, link);
+ if (ret) {
+ dev_err(card->dev, "ASoC: failed to init multicodec\n");
+ return ret;
}
- /* Codec DAI name must be specified */
- if (!link->codec_dai_name) {
- dev_err(card->dev,
- "ASoC: codec_dai_name not set for %s\n",
- link->name);
- return -EINVAL;
+
+ for (j = 0; j < link->num_codecs; j++) {
+ /*
+ * Codec must be specified by 1 of name or OF node,
+ * not both or neither.
+ */
+ if (!!link->codecs[j].name ==
+ !!link->codecs[j].of_node) {
+ dev_err(card->dev, "ASoC: Neither/both codec name/of_node are set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
+ /* Codec DAI name must be specified */
+ if (!link->codecs[j].dai_name) {
+ dev_err(card->dev, "ASoC: codec_dai_name not set for %s\n",
+ link->name);
+ return -EINVAL;
+ }
}
/*
@@ -3797,8 +3856,19 @@ int snd_soc_register_card(struct snd_soc_card *card)
card->num_rtd = 0;
card->rtd_aux = &card->rtd[card->num_links];
- for (i = 0; i < card->num_links; i++)
+ for (i = 0; i < card->num_links; i++) {
+ card->rtd[i].card = card;
card->rtd[i].dai_link = &card->dai_link[i];
+ card->rtd[i].codec_dais = devm_kzalloc(card->dev,
+ sizeof(struct snd_soc_dai *) *
+ (card->rtd[i].dai_link->num_codecs),
+ GFP_KERNEL);
+ if (card->rtd[i].codec_dais == NULL)
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < card->num_aux_devs; i++)
+ card->rtd_aux[i].card = card;
INIT_LIST_HEAD(&card->dapm_dirty);
card->instantiated = 0;
@@ -3811,10 +3881,16 @@ int snd_soc_register_card(struct snd_soc_card *card)
/* deactivate pins to sleep state */
for (i = 0; i < card->num_rtd; i++) {
- struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_dai *codec_dai = card->rtd[i].codec_dai;
- if (!codec_dai->active)
- pinctrl_pm_select_sleep_state(codec_dai->dev);
+ struct snd_soc_pcm_runtime *rtd = &card->rtd[i];
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ int j;
+
+ for (j = 0; j < rtd->num_codecs; j++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[j];
+ if (!codec_dai->active)
+ pinctrl_pm_select_sleep_state(codec_dai->dev);
+ }
+
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
}
@@ -3921,16 +3997,14 @@ static void snd_soc_unregister_dais(struct snd_soc_component *component)
* snd_soc_register_dais - Register a DAI with the ASoC core
*
* @component: The component the DAIs are registered for
- * @codec: The CODEC that the DAIs are registered for, NULL if the component is
- * not a CODEC.
* @dai_drv: DAI driver to use for the DAIs
* @count: Number of DAIs
* @legacy_dai_naming: Use the legacy naming scheme and let the DAI inherit the
* parent's name.
*/
static int snd_soc_register_dais(struct snd_soc_component *component,
- struct snd_soc_codec *codec, struct snd_soc_dai_driver *dai_drv,
- size_t count, bool legacy_dai_naming)
+ struct snd_soc_dai_driver *dai_drv, size_t count,
+ bool legacy_dai_naming)
{
struct device *dev = component->dev;
struct snd_soc_dai *dai;
@@ -3939,6 +4013,9 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
dev_dbg(dev, "ASoC: dai register %s #%Zu\n", dev_name(dev), count);
+ component->dai_drv = dai_drv;
+ component->num_dai = count;
+
for (i = 0; i < count; i++) {
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
@@ -3971,16 +4048,11 @@ static int snd_soc_register_dais(struct snd_soc_component *component,
}
dai->component = component;
- dai->codec = codec;
dai->dev = dev;
dai->driver = &dai_drv[i];
- dai->dapm.dev = dev;
if (!dai->driver->ops)
dai->driver->ops = &null_dai_ops;
- if (!dai->codec)
- dai->dapm.idle_bias_off = 1;
-
list_add(&dai->list, &component->dai_list);
dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
@@ -3994,60 +4066,82 @@ err:
return ret;
}
-/**
- * snd_soc_register_component - Register a component with the ASoC core
- *
- */
-static int
-__snd_soc_register_component(struct device *dev,
- struct snd_soc_component *cmpnt,
- const struct snd_soc_component_driver *cmpnt_drv,
- struct snd_soc_codec *codec,
- struct snd_soc_dai_driver *dai_drv,
- int num_dai, bool allow_single_dai)
+static void snd_soc_component_seq_notifier(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_dapm_type type, int subseq)
{
- int ret;
+ struct snd_soc_component *component = dapm->component;
- dev_dbg(dev, "component register %s\n", dev_name(dev));
+ component->driver->seq_notifier(component, type, subseq);
+}
- if (!cmpnt) {
- dev_err(dev, "ASoC: Failed to connecting component\n");
- return -ENOMEM;
- }
+static int snd_soc_component_stream_event(struct snd_soc_dapm_context *dapm,
+ int event)
+{
+ struct snd_soc_component *component = dapm->component;
- mutex_init(&cmpnt->io_mutex);
+ return component->driver->stream_event(component, event);
+}
+
+static int snd_soc_component_initialize(struct snd_soc_component *component,
+ const struct snd_soc_component_driver *driver, struct device *dev)
+{
+ struct snd_soc_dapm_context *dapm;
- cmpnt->name = fmt_single_name(dev, &cmpnt->id);
- if (!cmpnt->name) {
- dev_err(dev, "ASoC: Failed to simplifying name\n");
+ component->name = fmt_single_name(dev, &component->id);
+ if (!component->name) {
+ dev_err(dev, "ASoC: Failed to allocate name\n");
return -ENOMEM;
}
- cmpnt->dev = dev;
- cmpnt->driver = cmpnt_drv;
- cmpnt->dai_drv = dai_drv;
- cmpnt->num_dai = num_dai;
- INIT_LIST_HEAD(&cmpnt->dai_list);
+ component->dev = dev;
+ component->driver = driver;
- ret = snd_soc_register_dais(cmpnt, codec, dai_drv, num_dai,
- allow_single_dai);
- if (ret < 0) {
- dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
- goto error_component_name;
- }
+ if (!component->dapm_ptr)
+ component->dapm_ptr = &component->dapm;
+
+ dapm = component->dapm_ptr;
+ dapm->dev = dev;
+ dapm->component = component;
+ dapm->bias_level = SND_SOC_BIAS_OFF;
+ if (driver->seq_notifier)
+ dapm->seq_notifier = snd_soc_component_seq_notifier;
+ if (driver->stream_event)
+ dapm->stream_event = snd_soc_component_stream_event;
+
+ INIT_LIST_HEAD(&component->dai_list);
+ mutex_init(&component->io_mutex);
+
+ return 0;
+}
+static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
+{
+ list_add(&component->list, &component_list);
+}
+
+static void snd_soc_component_add(struct snd_soc_component *component)
+{
mutex_lock(&client_mutex);
- list_add(&cmpnt->list, &component_list);
+ snd_soc_component_add_unlocked(component);
mutex_unlock(&client_mutex);
+}
- dev_dbg(cmpnt->dev, "ASoC: Registered component '%s'\n", cmpnt->name);
-
- return ret;
+static void snd_soc_component_cleanup(struct snd_soc_component *component)
+{
+ snd_soc_unregister_dais(component);
+ kfree(component->name);
+}
-error_component_name:
- kfree(cmpnt->name);
+static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
+{
+ list_del(&component->list);
+}
- return ret;
+static void snd_soc_component_del(struct snd_soc_component *component)
+{
+ mutex_lock(&client_mutex);
+ snd_soc_component_del_unlocked(component);
+ mutex_unlock(&client_mutex);
}
int snd_soc_register_component(struct device *dev,
@@ -4056,32 +4150,38 @@ int snd_soc_register_component(struct device *dev,
int num_dai)
{
struct snd_soc_component *cmpnt;
+ int ret;
- cmpnt = devm_kzalloc(dev, sizeof(*cmpnt), GFP_KERNEL);
+ cmpnt = kzalloc(sizeof(*cmpnt), GFP_KERNEL);
if (!cmpnt) {
dev_err(dev, "ASoC: Failed to allocate memory\n");
return -ENOMEM;
}
+ ret = snd_soc_component_initialize(cmpnt, cmpnt_drv, dev);
+ if (ret)
+ goto err_free;
+
cmpnt->ignore_pmdown_time = true;
cmpnt->registered_as_component = true;
- return __snd_soc_register_component(dev, cmpnt, cmpnt_drv, NULL,
- dai_drv, num_dai, true);
-}
-EXPORT_SYMBOL_GPL(snd_soc_register_component);
+ ret = snd_soc_register_dais(cmpnt, dai_drv, num_dai, true);
+ if (ret < 0) {
+ dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
+ goto err_cleanup;
+ }
-static void __snd_soc_unregister_component(struct snd_soc_component *cmpnt)
-{
- snd_soc_unregister_dais(cmpnt);
+ snd_soc_component_add(cmpnt);
- mutex_lock(&client_mutex);
- list_del(&cmpnt->list);
- mutex_unlock(&client_mutex);
+ return 0;
- dev_dbg(cmpnt->dev, "ASoC: Unregistered component '%s'\n", cmpnt->name);
- kfree(cmpnt->name);
+err_cleanup:
+ snd_soc_component_cleanup(cmpnt);
+err_free:
+ kfree(cmpnt);
+ return ret;
}
+EXPORT_SYMBOL_GPL(snd_soc_register_component);
/**
* snd_soc_unregister_component - Unregister a component from the ASoC core
@@ -4098,7 +4198,9 @@ void snd_soc_unregister_component(struct device *dev)
return;
found:
- __snd_soc_unregister_component(cmpnt);
+ snd_soc_component_del(cmpnt);
+ snd_soc_component_cleanup(cmpnt);
+ kfree(cmpnt);
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
@@ -4131,37 +4233,25 @@ int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
{
int ret;
- /* create platform component name */
- platform->name = fmt_single_name(dev, &platform->id);
- if (platform->name == NULL)
- return -ENOMEM;
+ ret = snd_soc_component_initialize(&platform->component,
+ &platform_drv->component_driver, dev);
+ if (ret)
+ return ret;
platform->dev = dev;
platform->driver = platform_drv;
- platform->dapm.dev = dev;
- platform->dapm.platform = platform;
- platform->dapm.component = &platform->component;
- platform->dapm.stream_event = platform_drv->stream_event;
if (platform_drv->write)
platform->component.write = snd_soc_platform_drv_write;
if (platform_drv->read)
platform->component.read = snd_soc_platform_drv_read;
- /* register component */
- ret = __snd_soc_register_component(dev, &platform->component,
- &platform_drv->component_driver,
- NULL, NULL, 0, false);
- if (ret < 0) {
- dev_err(platform->component.dev,
- "ASoC: Failed to register component: %d\n", ret);
- return ret;
- }
-
mutex_lock(&client_mutex);
+ snd_soc_component_add_unlocked(&platform->component);
list_add(&platform->list, &platform_list);
mutex_unlock(&client_mutex);
- dev_dbg(dev, "ASoC: Registered platform '%s'\n", platform->name);
+ dev_dbg(dev, "ASoC: Registered platform '%s'\n",
+ platform->component.name);
return 0;
}
@@ -4198,15 +4288,16 @@ EXPORT_SYMBOL_GPL(snd_soc_register_platform);
*/
void snd_soc_remove_platform(struct snd_soc_platform *platform)
{
- __snd_soc_unregister_component(&platform->component);
mutex_lock(&client_mutex);
list_del(&platform->list);
+ snd_soc_component_del_unlocked(&platform->component);
mutex_unlock(&client_mutex);
+ snd_soc_component_cleanup(&platform->component);
+
dev_dbg(platform->dev, "ASoC: Unregistered platform '%s'\n",
- platform->name);
- kfree(platform->name);
+ platform->component.name);
}
EXPORT_SYMBOL_GPL(snd_soc_remove_platform);
@@ -4292,6 +4383,14 @@ static int snd_soc_codec_drv_read(struct snd_soc_component *component,
return 0;
}
+static int snd_soc_codec_set_bias_level(struct snd_soc_dapm_context *dapm,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+
+ return codec->driver->set_bias_level(codec, level);
+}
+
/**
* snd_soc_register_codec - Register a codec with the ASoC core
*
@@ -4303,6 +4402,7 @@ int snd_soc_register_codec(struct device *dev,
int num_dai)
{
struct snd_soc_codec *codec;
+ struct snd_soc_dai *dai;
struct regmap *regmap;
int ret, i;
@@ -4312,24 +4412,23 @@ int snd_soc_register_codec(struct device *dev,
if (codec == NULL)
return -ENOMEM;
- /* create CODEC component name */
- codec->name = fmt_single_name(dev, &codec->id);
- if (codec->name == NULL) {
- ret = -ENOMEM;
- goto fail_codec;
- }
+ codec->component.dapm_ptr = &codec->dapm;
+
+ ret = snd_soc_component_initialize(&codec->component,
+ &codec_drv->component_driver, dev);
+ if (ret)
+ goto err_free;
if (codec_drv->write)
codec->component.write = snd_soc_codec_drv_write;
if (codec_drv->read)
codec->component.read = snd_soc_codec_drv_read;
codec->component.ignore_pmdown_time = codec_drv->ignore_pmdown_time;
- codec->dapm.bias_level = SND_SOC_BIAS_OFF;
- codec->dapm.dev = dev;
codec->dapm.codec = codec;
- codec->dapm.component = &codec->component;
- codec->dapm.seq_notifier = codec_drv->seq_notifier;
- codec->dapm.stream_event = codec_drv->stream_event;
+ if (codec_drv->seq_notifier)
+ codec->dapm.seq_notifier = codec_drv->seq_notifier;
+ if (codec_drv->set_bias_level)
+ codec->dapm.set_bias_level = snd_soc_codec_set_bias_level;
codec->dev = dev;
codec->driver = codec_drv;
codec->component.val_bytes = codec_drv->reg_word_size;
@@ -4348,7 +4447,7 @@ int snd_soc_register_codec(struct device *dev,
dev_err(codec->dev,
"Failed to set cache I/O:%d\n",
ret);
- return ret;
+ goto err_cleanup;
}
}
}
@@ -4358,29 +4457,27 @@ int snd_soc_register_codec(struct device *dev,
fixup_codec_formats(&dai_drv[i].capture);
}
- mutex_lock(&client_mutex);
- list_add(&codec->list, &codec_list);
- mutex_unlock(&client_mutex);
-
- /* register component */
- ret = __snd_soc_register_component(dev, &codec->component,
- &codec_drv->component_driver,
- codec, dai_drv, num_dai, false);
+ ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
if (ret < 0) {
- dev_err(codec->dev, "ASoC: Failed to regster component: %d\n", ret);
- goto fail_codec_name;
+ dev_err(dev, "ASoC: Failed to regster DAIs: %d\n", ret);
+ goto err_cleanup;
}
- dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n", codec->name);
- return 0;
+ list_for_each_entry(dai, &codec->component.dai_list, list)
+ dai->codec = codec;
-fail_codec_name:
mutex_lock(&client_mutex);
- list_del(&codec->list);
+ snd_soc_component_add_unlocked(&codec->component);
+ list_add(&codec->list, &codec_list);
mutex_unlock(&client_mutex);
- kfree(codec->name);
-fail_codec:
+ dev_dbg(codec->dev, "ASoC: Registered codec '%s'\n",
+ codec->component.name);
+ return 0;
+
+err_cleanup:
+ snd_soc_component_cleanup(&codec->component);
+err_free:
kfree(codec);
return ret;
}
@@ -4402,16 +4499,17 @@ void snd_soc_unregister_codec(struct device *dev)
return;
found:
- __snd_soc_unregister_component(&codec->component);
mutex_lock(&client_mutex);
list_del(&codec->list);
+ snd_soc_component_del_unlocked(&codec->component);
mutex_unlock(&client_mutex);
- dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n", codec->name);
+ dev_dbg(codec->dev, "ASoC: Unregistered codec '%s'\n",
+ codec->component.name);
+ snd_soc_component_cleanup(&codec->component);
snd_soc_cache_exit(codec);
- kfree(codec->name);
kfree(codec);
}
EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
@@ -4420,9 +4518,16 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_codec);
int snd_soc_of_parse_card_name(struct snd_soc_card *card,
const char *propname)
{
- struct device_node *np = card->dev->of_node;
+ struct device_node *np;
int ret;
+ if (!card->dev) {
+ pr_err("card->dev is not set before calling %s\n", __func__);
+ return -EINVAL;
+ }
+
+ np = card->dev->of_node;
+
ret = of_property_read_string_index(np, propname, 0, &card->name);
/*
* EINVAL means the property does not exist. This is fine providing
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index a74b9bf..8348352 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -350,12 +350,27 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
}
/**
+ * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
+ * kcontrol
+ * @kcontrol: The kcontrol
+ *
+ * Note: This function must only be used on kcontrols that are known to have
+ * been registered for a CODEC. Otherwise the behaviour is undefined.
+ */
+struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
+ struct snd_kcontrol *kcontrol)
+{
+ return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->dapm;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_dapm);
+
+/**
* snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
* @kcontrol: The kcontrol
*/
struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol)
{
- return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec;
+ return snd_soc_dapm_to_codec(snd_soc_dapm_kcontrol_dapm(kcontrol));
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
@@ -375,23 +390,38 @@ static void dapm_reset(struct snd_soc_card *card)
}
}
-static int soc_widget_read(struct snd_soc_dapm_widget *w, int reg,
+static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm)
+{
+ if (!dapm->component)
+ return NULL;
+ return dapm->component->name_prefix;
+}
+
+static int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg,
unsigned int *value)
{
- if (!w->dapm->component)
+ if (!dapm->component)
return -EIO;
- return snd_soc_component_read(w->dapm->component, reg, value);
+ return snd_soc_component_read(dapm->component, reg, value);
}
-static int soc_widget_update_bits(struct snd_soc_dapm_widget *w,
+static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
int reg, unsigned int mask, unsigned int value)
{
- if (!w->dapm->component)
+ if (!dapm->component)
return -EIO;
- return snd_soc_component_update_bits_async(w->dapm->component, reg,
+ return snd_soc_component_update_bits_async(dapm->component, reg,
mask, value);
}
+static int soc_dapm_test_bits(struct snd_soc_dapm_context *dapm,
+ int reg, unsigned int mask, unsigned int value)
+{
+ if (!dapm->component)
+ return -EIO;
+ return snd_soc_component_test_bits(dapm->component, reg, mask, value);
+}
+
static void soc_dapm_async_complete(struct snd_soc_dapm_context *dapm)
{
if (dapm->component)
@@ -420,15 +450,10 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
if (ret != 0)
goto out;
- if (dapm->codec) {
- if (dapm->codec->driver->set_bias_level)
- ret = dapm->codec->driver->set_bias_level(dapm->codec,
- level);
- else
- dapm->bias_level = level;
- } else if (!card || dapm != &card->dapm) {
+ if (dapm->set_bias_level)
+ ret = dapm->set_bias_level(dapm, level);
+ else if (!card || dapm != &card->dapm)
dapm->bias_level = level;
- }
if (ret != 0)
goto out;
@@ -452,7 +477,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
int i;
if (e->reg != SND_SOC_NOPM) {
- soc_widget_read(dest, e->reg, &val);
+ soc_dapm_read(dapm, e->reg, &val);
val = (val >> e->shift_l) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
} else {
@@ -496,7 +521,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
unsigned int val;
if (reg != SND_SOC_NOPM) {
- soc_widget_read(w, reg, &val);
+ soc_dapm_read(w->dapm, reg, &val);
val = (val >> shift) & mask;
if (invert)
val = max - val;
@@ -570,11 +595,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
const char *name;
int ret;
- if (dapm->codec)
- prefix = dapm->codec->name_prefix;
- else
- prefix = NULL;
-
+ prefix = soc_dapm_prefix(dapm);
if (prefix)
prefix_len = strlen(prefix) + 1;
else
@@ -1308,16 +1329,18 @@ static void dapm_seq_check_event(struct snd_soc_card *card,
static void dapm_seq_run_coalesced(struct snd_soc_card *card,
struct list_head *pending)
{
+ struct snd_soc_dapm_context *dapm;
struct snd_soc_dapm_widget *w;
int reg;
unsigned int value = 0;
unsigned int mask = 0;
- reg = list_first_entry(pending, struct snd_soc_dapm_widget,
- power_list)->reg;
+ w = list_first_entry(pending, struct snd_soc_dapm_widget, power_list);
+ reg = w->reg;
+ dapm = w->dapm;
list_for_each_entry(w, pending, power_list) {
- WARN_ON(reg != w->reg);
+ WARN_ON(reg != w->reg || dapm != w->dapm);
w->power = w->new_power;
mask |= w->mask << w->shift;
@@ -1326,7 +1349,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
else
value |= w->off_val << w->shift;
- pop_dbg(w->dapm->dev, card->pop_time,
+ pop_dbg(dapm->dev, card->pop_time,
"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
w->name, reg, value, mask);
@@ -1339,14 +1362,12 @@ static void dapm_seq_run_coalesced(struct snd_soc_card *card,
/* Any widget will do, they should all be updating the
* same register.
*/
- w = list_first_entry(pending, struct snd_soc_dapm_widget,
- power_list);
- pop_dbg(w->dapm->dev, card->pop_time,
+ pop_dbg(dapm->dev, card->pop_time,
"pop test : Applying 0x%x/0x%x to %x in %dms\n",
value, mask, reg, card->pop_time);
pop_wait(card->pop_time);
- soc_widget_update_bits(w, reg, mask, value);
+ soc_dapm_update_bits(dapm, reg, mask, value);
}
list_for_each_entry(w, pending, power_list) {
@@ -1492,7 +1513,8 @@ static void dapm_widget_update(struct snd_soc_card *card)
if (!w)
return;
- ret = soc_widget_update_bits(w, update->reg, update->mask, update->val);
+ ret = soc_dapm_update_bits(w->dapm, update->reg, update->mask,
+ update->val);
if (ret < 0)
dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
w->name, ret);
@@ -2062,17 +2084,13 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
-/* show dapm widget status in sys fs */
-static ssize_t dapm_widget_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t dapm_widget_show_codec(struct snd_soc_codec *codec, char *buf)
{
- struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
- struct snd_soc_codec *codec =rtd->codec;
struct snd_soc_dapm_widget *w;
int count = 0;
char *state = "not set";
- list_for_each_entry(w, &codec->card->widgets, list) {
+ list_for_each_entry(w, &codec->component.card->widgets, list) {
if (w->dapm != &codec->dapm)
continue;
@@ -2120,6 +2138,21 @@ static ssize_t dapm_widget_show(struct device *dev,
return count;
}
+/* show dapm widget status in sys fs */
+static ssize_t dapm_widget_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
+ int i, count = 0;
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_codec *codec = rtd->codec_dais[i]->codec;
+ count += dapm_widget_show_codec(codec, buf + count);
+ }
+
+ return count;
+}
+
static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL);
int snd_soc_dapm_sys_add(struct device *dev)
@@ -2371,14 +2404,16 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
const char *source;
char prefixed_sink[80];
char prefixed_source[80];
+ const char *prefix;
int ret;
- if (dapm->codec && dapm->codec->name_prefix) {
+ prefix = soc_dapm_prefix(dapm);
+ if (prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
- dapm->codec->name_prefix, route->sink);
+ prefix, route->sink);
sink = prefixed_sink;
snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
- dapm->codec->name_prefix, route->source);
+ prefix, route->source);
source = prefixed_source;
} else {
sink = route->sink;
@@ -2439,6 +2474,7 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
const char *source;
char prefixed_sink[80];
char prefixed_source[80];
+ const char *prefix;
if (route->control) {
dev_err(dapm->dev,
@@ -2446,12 +2482,13 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}
- if (dapm->codec && dapm->codec->name_prefix) {
+ prefix = soc_dapm_prefix(dapm);
+ if (prefix) {
snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
- dapm->codec->name_prefix, route->sink);
+ prefix, route->sink);
sink = prefixed_sink;
snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
- dapm->codec->name_prefix, route->source);
+ prefix, route->source);
source = prefixed_source;
} else {
sink = route->sink;
@@ -2670,7 +2707,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
/* Read the initial power state from the device */
if (w->reg >= 0) {
- soc_widget_read(w, w->reg, &val);
+ soc_dapm_read(w->dapm, w->reg, &val);
val = val >> w->shift;
val &= w->mask;
if (val == w->on_val)
@@ -2701,8 +2738,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_card *card = dapm->card;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int reg = mc->reg;
@@ -2711,17 +2748,20 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val;
+ int ret = 0;
if (snd_soc_volsw_is_stereo(mc))
- dev_warn(codec->dapm.dev,
+ dev_warn(dapm->dev,
"ASoC: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM)
- val = (snd_soc_read(codec, reg) >> shift) & mask;
- else
+ if (dapm_kcontrol_is_powered(kcontrol) && reg != SND_SOC_NOPM) {
+ ret = soc_dapm_read(dapm, reg, &val);
+ val = (val >> shift) & mask;
+ } else {
val = dapm_kcontrol_get_value(kcontrol);
+ }
mutex_unlock(&card->dapm_mutex);
if (invert)
@@ -2729,7 +2769,7 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
else
ucontrol->value.integer.value[0] = val;
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
@@ -2745,8 +2785,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_card *card = dapm->card;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int reg = mc->reg;
@@ -2755,12 +2795,12 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val;
- int connect, change;
+ int connect, change, reg_change = 0;
struct snd_soc_dapm_update update;
int ret = 0;
if (snd_soc_volsw_is_stereo(mc))
- dev_warn(codec->dapm.dev,
+ dev_warn(dapm->dev,
"ASoC: Control '%s' is stereo, which is not supported\n",
kcontrol->id.name);
@@ -2773,20 +2813,23 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
change = dapm_kcontrol_set_value(kcontrol, val);
- if (change) {
- if (reg != SND_SOC_NOPM) {
- mask = mask << shift;
- val = val << shift;
-
- if (snd_soc_test_bits(codec, reg, mask, val)) {
- update.kcontrol = kcontrol;
- update.reg = reg;
- update.mask = mask;
- update.val = val;
- card->update = &update;
- }
+ if (reg != SND_SOC_NOPM) {
+ mask = mask << shift;
+ val = val << shift;
+
+ reg_change = soc_dapm_test_bits(dapm, reg, mask, val);
+ }
+
+ if (change || reg_change) {
+ if (reg_change) {
+ update.kcontrol = kcontrol;
+ update.reg = reg;
+ update.mask = mask;
+ update.val = val;
+ card->update = &update;
}
+ change |= reg_change;
ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
@@ -2814,12 +2857,13 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_val, val;
+ int ret = 0;
if (e->reg != SND_SOC_NOPM)
- reg_val = snd_soc_read(codec, e->reg);
+ ret = soc_dapm_read(dapm, e->reg, &reg_val);
else
reg_val = dapm_kcontrol_get_value(kcontrol);
@@ -2831,7 +2875,7 @@ int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
ucontrol->value.enumerated.item[1] = val;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
@@ -2847,8 +2891,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_card *card = dapm->card;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int val, change;
@@ -2871,7 +2915,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
if (e->reg != SND_SOC_NOPM)
- change = snd_soc_test_bits(codec, e->reg, mask, val);
+ change = soc_dapm_test_bits(dapm, e->reg, mask, val);
else
change = dapm_kcontrol_set_value(kcontrol, val);
@@ -2968,6 +3012,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
{
struct snd_soc_dapm_widget *w;
+ const char *prefix;
int ret;
if ((w = dapm_cnew_widget(widget)) == NULL)
@@ -3008,9 +3053,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
break;
}
- if (dapm->codec && dapm->codec->name_prefix)
- w->name = kasprintf(GFP_KERNEL, "%s %s",
- dapm->codec->name_prefix, widget->name);
+ prefix = soc_dapm_prefix(dapm);
+ if (prefix)
+ w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
else
w->name = kasprintf(GFP_KERNEL, "%s", widget->name);
@@ -3063,7 +3108,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
w->dapm = dapm;
w->codec = dapm->codec;
- w->platform = dapm->platform;
INIT_LIST_HEAD(&w->sources);
INIT_LIST_HEAD(&w->sinks);
INIT_LIST_HEAD(&w->list);
@@ -3170,27 +3214,15 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- if (source->driver->ops && source->driver->ops->hw_params) {
- substream.stream = SNDRV_PCM_STREAM_CAPTURE;
- ret = source->driver->ops->hw_params(&substream,
- params, source);
- if (ret != 0) {
- dev_err(source->dev,
- "ASoC: hw_params() failed: %d\n", ret);
- goto out;
- }
- }
+ substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+ ret = soc_dai_hw_params(&substream, params, source);
+ if (ret < 0)
+ goto out;
- if (sink->driver->ops && sink->driver->ops->hw_params) {
- substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
- ret = sink->driver->ops->hw_params(&substream, params,
- sink);
- if (ret != 0) {
- dev_err(sink->dev,
- "ASoC: hw_params() failed: %d\n", ret);
- goto out;
- }
- }
+ substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+ ret = soc_dai_hw_params(&substream, params, sink);
+ if (ret < 0)
+ goto out;
break;
case SND_SOC_DAPM_POST_PMU:
@@ -3362,25 +3394,15 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
return 0;
}
-void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
+static void dapm_connect_dai_link_widgets(struct snd_soc_card *card,
+ struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_pcm_runtime *rtd = card->rtd;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dapm_widget *sink, *source;
- struct snd_soc_dai *cpu_dai, *codec_dai;
int i;
- /* for each BE DAI link... */
- for (i = 0; i < card->num_rtd; i++) {
- rtd = &card->rtd[i];
- cpu_dai = rtd->cpu_dai;
- codec_dai = rtd->codec_dai;
-
- /*
- * dynamic FE links have no fixed DAI mapping.
- * CODEC<->CODEC links have no direct connection.
- */
- if (rtd->dai_link->dynamic || rtd->dai_link->params)
- continue;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
/* there is no point in connecting BE DAI links with dummies */
if (snd_soc_dai_is_dummy(codec_dai) ||
@@ -3392,8 +3414,8 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
source = cpu_dai->playback_widget;
sink = codec_dai->playback_widget;
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
- cpu_dai->codec->name, source->name,
- codec_dai->platform->name, sink->name);
+ cpu_dai->component->name, source->name,
+ codec_dai->component->name, sink->name);
snd_soc_dapm_add_path(&card->dapm, source, sink,
NULL, NULL);
@@ -3404,8 +3426,8 @@ void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
source = codec_dai->capture_widget;
sink = cpu_dai->capture_widget;
dev_dbg(rtd->dev, "connected DAI link %s:%s -> %s:%s\n",
- codec_dai->codec->name, source->name,
- cpu_dai->platform->name, sink->name);
+ codec_dai->component->name, source->name,
+ cpu_dai->component->name, sink->name);
snd_soc_dapm_add_path(&card->dapm, source, sink,
NULL, NULL);
@@ -3442,11 +3464,34 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
}
}
+void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd = card->rtd;
+ int i;
+
+ /* for each BE DAI link... */
+ for (i = 0; i < card->num_rtd; i++) {
+ rtd = &card->rtd[i];
+
+ /*
+ * dynamic FE links have no fixed DAI mapping.
+ * CODEC<->CODEC links have no direct connection.
+ */
+ if (rtd->dai_link->dynamic || rtd->dai_link->params)
+ continue;
+
+ dapm_connect_dai_link_widgets(card, rtd);
+ }
+}
+
static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
int event)
{
+ int i;
+
soc_dapm_dai_stream_event(rtd->cpu_dai, stream, event);
- soc_dapm_dai_stream_event(rtd->codec_dai, stream, event);
+ for (i = 0; i < rtd->num_codecs; i++)
+ soc_dapm_dai_stream_event(rtd->codec_dais[i], stream, event);
dapm_power_widgets(rtd->card, event);
}
@@ -3755,36 +3800,31 @@ static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
}
/**
- * snd_soc_dapm_auto_nc_codec_pins - call snd_soc_dapm_nc_pin for unused pins
- * @codec: The codec whose pins should be processed
+ * snd_soc_dapm_auto_nc_pins - call snd_soc_dapm_nc_pin for unused pins
+ * @card: The card whose pins should be processed
*
- * Automatically call snd_soc_dapm_nc_pin() for any external pins in the codec
- * which are unused. Pins are used if they are connected externally to the
- * codec, whether that be to some other device, or a loop-back connection to
- * the codec itself.
+ * Automatically call snd_soc_dapm_nc_pin() for any external pins in the card
+ * which are unused. Pins are used if they are connected externally to a
+ * component, whether that be to some other device, or a loop-back connection to
+ * the component itself.
*/
-void snd_soc_dapm_auto_nc_codec_pins(struct snd_soc_codec *codec)
+void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card)
{
- struct snd_soc_card *card = codec->card;
- struct snd_soc_dapm_context *dapm = &codec->dapm;
struct snd_soc_dapm_widget *w;
- dev_dbg(codec->dev, "ASoC: Auto NC: DAPMs: card:%p codec:%p\n",
- &card->dapm, &codec->dapm);
+ dev_dbg(card->dev, "ASoC: Auto NC: DAPMs: card:%p\n", &card->dapm);
list_for_each_entry(w, &card->widgets, list) {
- if (w->dapm != dapm)
- continue;
switch (w->id) {
case snd_soc_dapm_input:
case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
- dev_dbg(codec->dev, "ASoC: Auto NC: Checking widget %s\n",
+ dev_dbg(card->dev, "ASoC: Auto NC: Checking widget %s\n",
w->name);
if (!snd_soc_dapm_widget_in_card_paths(card, w)) {
- dev_dbg(codec->dev,
+ dev_dbg(card->dev,
"... Not in map; disabling\n");
- snd_soc_dapm_nc_pin(dapm, w->name);
+ snd_soc_dapm_nc_pin(w->dapm, w->name);
}
break;
default:
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 5bace12..6307f85 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -119,7 +119,10 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
struct snd_dmaengine_dai_dma_data *dma_data;
struct dma_slave_caps dma_caps;
struct snd_pcm_hardware hw;
- int ret;
+ u32 addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) |
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) |
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ int i, ret;
if (pcm->config && pcm->config->pcm_hardware)
return snd_soc_set_runtime_hwparams(substream,
@@ -146,6 +149,38 @@ static int dmaengine_pcm_set_runtime_hwparams(struct snd_pcm_substream *substrea
hw.info |= SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME;
if (dma_caps.residue_granularity <= DMA_RESIDUE_GRANULARITY_SEGMENT)
hw.info |= SNDRV_PCM_INFO_BATCH;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ addr_widths = dma_caps.dstn_addr_widths;
+ else
+ addr_widths = dma_caps.src_addr_widths;
+ }
+
+ /*
+ * Prepare formats mask for valid/allowed sample types. If the dma does
+ * not have support for the given physical word size, it needs to be
+ * masked out so user space can not use the format which produces
+ * corrupted audio.
+ * In case the dma driver does not implement the slave_caps the default
+ * assumption is that it supports 1, 2 and 4 bytes widths.
+ */
+ for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
+ int bits = snd_pcm_format_physical_width(i);
+
+ /* Enable only samples with DMA supported physical widths */
+ switch (bits) {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ case 64:
+ if (addr_widths & (1 << (bits / 8)))
+ hw.formats |= (1LL << i);
+ break;
+ default:
+ /* Unsupported types */
+ break;
+ }
}
return snd_soc_set_runtime_hwparams(substream, &hw);
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index d0d9881..ab47fea 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -43,7 +43,7 @@ int snd_soc_jack_new(struct snd_soc_codec *codec, const char *id, int type,
INIT_LIST_HEAD(&jack->jack_zones);
BLOCKING_INIT_NOTIFIER_HEAD(&jack->notifier);
- return snd_jack_new(codec->card->snd_card, id, type, &jack->jack);
+ return snd_jack_new(codec->component.card->snd_card, id, type, &jack->jack);
}
EXPORT_SYMBOL_GPL(snd_soc_jack_new);
@@ -260,7 +260,7 @@ static void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
static irqreturn_t gpio_handler(int irq, void *data)
{
struct snd_soc_jack_gpio *gpio = data;
- struct device *dev = gpio->jack->codec->card->dev;
+ struct device *dev = gpio->jack->codec->component.card->dev;
trace_snd_soc_jack_irq(gpio->name);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 54d18f2..731fdb5 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -7,7 +7,7 @@
* Copyright (C) 2010 Texas Instruments Inc.
*
* Authors: Liam Girdwood <lrg@ti.com>
- * Mark Brown <broonie@opensource.wolfsonmicro.com>
+ * Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -47,22 +47,26 @@
void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int i;
lockdep_assert_held(&rtd->pcm_mutex);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active++;
- codec_dai->playback_active++;
+ for (i = 0; i < rtd->num_codecs; i++)
+ rtd->codec_dais[i]->playback_active++;
} else {
cpu_dai->capture_active++;
- codec_dai->capture_active++;
+ for (i = 0; i < rtd->num_codecs; i++)
+ rtd->codec_dais[i]->capture_active++;
}
cpu_dai->active++;
- codec_dai->active++;
cpu_dai->component->active++;
- codec_dai->component->active++;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ rtd->codec_dais[i]->active++;
+ rtd->codec_dais[i]->component->active++;
+ }
}
/**
@@ -78,22 +82,26 @@ void snd_soc_runtime_activate(struct snd_soc_pcm_runtime *rtd, int stream)
void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
{
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int i;
lockdep_assert_held(&rtd->pcm_mutex);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
cpu_dai->playback_active--;
- codec_dai->playback_active--;
+ for (i = 0; i < rtd->num_codecs; i++)
+ rtd->codec_dais[i]->playback_active--;
} else {
cpu_dai->capture_active--;
- codec_dai->capture_active--;
+ for (i = 0; i < rtd->num_codecs; i++)
+ rtd->codec_dais[i]->capture_active--;
}
cpu_dai->active--;
- codec_dai->active--;
cpu_dai->component->active--;
- codec_dai->component->active--;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ rtd->codec_dais[i]->component->active--;
+ rtd->codec_dais[i]->active--;
+ }
}
/**
@@ -107,11 +115,16 @@ void snd_soc_runtime_deactivate(struct snd_soc_pcm_runtime *rtd, int stream)
*/
bool snd_soc_runtime_ignore_pmdown_time(struct snd_soc_pcm_runtime *rtd)
{
+ int i;
+ bool ignore = true;
+
if (!rtd->pmdown_time || rtd->dai_link->ignore_pmdown_time)
return true;
- return rtd->cpu_dai->component->ignore_pmdown_time &&
- rtd->codec_dai->component->ignore_pmdown_time;
+ for (i = 0; i < rtd->num_codecs; i++)
+ ignore &= rtd->codec_dais[i]->component->ignore_pmdown_time;
+
+ return rtd->cpu_dai->component->ignore_pmdown_time && ignore;
}
/**
@@ -222,8 +235,7 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- unsigned int rate, channels, sample_bits, symmetry;
+ unsigned int rate, channels, sample_bits, symmetry, i;
rate = params_rate(params);
channels = params_channels(params);
@@ -231,8 +243,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
/* reject unmatched parameters when applying symmetry */
symmetry = cpu_dai->driver->symmetric_rates ||
- codec_dai->driver->symmetric_rates ||
rtd->dai_link->symmetric_rates;
+
+ for (i = 0; i < rtd->num_codecs; i++)
+ symmetry |= rtd->codec_dais[i]->driver->symmetric_rates;
+
if (symmetry && cpu_dai->rate && cpu_dai->rate != rate) {
dev_err(rtd->dev, "ASoC: unmatched rate symmetry: %d - %d\n",
cpu_dai->rate, rate);
@@ -240,8 +255,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
}
symmetry = cpu_dai->driver->symmetric_channels ||
- codec_dai->driver->symmetric_channels ||
rtd->dai_link->symmetric_channels;
+
+ for (i = 0; i < rtd->num_codecs; i++)
+ symmetry |= rtd->codec_dais[i]->driver->symmetric_channels;
+
if (symmetry && cpu_dai->channels && cpu_dai->channels != channels) {
dev_err(rtd->dev, "ASoC: unmatched channel symmetry: %d - %d\n",
cpu_dai->channels, channels);
@@ -249,8 +267,11 @@ static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
}
symmetry = cpu_dai->driver->symmetric_samplebits ||
- codec_dai->driver->symmetric_samplebits ||
rtd->dai_link->symmetric_samplebits;
+
+ for (i = 0; i < rtd->num_codecs; i++)
+ symmetry |= rtd->codec_dais[i]->driver->symmetric_samplebits;
+
if (symmetry && cpu_dai->sample_bits && cpu_dai->sample_bits != sample_bits) {
dev_err(rtd->dev, "ASoC: unmatched sample bits symmetry: %d - %d\n",
cpu_dai->sample_bits, sample_bits);
@@ -264,15 +285,20 @@ static bool soc_pcm_has_symmetry(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai_driver *cpu_driver = rtd->cpu_dai->driver;
- struct snd_soc_dai_driver *codec_driver = rtd->codec_dai->driver;
struct snd_soc_dai_link *link = rtd->dai_link;
+ unsigned int symmetry, i;
- return cpu_driver->symmetric_rates || codec_driver->symmetric_rates ||
- link->symmetric_rates || cpu_driver->symmetric_channels ||
- codec_driver->symmetric_channels || link->symmetric_channels ||
- cpu_driver->symmetric_samplebits ||
- codec_driver->symmetric_samplebits ||
- link->symmetric_samplebits;
+ symmetry = cpu_driver->symmetric_rates || link->symmetric_rates ||
+ cpu_driver->symmetric_channels || link->symmetric_channels ||
+ cpu_driver->symmetric_samplebits || link->symmetric_samplebits;
+
+ for (i = 0; i < rtd->num_codecs; i++)
+ symmetry = symmetry ||
+ rtd->codec_dais[i]->driver->symmetric_rates ||
+ rtd->codec_dais[i]->driver->symmetric_channels ||
+ rtd->codec_dais[i]->driver->symmetric_samplebits;
+
+ return symmetry;
}
/*
@@ -284,15 +310,10 @@ static int sample_sizes[] = {
24, 32,
};
-static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
{
- int ret, i, bits;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- bits = dai->driver->playback.sig_bits;
- else
- bits = dai->driver->capture.sig_bits;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ int ret, i;
if (!bits)
return;
@@ -304,38 +325,105 @@ static void soc_pcm_apply_msb(struct snd_pcm_substream *substream,
ret = snd_pcm_hw_constraint_msbits(substream->runtime, 0,
sample_sizes[i], bits);
if (ret != 0)
- dev_warn(dai->dev,
+ dev_warn(rtd->dev,
"ASoC: Failed to set MSB %d/%d: %d\n",
bits, sample_sizes[i], ret);
}
}
-static void soc_pcm_init_runtime_hw(struct snd_pcm_runtime *runtime,
- struct snd_soc_pcm_stream *codec_stream,
- struct snd_soc_pcm_stream *cpu_stream)
+static void soc_pcm_apply_msb(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai;
+ int i;
+ unsigned int bits = 0, cpu_bits;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->playback.sig_bits == 0) {
+ bits = 0;
+ break;
+ }
+ bits = max(codec_dai->driver->playback.sig_bits, bits);
+ }
+ cpu_bits = cpu_dai->driver->playback.sig_bits;
+ } else {
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->playback.sig_bits == 0) {
+ bits = 0;
+ break;
+ }
+ bits = max(codec_dai->driver->capture.sig_bits, bits);
+ }
+ cpu_bits = cpu_dai->driver->capture.sig_bits;
+ }
+
+ soc_pcm_set_msb(substream, bits);
+ soc_pcm_set_msb(substream, cpu_bits);
+}
+
+static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hardware *hw = &runtime->hw;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai_driver *cpu_dai_drv = rtd->cpu_dai->driver;
+ struct snd_soc_dai_driver *codec_dai_drv;
+ struct snd_soc_pcm_stream *codec_stream;
+ struct snd_soc_pcm_stream *cpu_stream;
+ unsigned int chan_min = 0, chan_max = UINT_MAX;
+ unsigned int rate_min = 0, rate_max = UINT_MAX;
+ unsigned int rates = UINT_MAX;
+ u64 formats = ULLONG_MAX;
+ int i;
- hw->channels_min = max(codec_stream->channels_min,
- cpu_stream->channels_min);
- hw->channels_max = min(codec_stream->channels_max,
- cpu_stream->channels_max);
- if (hw->formats)
- hw->formats &= codec_stream->formats & cpu_stream->formats;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ cpu_stream = &cpu_dai_drv->playback;
else
- hw->formats = codec_stream->formats & cpu_stream->formats;
- hw->rates = snd_pcm_rate_mask_intersect(codec_stream->rates,
- cpu_stream->rates);
+ cpu_stream = &cpu_dai_drv->capture;
- hw->rate_min = 0;
- hw->rate_max = UINT_MAX;
+ /* first calculate min/max only for CODECs in the DAI link */
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai_drv = rtd->codec_dais[i]->driver;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_stream = &codec_dai_drv->playback;
+ else
+ codec_stream = &codec_dai_drv->capture;
+ chan_min = max(chan_min, codec_stream->channels_min);
+ chan_max = min(chan_max, codec_stream->channels_max);
+ rate_min = max(rate_min, codec_stream->rate_min);
+ rate_max = min_not_zero(rate_max, codec_stream->rate_max);
+ formats &= codec_stream->formats;
+ rates = snd_pcm_rate_mask_intersect(codec_stream->rates, rates);
+ }
+
+ /*
+ * chan min/max cannot be enforced if there are multiple CODEC DAIs
+ * connected to a single CPU DAI, use CPU DAI's directly and let
+ * channel allocation be fixed up later
+ */
+ if (rtd->num_codecs > 1) {
+ chan_min = cpu_stream->channels_min;
+ chan_max = cpu_stream->channels_max;
+ }
+
+ hw->channels_min = max(chan_min, cpu_stream->channels_min);
+ hw->channels_max = min(chan_max, cpu_stream->channels_max);
+ if (hw->formats)
+ hw->formats &= formats & cpu_stream->formats;
+ else
+ hw->formats = formats & cpu_stream->formats;
+ hw->rates = snd_pcm_rate_mask_intersect(rates, cpu_stream->rates);
snd_pcm_limit_hw_rates(runtime);
hw->rate_min = max(hw->rate_min, cpu_stream->rate_min);
- hw->rate_min = max(hw->rate_min, codec_stream->rate_min);
+ hw->rate_min = max(hw->rate_min, rate_min);
hw->rate_max = min_not_zero(hw->rate_max, cpu_stream->rate_max);
- hw->rate_max = min_not_zero(hw->rate_max, codec_stream->rate_max);
+ hw->rate_max = min_not_zero(hw->rate_max, rate_max);
}
/*
@@ -349,15 +437,16 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_dai_driver *cpu_dai_drv = cpu_dai->driver;
- struct snd_soc_dai_driver *codec_dai_drv = codec_dai->driver;
- int ret = 0;
+ struct snd_soc_dai *codec_dai;
+ const char *codec_dai_name = "multicodec";
+ int i, ret = 0;
pinctrl_pm_select_default_state(cpu_dai->dev);
- pinctrl_pm_select_default_state(codec_dai->dev);
+ for (i = 0; i < rtd->num_codecs; i++)
+ pinctrl_pm_select_default_state(rtd->codec_dais[i]->dev);
pm_runtime_get_sync(cpu_dai->dev);
- pm_runtime_get_sync(codec_dai->dev);
+ for (i = 0; i < rtd->num_codecs; i++)
+ pm_runtime_get_sync(rtd->codec_dais[i]->dev);
pm_runtime_get_sync(platform->dev);
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -376,18 +465,28 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
ret = platform->driver->ops->open(substream);
if (ret < 0) {
dev_err(platform->dev, "ASoC: can't open platform"
- " %s: %d\n", platform->name, ret);
+ " %s: %d\n", platform->component.name, ret);
goto platform_err;
}
}
- if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
- ret = codec_dai->driver->ops->startup(substream, codec_dai);
- if (ret < 0) {
- dev_err(codec_dai->dev, "ASoC: can't open codec"
- " %s: %d\n", codec_dai->name, ret);
- goto codec_dai_err;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops && codec_dai->driver->ops->startup) {
+ ret = codec_dai->driver->ops->startup(substream,
+ codec_dai);
+ if (ret < 0) {
+ dev_err(codec_dai->dev,
+ "ASoC: can't open codec %s: %d\n",
+ codec_dai->name, ret);
+ goto codec_dai_err;
+ }
}
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ codec_dai->tx_mask = 0;
+ else
+ codec_dai->rx_mask = 0;
}
if (rtd->dai_link->ops && rtd->dai_link->ops->startup) {
@@ -404,13 +503,10 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
goto dynamic;
/* Check that the codec and cpu DAIs are compatible */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->playback,
- &cpu_dai_drv->playback);
- } else {
- soc_pcm_init_runtime_hw(runtime, &codec_dai_drv->capture,
- &cpu_dai_drv->capture);
- }
+ soc_pcm_init_runtime_hw(substream);
+
+ if (rtd->num_codecs == 1)
+ codec_dai_name = rtd->codec_dai->name;
if (soc_pcm_has_symmetry(substream))
runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
@@ -418,23 +514,22 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
ret = -EINVAL;
if (!runtime->hw.rates) {
printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n",
- codec_dai->name, cpu_dai->name);
+ codec_dai_name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.formats) {
printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n",
- codec_dai->name, cpu_dai->name);
+ codec_dai_name, cpu_dai->name);
goto config_err;
}
if (!runtime->hw.channels_min || !runtime->hw.channels_max ||
runtime->hw.channels_min > runtime->hw.channels_max) {
printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n",
- codec_dai->name, cpu_dai->name);
+ codec_dai_name, cpu_dai->name);
goto config_err;
}
- soc_pcm_apply_msb(substream, codec_dai);
- soc_pcm_apply_msb(substream, cpu_dai);
+ soc_pcm_apply_msb(substream);
/* Symmetry only applies if we've already got an active stream. */
if (cpu_dai->active) {
@@ -443,14 +538,17 @@ static int soc_pcm_open(struct snd_pcm_substream *substream)
goto config_err;
}
- if (codec_dai->active) {
- ret = soc_pcm_apply_symmetry(substream, codec_dai);
- if (ret != 0)
- goto config_err;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if (rtd->codec_dais[i]->active) {
+ ret = soc_pcm_apply_symmetry(substream,
+ rtd->codec_dais[i]);
+ if (ret != 0)
+ goto config_err;
+ }
}
pr_debug("ASoC: %s <-> %s info:\n",
- codec_dai->name, cpu_dai->name);
+ codec_dai_name, cpu_dai->name);
pr_debug("ASoC: rate mask 0x%x\n", runtime->hw.rates);
pr_debug("ASoC: min ch %d max ch %d\n", runtime->hw.channels_min,
runtime->hw.channels_max);
@@ -469,10 +567,15 @@ config_err:
rtd->dai_link->ops->shutdown(substream);
machine_err:
- if (codec_dai->driver->ops->shutdown)
- codec_dai->driver->ops->shutdown(substream, codec_dai);
+ i = rtd->num_codecs;
codec_dai_err:
+ while (--i >= 0) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops->shutdown)
+ codec_dai->driver->ops->shutdown(substream, codec_dai);
+ }
+
if (platform->driver->ops && platform->driver->ops->close)
platform->driver->ops->close(substream);
@@ -483,10 +586,13 @@ out:
mutex_unlock(&rtd->pcm_mutex);
pm_runtime_put(platform->dev);
- pm_runtime_put(codec_dai->dev);
+ for (i = 0; i < rtd->num_codecs; i++)
+ pm_runtime_put(rtd->codec_dais[i]->dev);
pm_runtime_put(cpu_dai->dev);
- if (!codec_dai->active)
- pinctrl_pm_select_sleep_state(codec_dai->dev);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if (!rtd->codec_dais[i]->active)
+ pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
+ }
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
@@ -502,7 +608,7 @@ static void close_delayed_work(struct work_struct *work)
{
struct snd_soc_pcm_runtime *rtd =
container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[0];
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -531,7 +637,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai;
+ int i;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -541,14 +648,20 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
if (!cpu_dai->active)
cpu_dai->rate = 0;
- if (!codec_dai->active)
- codec_dai->rate = 0;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (!codec_dai->active)
+ codec_dai->rate = 0;
+ }
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
- if (codec_dai->driver->ops->shutdown)
- codec_dai->driver->ops->shutdown(substream, codec_dai);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops->shutdown)
+ codec_dai->driver->ops->shutdown(substream, codec_dai);
+ }
if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown)
rtd->dai_link->ops->shutdown(substream);
@@ -578,10 +691,13 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
mutex_unlock(&rtd->pcm_mutex);
pm_runtime_put(platform->dev);
- pm_runtime_put(codec_dai->dev);
+ for (i = 0; i < rtd->num_codecs; i++)
+ pm_runtime_put(rtd->codec_dais[i]->dev);
pm_runtime_put(cpu_dai->dev);
- if (!codec_dai->active)
- pinctrl_pm_select_sleep_state(codec_dai->dev);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if (!rtd->codec_dais[i]->active)
+ pinctrl_pm_select_sleep_state(rtd->codec_dais[i]->dev);
+ }
if (!cpu_dai->active)
pinctrl_pm_select_sleep_state(cpu_dai->dev);
@@ -598,8 +714,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret = 0;
+ struct snd_soc_dai *codec_dai;
+ int i, ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -621,12 +737,16 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
}
}
- if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) {
- ret = codec_dai->driver->ops->prepare(substream, codec_dai);
- if (ret < 0) {
- dev_err(codec_dai->dev, "ASoC: DAI prepare error: %d\n",
- ret);
- goto out;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops && codec_dai->driver->ops->prepare) {
+ ret = codec_dai->driver->ops->prepare(substream,
+ codec_dai);
+ if (ret < 0) {
+ dev_err(codec_dai->dev,
+ "ASoC: DAI prepare error: %d\n", ret);
+ goto out;
+ }
}
}
@@ -649,13 +769,44 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dapm_stream_event(rtd, substream->stream,
SND_SOC_DAPM_STREAM_START);
- snd_soc_dai_digital_mute(codec_dai, 0, substream->stream);
+ for (i = 0; i < rtd->num_codecs; i++)
+ snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
+ substream->stream);
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
}
+static void soc_pcm_codec_params_fixup(struct snd_pcm_hw_params *params,
+ unsigned int mask)
+{
+ struct snd_interval *interval;
+ int channels = hweight_long(mask);
+
+ interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ interval->min = channels;
+ interval->max = channels;
+}
+
+int soc_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ if (dai->driver->ops && dai->driver->ops->hw_params) {
+ ret = dai->driver->ops->hw_params(substream, params, dai);
+ if (ret < 0) {
+ dev_err(dai->dev, "ASoC: can't set %s hw params: %d\n",
+ dai->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
/*
* Called by ALSA when the hardware params are set by application. This
* function can also be called multiple times and can allocate buffers
@@ -667,8 +818,7 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret = 0;
+ int i, ret = 0;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -685,29 +835,40 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
}
}
- if (codec_dai->driver->ops && codec_dai->driver->ops->hw_params) {
- ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
- if (ret < 0) {
- dev_err(codec_dai->dev, "ASoC: can't set %s hw params:"
- " %d\n", codec_dai->name, ret);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+ struct snd_pcm_hw_params codec_params;
+
+ /* copy params for each codec */
+ codec_params = *params;
+
+ /* fixup params based on TDM slot masks */
+ if (codec_dai->tx_mask)
+ soc_pcm_codec_params_fixup(&codec_params,
+ codec_dai->tx_mask);
+ if (codec_dai->rx_mask)
+ soc_pcm_codec_params_fixup(&codec_params,
+ codec_dai->rx_mask);
+
+ ret = soc_dai_hw_params(substream, &codec_params, codec_dai);
+ if(ret < 0)
goto codec_err;
- }
- }
- if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_params) {
- ret = cpu_dai->driver->ops->hw_params(substream, params, cpu_dai);
- if (ret < 0) {
- dev_err(cpu_dai->dev, "ASoC: %s hw params failed: %d\n",
- cpu_dai->name, ret);
- goto interface_err;
- }
+ codec_dai->rate = params_rate(&codec_params);
+ codec_dai->channels = params_channels(&codec_params);
+ codec_dai->sample_bits = snd_pcm_format_physical_width(
+ params_format(&codec_params));
}
+ ret = soc_dai_hw_params(substream, params, cpu_dai);
+ if (ret < 0)
+ goto interface_err;
+
if (platform->driver->ops && platform->driver->ops->hw_params) {
ret = platform->driver->ops->hw_params(substream, params);
if (ret < 0) {
dev_err(platform->dev, "ASoC: %s hw params failed: %d\n",
- platform->name, ret);
+ platform->component.name, ret);
goto platform_err;
}
}
@@ -718,11 +879,6 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
cpu_dai->sample_bits =
snd_pcm_format_physical_width(params_format(params));
- codec_dai->rate = params_rate(params);
- codec_dai->channels = params_channels(params);
- codec_dai->sample_bits =
- snd_pcm_format_physical_width(params_format(params));
-
out:
mutex_unlock(&rtd->pcm_mutex);
return ret;
@@ -732,10 +888,16 @@ platform_err:
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
interface_err:
- if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
- codec_dai->driver->ops->hw_free(substream, codec_dai);
+ i = rtd->num_codecs;
codec_err:
+ while (--i >= 0) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
+ codec_dai->driver->ops->hw_free(substream, codec_dai);
+ codec_dai->rate = 0;
+ }
+
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
rtd->dai_link->ops->hw_free(substream);
@@ -751,8 +913,9 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai;
bool playback = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ int i;
mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
@@ -763,16 +926,22 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
cpu_dai->sample_bits = 0;
}
- if (codec_dai->active == 1) {
- codec_dai->rate = 0;
- codec_dai->channels = 0;
- codec_dai->sample_bits = 0;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->active == 1) {
+ codec_dai->rate = 0;
+ codec_dai->channels = 0;
+ codec_dai->sample_bits = 0;
+ }
}
/* apply codec digital mute */
- if ((playback && codec_dai->playback_active == 1) ||
- (!playback && codec_dai->capture_active == 1))
- snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ if ((playback && rtd->codec_dais[i]->playback_active == 1) ||
+ (!playback && rtd->codec_dais[i]->capture_active == 1))
+ snd_soc_dai_digital_mute(rtd->codec_dais[i], 1,
+ substream->stream);
+ }
/* free any machine hw params */
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
@@ -783,8 +952,11 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
platform->driver->ops->hw_free(substream);
/* now free hw params for the DAIs */
- if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
- codec_dai->driver->ops->hw_free(substream, codec_dai);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops && codec_dai->driver->ops->hw_free)
+ codec_dai->driver->ops->hw_free(substream, codec_dai);
+ }
if (cpu_dai->driver->ops && cpu_dai->driver->ops->hw_free)
cpu_dai->driver->ops->hw_free(substream, cpu_dai);
@@ -798,13 +970,17 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
- ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
- if (ret < 0)
- return ret;
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops && codec_dai->driver->ops->trigger) {
+ ret = codec_dai->driver->ops->trigger(substream,
+ cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
}
if (platform->driver->ops && platform->driver->ops->trigger) {
@@ -834,14 +1010,18 @@ static int soc_pcm_bespoke_trigger(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
- int ret;
-
- if (codec_dai->driver->ops &&
- codec_dai->driver->ops->bespoke_trigger) {
- ret = codec_dai->driver->ops->bespoke_trigger(substream, cmd, codec_dai);
- if (ret < 0)
- return ret;
+ struct snd_soc_dai *codec_dai;
+ int i, ret;
+
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops &&
+ codec_dai->driver->ops->bespoke_trigger) {
+ ret = codec_dai->driver->ops->bespoke_trigger(substream,
+ cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
}
if (platform->driver->bespoke_trigger) {
@@ -867,10 +1047,12 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t offset = 0;
snd_pcm_sframes_t delay = 0;
+ snd_pcm_sframes_t codec_delay = 0;
+ int i;
if (platform->driver->ops && platform->driver->ops->pointer)
offset = platform->driver->ops->pointer(substream);
@@ -878,11 +1060,21 @@ static snd_pcm_uframes_t soc_pcm_pointer(struct snd_pcm_substream *substream)
if (cpu_dai->driver->ops && cpu_dai->driver->ops->delay)
delay += cpu_dai->driver->ops->delay(substream, cpu_dai);
- if (codec_dai->driver->ops && codec_dai->driver->ops->delay)
- delay += codec_dai->driver->ops->delay(substream, codec_dai);
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->ops && codec_dai->driver->ops->delay)
+ codec_delay = max(codec_delay,
+ codec_dai->driver->ops->delay(substream,
+ codec_dai));
+ }
+ delay += codec_delay;
+ /*
+ * None of the existing platform drivers implement delay(), so
+ * for now the codec_dai of first multicodec entry is used
+ */
if (platform->driver->delay)
- delay += platform->driver->delay(substream, codec_dai);
+ delay += platform->driver->delay(substream, rtd->codec_dais[0]);
runtime->delay = delay;
@@ -985,7 +1177,7 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
struct snd_soc_dapm_widget *widget, int stream)
{
struct snd_soc_pcm_runtime *be;
- int i;
+ int i, j;
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
for (i = 0; i < card->num_links; i++) {
@@ -994,9 +1186,14 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (!be->dai_link->no_pcm)
continue;
- if (be->cpu_dai->playback_widget == widget ||
- be->codec_dai->playback_widget == widget)
+ if (be->cpu_dai->playback_widget == widget)
return be;
+
+ for (j = 0; j < be->num_codecs; j++) {
+ struct snd_soc_dai *dai = be->codec_dais[j];
+ if (dai->playback_widget == widget)
+ return be;
+ }
}
} else {
@@ -1006,9 +1203,14 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
if (!be->dai_link->no_pcm)
continue;
- if (be->cpu_dai->capture_widget == widget ||
- be->codec_dai->capture_widget == widget)
+ if (be->cpu_dai->capture_widget == widget)
return be;
+
+ for (j = 0; j < be->num_codecs; j++) {
+ struct snd_soc_dai *dai = be->codec_dais[j];
+ if (dai->capture_widget == widget)
+ return be;
+ }
}
}
@@ -1071,6 +1273,7 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
/* Destroy any old FE <--> BE connections */
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+ unsigned int i;
/* is there a valid CPU DAI widget for this BE */
widget = dai_get_widget(dpcm->be->cpu_dai, stream);
@@ -1080,11 +1283,14 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
continue;
/* is there a valid CODEC DAI widget for this BE */
- widget = dai_get_widget(dpcm->be->codec_dai, stream);
+ for (i = 0; i < dpcm->be->num_codecs; i++) {
+ struct snd_soc_dai *dai = dpcm->be->codec_dais[i];
+ widget = dai_get_widget(dai, stream);
- /* prune the BE if it's no longer in our active list */
- if (widget && widget_in_list(list, widget))
- continue;
+ /* prune the BE if it's no longer in our active list */
+ if (widget && widget_in_list(list, widget))
+ continue;
+ }
dev_dbg(fe->dev, "ASoC: pruning %s BE %s for %s\n",
stream ? "capture" : "playback",
@@ -2069,6 +2275,7 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
dpcm_be_disconnect(fe, SNDRV_PCM_STREAM_PLAYBACK);
}
+ dpcm_path_put(&list);
capture:
/* skip if FE doesn't have capture capability */
if (!fe->cpu_dai->driver->capture.channels_min)
@@ -2113,16 +2320,22 @@ int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute)
list_for_each_entry(dpcm, clients, list_be) {
struct snd_soc_pcm_runtime *be = dpcm->be;
- struct snd_soc_dai *dai = be->codec_dai;
- struct snd_soc_dai_driver *drv = dai->driver;
+ int i;
if (be->dai_link->ignore_suspend)
continue;
- dev_dbg(be->dev, "ASoC: BE digital mute %s\n", be->dai_link->name);
+ for (i = 0; i < be->num_codecs; i++) {
+ struct snd_soc_dai *dai = be->codec_dais[i];
+ struct snd_soc_dai_driver *drv = dai->driver;
+
+ dev_dbg(be->dev, "ASoC: BE digital mute %s\n",
+ be->dai_link->name);
- if (drv->ops && drv->ops->digital_mute && dai->playback_active)
- drv->ops->digital_mute(dai, mute);
+ if (drv->ops && drv->ops->digital_mute &&
+ dai->playback_active)
+ drv->ops->digital_mute(dai, mute);
+ }
}
return 0;
@@ -2187,22 +2400,27 @@ static int dpcm_fe_dai_close(struct snd_pcm_substream *fe_substream)
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_platform *platform = rtd->platform;
- struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm *pcm;
char new_name[64];
int ret = 0, playback = 0, capture = 0;
+ int i;
if (rtd->dai_link->dynamic || rtd->dai_link->no_pcm) {
playback = rtd->dai_link->dpcm_playback;
capture = rtd->dai_link->dpcm_capture;
} else {
- if (codec_dai->driver->playback.channels_min &&
- cpu_dai->driver->playback.channels_min)
- playback = 1;
- if (codec_dai->driver->capture.channels_min &&
- cpu_dai->driver->capture.channels_min)
- capture = 1;
+ for (i = 0; i < rtd->num_codecs; i++) {
+ codec_dai = rtd->codec_dais[i];
+ if (codec_dai->driver->playback.channels_min)
+ playback = 1;
+ if (codec_dai->driver->capture.channels_min)
+ capture = 1;
+ }
+
+ capture = capture && cpu_dai->driver->capture.channels_min;
+ playback = playback && cpu_dai->driver->playback.channels_min;
}
if (rtd->dai_link->playback_only) {
@@ -2228,7 +2446,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
rtd->dai_link->stream_name);
else
snprintf(new_name, sizeof(new_name), "%s %s-%d",
- rtd->dai_link->stream_name, codec_dai->name, num);
+ rtd->dai_link->stream_name,
+ (rtd->num_codecs > 1) ?
+ "multicodec" : rtd->codec_dai->name, num);
ret = snd_pcm_new(rtd->card->snd_card, new_name, num, playback,
capture, &pcm);
@@ -2301,8 +2521,9 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
pcm->private_free = platform->driver->pcm_free;
out:
- dev_info(rtd->card->dev, "%s <-> %s mapping ok\n", codec_dai->name,
- cpu_dai->name);
+ dev_info(rtd->card->dev, "%s <-> %s mapping ok\n",
+ (rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name,
+ cpu_dai->name);
return ret;
}
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index 02734bd..a83aff0 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -41,8 +41,7 @@ static int tegra_alc5632_asoc_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_alc5632 *alc5632 = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
@@ -105,7 +104,7 @@ static int tegra_alc5632_asoc_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
- struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(codec->card);
+ struct tegra_alc5632 *machine = snd_soc_card_get_drvdata(rtd->card);
snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
&tegra_alc5632_hs_jack);
diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c
index ce73e1f..b86cd99 100644
--- a/sound/soc/tegra/tegra_max98090.c
+++ b/sound/soc/tegra/tegra_max98090.c
@@ -49,8 +49,7 @@ static int tegra_max98090_asoc_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_max98090 *machine = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
@@ -127,7 +126,7 @@ static int tegra_max98090_asoc_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
- struct tegra_max98090 *machine = snd_soc_card_get_drvdata(codec->card);
+ struct tegra_max98090 *machine = snd_soc_card_get_drvdata(rtd->card);
if (gpio_is_valid(machine->gpio_hp_det)) {
snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index 4feb16a..a689883 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -51,8 +51,7 @@ static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
@@ -110,7 +109,7 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
- struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(codec->card);
+ struct tegra_rt5640 *machine = snd_soc_card_get_drvdata(rtd->card);
snd_soc_jack_new(codec, "Headphones", SND_JACK_HEADPHONE,
&tegra_rt5640_hp_jack);
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
index 8e774d1..769e28f 100644
--- a/sound/soc/tegra/tegra_wm8753.c
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -55,8 +55,7 @@ static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 0939661..86e05e9 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -60,8 +60,7 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
@@ -173,7 +172,7 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = codec_dai->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
if (gpio_is_valid(machine->gpio_hp_det)) {
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 734bfcd..589d2d9 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -50,8 +50,7 @@ static int trimslice_asoc_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
- struct snd_soc_codec *codec = codec_dai->codec;
- struct snd_soc_card *card = codec->card;
+ struct snd_soc_card *card = rtd->card;
struct tegra_trimslice *trimslice = snd_soc_card_get_drvdata(card);
int srate, mclk;
int err;
OpenPOWER on IntegriCloud