summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/core/pcm.c2
-rw-r--r--sound/core/pcm_compat.c2
-rw-r--r--sound/core/pcm_misc.c8
-rw-r--r--sound/core/pcm_native.c14
-rw-r--r--sound/firewire/bebob/bebob_focusrite.c62
-rw-r--r--sound/firewire/bebob/bebob_stream.c18
-rw-r--r--sound/firewire/bebob/bebob_terratec.c7
-rw-r--r--sound/pci/ad1889.c8
-rw-r--r--sound/pci/hda/hda_intel.c32
-rw-r--r--sound/pci/hda/hda_local.h4
-rw-r--r--sound/pci/hda/hda_priv.h1
-rw-r--r--sound/pci/hda/patch_conexant.c31
-rw-r--r--sound/pci/hda/patch_hdmi.c15
-rw-r--r--sound/pci/hda/patch_realtek.c241
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile7
-rw-r--r--sound/soc/atmel/Kconfig9
-rw-r--r--sound/soc/atmel/Makefile1
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c5
-rw-r--r--sound/soc/atmel/snd-soc-afeb9260.c151
-rw-r--r--sound/soc/au1x/ac97c.c2
-rw-r--r--sound/soc/au1x/psc-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c2
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c2
-rw-r--r--sound/soc/cirrus/Kconfig3
-rw-r--r--sound/soc/cirrus/ep93xx-ac97.c2
-rw-r--r--sound/soc/codecs/Kconfig22
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/ab8500-codec.c32
-rw-r--r--sound/soc/codecs/ac97.c18
-rw-r--r--sound/soc/codecs/ad193x.c14
-rw-r--r--sound/soc/codecs/ad1980.c212
-rw-r--r--sound/soc/codecs/ad1980.h26
-rw-r--r--sound/soc/codecs/adau1373.c6
-rw-r--r--sound/soc/codecs/adau1761.c7
-rw-r--r--sound/soc/codecs/adau1781.c2
-rw-r--r--sound/soc/codecs/adau17x1.c3
-rw-r--r--sound/soc/codecs/adav80x.c4
-rw-r--r--sound/soc/codecs/ak4535.c31
-rw-r--r--sound/soc/codecs/ak4641.c33
-rw-r--r--sound/soc/codecs/ak4642.c16
-rw-r--r--sound/soc/codecs/ak4671.c13
-rw-r--r--sound/soc/codecs/alc5623.c22
-rw-r--r--sound/soc/codecs/alc5632.c22
-rw-r--r--sound/soc/codecs/arizona.c34
-rw-r--r--sound/soc/codecs/cq93vc.c33
-rw-r--r--sound/soc/codecs/cs4265.c2
-rw-r--r--sound/soc/codecs/cs4271-i2c.c62
-rw-r--r--sound/soc/codecs/cs4271-spi.c55
-rw-r--r--sound/soc/codecs/cs4271.c155
-rw-r--r--sound/soc/codecs/cs4271.h11
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c1
-rw-r--r--sound/soc/codecs/cs42l51.c10
-rw-r--r--sound/soc/codecs/cs42l51.h1
-rw-r--r--sound/soc/codecs/cs42l73.c6
-rw-r--r--sound/soc/codecs/es8328-i2c.c2
-rw-r--r--sound/soc/codecs/hdmi.c2
-rw-r--r--sound/soc/codecs/lm49453.c8
-rw-r--r--sound/soc/codecs/max98088.c31
-rw-r--r--sound/soc/codecs/max98090.c207
-rw-r--r--sound/soc/codecs/max98090.h8
-rw-r--r--sound/soc/codecs/max98095.c23
-rw-r--r--sound/soc/codecs/max9850.c22
-rw-r--r--sound/soc/codecs/rt5645.c2
-rw-r--r--sound/soc/codecs/rt5670.c36
-rw-r--r--sound/soc/codecs/sgtl5000.c3
-rw-r--r--sound/soc/codecs/sgtl5000.h2
-rw-r--r--sound/soc/codecs/sigmadsp.c7
-rw-r--r--sound/soc/codecs/stac9766.c40
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c13
-rw-r--r--sound/soc/codecs/wm5102.c18
-rw-r--r--sound/soc/codecs/wm8731.c9
-rw-r--r--sound/soc/codecs/wm8903.c8
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c12
-rw-r--r--sound/soc/codecs/wm8962.c8
-rw-r--r--sound/soc/codecs/wm8994.c2
-rw-r--r--sound/soc/codecs/wm8994.h2
-rw-r--r--sound/soc/codecs/wm9705.c46
-rw-r--r--sound/soc/codecs/wm9712.c209
-rw-r--r--sound/soc/codecs/wm9713.c228
-rw-r--r--sound/soc/codecs/wm_adsp.c98
-rw-r--r--sound/soc/davinci/davinci-mcasp.c339
-rw-r--r--sound/soc/davinci/davinci-mcasp.h17
-rw-r--r--sound/soc/dwc/designware_i2s.c46
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c5
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c19
-rw-r--r--sound/soc/fsl/fsl_asrc.c28
-rw-r--r--sound/soc/fsl/fsl_esai.c14
-rw-r--r--sound/soc/fsl/fsl_ssi.c17
-rw-r--r--sound/soc/fsl/imx-sgtl5000.c6
-rw-r--r--sound/soc/fsl/imx-spdif.c3
-rw-r--r--sound/soc/fsl/imx-ssi.c2
-rw-r--r--sound/soc/fsl/imx-wm8962.c6
-rw-r--r--sound/soc/fsl/mpc5200_dma.c3
-rw-r--r--sound/soc/fsl/mpc5200_psc_ac97.c6
-rw-r--r--sound/soc/intel/Kconfig44
-rw-r--r--sound/soc/intel/Makefile7
-rw-r--r--sound/soc/intel/broadwell.c64
-rw-r--r--sound/soc/intel/bytcr_dpcm_rt5640.c230
-rw-r--r--sound/soc/intel/cht_bsw_rt5672.c285
-rw-r--r--sound/soc/intel/haswell.c14
-rw-r--r--sound/soc/intel/sst-atom-controls.c1208
-rw-r--r--sound/soc/intel/sst-atom-controls.h428
-rw-r--r--sound/soc/intel/sst-baytrail-dsp.c24
-rw-r--r--sound/soc/intel/sst-dsp-priv.h136
-rw-r--r--sound/soc/intel/sst-dsp.c31
-rw-r--r--sound/soc/intel/sst-dsp.h28
-rw-r--r--sound/soc/intel/sst-firmware.c937
-rw-r--r--sound/soc/intel/sst-haswell-dsp.c295
-rw-r--r--sound/soc/intel/sst-haswell-ipc.c413
-rw-r--r--sound/soc/intel/sst-haswell-ipc.h25
-rw-r--r--sound/soc/intel/sst-haswell-pcm.c423
-rw-r--r--sound/soc/intel/sst-mfld-platform-compress.c8
-rw-r--r--sound/soc/intel/sst-mfld-platform-pcm.c154
-rw-r--r--sound/soc/intel/sst-mfld-platform.h5
-rw-r--r--sound/soc/intel/sst/Makefile7
-rw-r--r--sound/soc/intel/sst/sst.c437
-rw-r--r--sound/soc/intel/sst/sst.h546
-rw-r--r--sound/soc/intel/sst/sst_acpi.c383
-rw-r--r--sound/soc/intel/sst/sst_drv_interface.c686
-rw-r--r--sound/soc/intel/sst/sst_ipc.c373
-rw-r--r--sound/soc/intel/sst/sst_loader.c456
-rw-r--r--sound/soc/intel/sst/sst_pci.c209
-rw-r--r--sound/soc/intel/sst/sst_pvt.c449
-rw-r--r--sound/soc/intel/sst/sst_stream.c437
-rw-r--r--sound/soc/jz4740/qi_lb60.c11
-rw-r--r--sound/soc/nuc900/nuc900-ac97.c2
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c6
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c4
-rw-r--r--sound/soc/s6000/Kconfig26
-rw-r--r--sound/soc/s6000/Makefile11
-rw-r--r--sound/soc/s6000/s6000-i2s.c617
-rw-r--r--sound/soc/s6000/s6000-i2s.h23
-rw-r--r--sound/soc/s6000/s6000-pcm.c521
-rw-r--r--sound/soc/s6000/s6000-pcm.h33
-rw-r--r--sound/soc/s6000/s6105-ipcam.c221
-rw-r--r--sound/soc/samsung/ac97.c4
-rw-r--r--sound/soc/samsung/snow.c1
-rw-r--r--sound/soc/sh/fsi.c12
-rw-r--r--sound/soc/sh/hac.c2
-rw-r--r--sound/soc/sh/rcar/core.c3
-rw-r--r--sound/soc/soc-ac97.c256
-rw-r--r--sound/soc/soc-cache.c149
-rw-r--r--sound/soc/soc-compress.c11
-rw-r--r--sound/soc/soc-core.c1534
-rw-r--r--sound/soc/soc-dapm.c755
-rw-r--r--sound/soc/soc-jack.c11
-rw-r--r--sound/soc/soc-ops.c952
-rw-r--r--sound/soc/soc-pcm.c95
-rw-r--r--sound/soc/tegra/tegra20_ac97.c2
-rw-r--r--sound/soc/txx9/txx9aclc-ac97.c2
-rw-r--r--sound/soc/txx9/txx9aclc.c2
-rw-r--r--sound/usb/card.c9
-rw-r--r--sound/usb/mixer.c7
-rw-r--r--sound/usb/mixer_quirks.c10
-rw-r--r--sound/usb/quirks-table.h30
-rw-r--r--sound/usb/quirks.c18
157 files changed, 11817 insertions, 5311 deletions
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 42ded997b223..c6ff94ab1ad6 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -216,6 +216,8 @@ static char *snd_pcm_format_names[] = {
FORMAT(DSD_U8),
FORMAT(DSD_U16_LE),
FORMAT(DSD_U32_LE),
+ FORMAT(DSD_U16_BE),
+ FORMAT(DSD_U32_BE),
};
const char *snd_pcm_format_name(snd_pcm_format_t format)
diff --git a/sound/core/pcm_compat.c b/sound/core/pcm_compat.c
index 102e8fd1d450..2d957ba63557 100644
--- a/sound/core/pcm_compat.c
+++ b/sound/core/pcm_compat.c
@@ -210,6 +210,8 @@ static int snd_pcm_status_user_compat(struct snd_pcm_substream *substream,
if (err < 0)
return err;
+ if (clear_user(src, sizeof(*src)))
+ return -EFAULT;
if (put_user(status.state, &src->state) ||
compat_put_timespec(&status.trigger_tstamp, &src->trigger_tstamp) ||
compat_put_timespec(&status.tstamp, &src->tstamp) ||
diff --git a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
index ae7a0feb3b76..ebe8444de6c6 100644
--- a/sound/core/pcm_misc.c
+++ b/sound/core/pcm_misc.c
@@ -152,6 +152,14 @@ static struct pcm_format_data pcm_formats[(INT)SNDRV_PCM_FORMAT_LAST+1] = {
.width = 32, .phys = 32, .le = 1, .signd = 0,
.silence = { 0x69, 0x69, 0x69, 0x69 },
},
+ [SNDRV_PCM_FORMAT_DSD_U16_BE] = {
+ .width = 16, .phys = 16, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69 },
+ },
+ [SNDRV_PCM_FORMAT_DSD_U32_BE] = {
+ .width = 32, .phys = 32, .le = 0, .signd = 0,
+ .silence = { 0x69, 0x69, 0x69, 0x69 },
+ },
/* FIXME: the following three formats are not defined properly yet */
[SNDRV_PCM_FORMAT_MPEG] = {
.le = -1, .signd = -1,
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index bfe1cf6b492f..166d59cdc86b 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -781,16 +781,15 @@ static int snd_pcm_action_group(struct action_ops *ops,
{
struct snd_pcm_substream *s = NULL;
struct snd_pcm_substream *s1;
- int res = 0;
+ int res = 0, depth = 1;
snd_pcm_group_for_each_entry(s, substream) {
if (do_lock && s != substream) {
if (s->pcm->nonatomic)
- mutex_lock_nested(&s->self_group.mutex,
- SINGLE_DEPTH_NESTING);
+ mutex_lock_nested(&s->self_group.mutex, depth);
else
- spin_lock_nested(&s->self_group.lock,
- SINGLE_DEPTH_NESTING);
+ spin_lock_nested(&s->self_group.lock, depth);
+ depth++;
}
res = ops->pre_action(s, state);
if (res < 0)
@@ -906,8 +905,7 @@ static int snd_pcm_action_lock_mutex(struct action_ops *ops,
down_read(&snd_pcm_link_rwsem);
if (snd_pcm_stream_linked(substream)) {
mutex_lock(&substream->group->mutex);
- mutex_lock_nested(&substream->self_group.mutex,
- SINGLE_DEPTH_NESTING);
+ mutex_lock(&substream->self_group.mutex);
res = snd_pcm_action_group(ops, substream, state, 1);
mutex_unlock(&substream->self_group.mutex);
mutex_unlock(&substream->group->mutex);
@@ -3311,7 +3309,7 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
#ifndef ARCH_HAS_DMA_MMAP_COHERENT
/* This should be defined / handled globally! */
-#ifdef CONFIG_ARM
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
#define ARCH_HAS_DMA_MMAP_COHERENT
#endif
#endif
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
index 45a0eed6d5b1..3b052ed0fbf5 100644
--- a/sound/firewire/bebob/bebob_focusrite.c
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -27,12 +27,14 @@
#define SAFFIRE_CLOCK_SOURCE_INTERNAL 0
#define SAFFIRE_CLOCK_SOURCE_SPDIF 1
-/* '1' is absent, why... */
+/* clock sources as returned from register of Saffire Pro 10 and 26 */
#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL 0
+#define SAFFIREPRO_CLOCK_SOURCE_SKIP 1 /* never used on hardware */
#define SAFFIREPRO_CLOCK_SOURCE_SPDIF 2
-#define SAFFIREPRO_CLOCK_SOURCE_ADAT1 3
-#define SAFFIREPRO_CLOCK_SOURCE_ADAT2 4
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT1 3 /* not used on s.pro. 10 */
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT2 4 /* not used on s.pro. 10 */
#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK 5
+#define SAFFIREPRO_CLOCK_SOURCE_COUNT 6
/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
#define SAFFIREPRO_ENABLE_DIG_IFACES 0x01a4
@@ -101,13 +103,34 @@ saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
&data, sizeof(__be32), 0);
}
+static char *const saffirepro_10_clk_src_labels[] = {
+ SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+};
static char *const saffirepro_26_clk_src_labels[] = {
SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
};
-
-static char *const saffirepro_10_clk_src_labels[] = {
- SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+/* Value maps between registers and labels for SaffirePro 10/26. */
+static const signed char saffirepro_clk_maps[][SAFFIREPRO_CLOCK_SOURCE_COUNT] = {
+ /* SaffirePro 10 */
+ [0] = {
+ [SAFFIREPRO_CLOCK_SOURCE_INTERNAL] = 0,
+ [SAFFIREPRO_CLOCK_SOURCE_SKIP] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_SPDIF] = 1,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT1] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT2] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK] = 2,
+ },
+ /* SaffirePro 26 */
+ [1] = {
+ [SAFFIREPRO_CLOCK_SOURCE_INTERNAL] = 0,
+ [SAFFIREPRO_CLOCK_SOURCE_SKIP] = -1, /* not supported */
+ [SAFFIREPRO_CLOCK_SOURCE_SPDIF] = 1,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT1] = 2,
+ [SAFFIREPRO_CLOCK_SOURCE_ADAT2] = 3,
+ [SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK] = 4,
+ }
};
+
static int
saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
{
@@ -138,24 +161,35 @@ saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
}
+
+/*
+ * query hardware for current clock source, return our internally
+ * used clock index in *id, depending on hardware.
+ */
static int
saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
{
int err;
- u32 value;
+ u32 value; /* clock source read from hw register */
+ const signed char *map;
err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
if (err < 0)
goto end;
- if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels) {
- if (value == SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK)
- *id = 2;
- else if (value == SAFFIREPRO_CLOCK_SOURCE_SPDIF)
- *id = 1;
- } else if (value > 1) {
- *id = value - 1;
+ /* depending on hardware, use a different mapping */
+ if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels)
+ map = saffirepro_clk_maps[0];
+ else
+ map = saffirepro_clk_maps[1];
+
+ /* In a case that this driver cannot handle the value of register. */
+ if (value >= SAFFIREPRO_CLOCK_SOURCE_COUNT || map[value] < 0) {
+ err = -EIO;
+ goto end;
}
+
+ *id = (unsigned int)map[value];
end:
return err;
}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index ef4d0c9f6578..1aab0a32870c 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -129,12 +129,24 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
/* 1.The device has its own operation to switch source of clock */
if (clk_spec) {
err = clk_spec->get(bebob, &id);
- if (err < 0)
+ if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get clock source: %d\n", err);
- else if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
- strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
+ goto end;
+ }
+
+ if (id >= clk_spec->num) {
+ dev_err(&bebob->unit->device,
+ "clock source %d out of range 0..%d\n",
+ id, clk_spec->num - 1);
+ err = -EIO;
+ goto end;
+ }
+
+ if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
+ strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
*internal = true;
+
goto end;
}
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index 0e4c0bfc463b..9940611f2e1b 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -24,7 +24,12 @@ phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
if (err < 0)
goto end;
- *id = (enable_ext & 0x01) | ((enable_word & 0x01) << 1);
+ if (enable_ext == 0)
+ *id = 0;
+ else if (enable_word == 0)
+ *id = 1;
+ else
+ *id = 2;
end:
return err;
}
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 7bfdf9c51416..1610c38337af 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -681,7 +681,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* WARQ is at offset 12 */
tmp = (reg & AD_DS_WSMC_WARQ) ?
- (((reg & AD_DS_WSMC_WARQ >> 12) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_WSMC_WARQ) >> 12) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1;
snd_iprintf(buffer, "Wave FIFO: %d %s words\n\n", tmp,
@@ -693,7 +693,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* SYRQ is at offset 4 */
tmp = (reg & AD_DS_WSMC_SYRQ) ?
- (((reg & AD_DS_WSMC_SYRQ >> 4) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_WSMC_SYRQ) >> 4) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_WSMC_WAST) ? 2 : 1;
snd_iprintf(buffer, "Synthesis FIFO: %d %s words\n\n", tmp,
@@ -709,7 +709,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* ACRQ is at offset 4 */
tmp = (reg & AD_DS_RAMC_ACRQ) ?
- (((reg & AD_DS_RAMC_ACRQ >> 4) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_RAMC_ACRQ) >> 4) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1;
snd_iprintf(buffer, "ADC FIFO: %d %s words\n\n", tmp,
@@ -720,7 +720,7 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe
/* RERQ is at offset 12 */
tmp = (reg & AD_DS_RAMC_RERQ) ?
- (((reg & AD_DS_RAMC_RERQ >> 12) & 0x01) ? 12 : 18) : 4;
+ ((((reg & AD_DS_RAMC_RERQ) >> 12) & 0x01) ? 12 : 18) : 4;
tmp /= (reg & AD_DS_RAMC_ADST) ? 2 : 1;
snd_iprintf(buffer, "Resampler FIFO: %d %s words\n\n", tmp,
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index cfcca4c30d4d..48b6c5a3884f 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -219,6 +219,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, LPT_LP},"
"{Intel, WPT_LP},"
"{Intel, SPT},"
+ "{Intel, SPT_LP},"
"{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
@@ -297,7 +298,8 @@ enum {
/* quirks for ATI/AMD HDMI */
#define AZX_DCAPS_PRESET_ATI_HDMI \
- (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB)
+ (AZX_DCAPS_NO_TCSEL | AZX_DCAPS_SYNC_WRITE | AZX_DCAPS_POSFIX_LPIB|\
+ AZX_DCAPS_NO_MSI64)
/* quirks for Nvidia */
#define AZX_DCAPS_PRESET_NVIDIA \
@@ -374,6 +376,8 @@ static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool
#ifdef CONFIG_SND_DMA_SGBUF
if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) {
struct snd_sg_buf *sgbuf = dmab->private_data;
+ if (chip->driver_type == AZX_DRIVER_CMEDIA)
+ return; /* deal with only CORB/RIRB buffers */
if (on)
set_pages_array_wc(sgbuf->page_table, sgbuf->pages);
else
@@ -1483,6 +1487,7 @@ static int azx_first_init(struct azx *chip)
struct snd_card *card = chip->card;
int err;
unsigned short gcap;
+ unsigned int dma_bits = 64;
#if BITS_PER_LONG != 64
/* Fix up base address on ULI M5461 */
@@ -1506,9 +1511,14 @@ static int azx_first_init(struct azx *chip)
return -ENXIO;
}
- if (chip->msi)
+ if (chip->msi) {
+ if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
+ dev_dbg(card->dev, "Disabling 64bit MSI\n");
+ pci->no_64bit_msi = true;
+ }
if (pci_enable_msi(pci) < 0)
chip->msi = 0;
+ }
if (azx_acquire_irq(chip, 0) < 0)
return -EBUSY;
@@ -1519,9 +1529,14 @@ static int azx_first_init(struct azx *chip)
gcap = azx_readw(chip, GCAP);
dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
+ /* AMD devices support 40 or 48bit DMA, take the safe one */
+ if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
+ dma_bits = 40;
+
/* disable SB600 64bit support for safety */
if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
struct pci_dev *p_smbus;
+ dma_bits = 40;
p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
PCI_DEVICE_ID_ATI_SBX00_SMBUS,
NULL);
@@ -1551,9 +1566,11 @@ static int azx_first_init(struct azx *chip)
}
/* allow 64bit DMA address if supported by H/W */
- if ((gcap & AZX_GCAP_64OK) && !pci_set_dma_mask(pci, DMA_BIT_MASK(64)))
- pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(64));
- else {
+ if (!(gcap & AZX_GCAP_64OK))
+ dma_bits = 32;
+ if (!pci_set_dma_mask(pci, DMA_BIT_MASK(dma_bits))) {
+ pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(dma_bits));
+ } else {
pci_set_dma_mask(pci, DMA_BIT_MASK(32));
pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(32));
}
@@ -1769,7 +1786,7 @@ static void pcm_mmap_prepare(struct snd_pcm_substream *substream,
#ifdef CONFIG_X86
struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
struct azx *chip = apcm->chip;
- if (!azx_snoop(chip))
+ if (!azx_snoop(chip) && chip->driver_type != AZX_DRIVER_CMEDIA)
area->vm_page_prot = pgprot_writecombine(area->vm_page_prot);
#endif
}
@@ -2002,6 +2019,9 @@ static const struct pci_device_id azx_ids[] = {
/* Sunrise Point */
{ PCI_DEVICE(0x8086, 0xa170),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
+ /* Sunrise Point-LP */
+ { PCI_DEVICE(0x8086, 0x9d70),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0a0c),
.driver_data = AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL },
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 7eb44e78e141..62658f2f8c9f 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -419,7 +419,7 @@ struct snd_hda_pin_quirk {
.subvendor = _subvendor,\
.name = _name,\
.value = _value,\
- .pins = (const struct hda_pintbl[]) { _pins } \
+ .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
}
#else
@@ -427,7 +427,7 @@ struct snd_hda_pin_quirk {
{ .codec = _codec,\
.subvendor = _subvendor,\
.value = _value,\
- .pins = (const struct hda_pintbl[]) { _pins } \
+ .pins = (const struct hda_pintbl[]) { _pins, {0, 0}} \
}
#endif
diff --git a/sound/pci/hda/hda_priv.h b/sound/pci/hda/hda_priv.h
index 949cd437eeb2..5016014e57f2 100644
--- a/sound/pci/hda/hda_priv.h
+++ b/sound/pci/hda/hda_priv.h
@@ -171,6 +171,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
#define AZX_DCAPS_PM_RUNTIME (1 << 26) /* runtime PM support */
#define AZX_DCAPS_I915_POWERWELL (1 << 27) /* HSW i915 powerwell support */
#define AZX_DCAPS_CORBRP_SELF_CLEAR (1 << 28) /* CORBRP clears itself after reset */
+#define AZX_DCAPS_NO_MSI64 (1 << 29) /* Stick to 32-bit MSIs */
/* HD Audio class code */
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 71e4bad06345..e9ebc7bd752c 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -43,6 +43,7 @@ struct conexant_spec {
unsigned int num_eapds;
hda_nid_t eapds[4];
bool dynamic_eapd;
+ hda_nid_t mute_led_eapd;
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
@@ -163,6 +164,17 @@ static void cx_auto_vmaster_hook(void *private_data, int enabled)
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, enabled);
}
+/* turn on/off EAPD according to Master switch (inversely!) for mute LED */
+static void cx_auto_vmaster_hook_mute_led(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ snd_hda_codec_write(codec, spec->mute_led_eapd, 0,
+ AC_VERB_SET_EAPD_BTLENABLE,
+ enabled ? 0x00 : 0x02);
+}
+
static int cx_auto_build_controls(struct hda_codec *codec)
{
int err;
@@ -223,6 +235,7 @@ enum {
CXT_FIXUP_TOSHIBA_P105,
CXT_FIXUP_HP_530,
CXT_FIXUP_CAP_MIX_AMP_5047,
+ CXT_FIXUP_MUTE_LED_EAPD,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -557,6 +570,18 @@ static void cxt_fixup_olpc_xo(struct hda_codec *codec,
}
}
+static void cxt_fixup_mute_led_eapd(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->mute_led_eapd = 0x1b;
+ spec->dynamic_eapd = 1;
+ spec->gen.vmaster_mute.hook = cx_auto_vmaster_hook_mute_led;
+ }
+}
+
/*
* Fix max input level on mixer widget to 0dB
* (originally it has 0x2b steps with 0dB offset 0x14)
@@ -705,6 +730,10 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_cap_mix_amp_5047,
},
+ [CXT_FIXUP_MUTE_LED_EAPD] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_eapd,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -762,6 +791,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410),
+ SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD),
SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x17aa, 0x397b, "Lenovo S205", CXT_FIXUP_STEREO_DMIC),
@@ -780,6 +810,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .id = CXT_PINCFG_LEMOTE_A1004, .name = "lemote-a1004" },
{ .id = CXT_PINCFG_LEMOTE_A1205, .name = "lemote-a1205" },
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
+ { .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
{}
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 39862e98551c..9dc9cf8c90e9 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1583,19 +1583,22 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
}
}
- if (pin_eld->eld_valid && !eld->eld_valid) {
- update_eld = true;
+ if (pin_eld->eld_valid != eld->eld_valid)
eld_changed = true;
- }
+
+ if (pin_eld->eld_valid && !eld->eld_valid)
+ update_eld = true;
+
if (update_eld) {
bool old_eld_valid = pin_eld->eld_valid;
pin_eld->eld_valid = eld->eld_valid;
- eld_changed = pin_eld->eld_size != eld->eld_size ||
+ if (pin_eld->eld_size != eld->eld_size ||
memcmp(pin_eld->eld_buffer, eld->eld_buffer,
- eld->eld_size) != 0;
- if (eld_changed)
+ eld->eld_size) != 0) {
memcpy(pin_eld->eld_buffer, eld->eld_buffer,
eld->eld_size);
+ eld_changed = true;
+ }
pin_eld->eld_size = eld->eld_size;
pin_eld->info = eld->info;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index bc86c36b4bfa..b118a5be18df 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -288,21 +288,91 @@ static void alc880_unsol_event(struct hda_codec *codec, unsigned int res)
snd_hda_jack_unsol_event(codec, res >> 2);
}
-/* additional initialization for ALC888 variants */
-static void alc888_coef_init(struct hda_codec *codec)
+/* Change EAPD to verb control */
+static void alc_fill_eapd_coef(struct hda_codec *codec)
{
- if (alc_get_coef0(codec) == 0x20)
- /* alc888S-VC */
- alc_write_coef_idx(codec, 7, 0x830);
- else
- /* alc888-VB */
- alc_write_coef_idx(codec, 7, 0x3030);
+ int coef;
+
+ coef = alc_get_coef0(codec);
+
+ switch (codec->vendor_id) {
+ case 0x10ec0262:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<5);
+ break;
+ case 0x10ec0267:
+ case 0x10ec0268:
+ alc_update_coef_idx(codec, 0x7, 0, 1<<13);
+ break;
+ case 0x10ec0269:
+ if ((coef & 0x00f0) == 0x0010)
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14);
+ if ((coef & 0x00f0) == 0x0020)
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0280:
+ case 0x10ec0284:
+ case 0x10ec0290:
+ case 0x10ec0292:
+ alc_update_coef_idx(codec, 0x4, 1<<15, 0);
+ break;
+ case 0x10ec0233:
+ case 0x10ec0255:
+ case 0x10ec0282:
+ case 0x10ec0283:
+ case 0x10ec0286:
+ case 0x10ec0288:
+ alc_update_coef_idx(codec, 0x10, 1<<9, 0);
+ break;
+ case 0x10ec0285:
+ case 0x10ec0293:
+ alc_update_coef_idx(codec, 0xa, 1<<13, 0);
+ break;
+ case 0x10ec0662:
+ if ((coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
+ break;
+ case 0x10ec0272:
+ case 0x10ec0273:
+ case 0x10ec0663:
+ case 0x10ec0665:
+ case 0x10ec0670:
+ case 0x10ec0671:
+ case 0x10ec0672:
+ alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
+ break;
+ case 0x10ec0668:
+ alc_update_coef_idx(codec, 0x7, 3<<13, 0);
+ break;
+ case 0x10ec0867:
+ alc_update_coef_idx(codec, 0x4, 1<<10, 0);
+ break;
+ case 0x10ec0888:
+ if ((coef & 0x00f0) == 0x0020 || (coef & 0x00f0) == 0x0030)
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0892:
+ alc_update_coef_idx(codec, 0x7, 1<<5, 0);
+ break;
+ case 0x10ec0899:
+ case 0x10ec0900:
+ alc_update_coef_idx(codec, 0x7, 1<<1, 0);
+ break;
+ }
}
-/* additional initialization for ALC889 variants */
-static void alc889_coef_init(struct hda_codec *codec)
+/* additional initialization for ALC888 variants */
+static void alc888_coef_init(struct hda_codec *codec)
{
- alc_update_coef_idx(codec, 7, 0, 0x2010);
+ switch (alc_get_coef0(codec) & 0x00f0) {
+ /* alc888-VA */
+ case 0x00:
+ /* alc888-VB */
+ case 0x10:
+ alc_update_coef_idx(codec, 7, 0, 0x2030); /* Turn EAPD to High */
+ break;
+ }
}
/* turn on/off EAPD control (only if available) */
@@ -343,6 +413,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
/* generic EAPD initialization */
static void alc_auto_init_amp(struct hda_codec *codec, int type)
{
+ alc_fill_eapd_coef(codec);
alc_auto_setup_eapd(codec, true);
switch (type) {
case ALC_INIT_GPIO1:
@@ -359,25 +430,15 @@ static void alc_auto_init_amp(struct hda_codec *codec, int type)
case 0x10ec0260:
alc_update_coefex_idx(codec, 0x1a, 7, 0, 0x2010);
break;
- case 0x10ec0262:
case 0x10ec0880:
case 0x10ec0882:
case 0x10ec0883:
case 0x10ec0885:
- case 0x10ec0887:
- /*case 0x10ec0889:*/ /* this causes an SPDIF problem */
- case 0x10ec0900:
- alc889_coef_init(codec);
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
break;
case 0x10ec0888:
alc888_coef_init(codec);
break;
-#if 0 /* XXX: This may cause the silent output on speaker on some machines */
- case 0x10ec0267:
- case 0x10ec0268:
- alc_update_coef_idx(codec, 7, 0, 0x3000);
- break;
-#endif /* XXX */
}
break;
}
@@ -1710,7 +1771,7 @@ static void alc889_fixup_coef(struct hda_codec *codec,
{
if (action != HDA_FIXUP_ACT_INIT)
return;
- alc889_coef_init(codec);
+ alc_update_coef_idx(codec, 7, 0, 0x2030);
}
/* toggle speaker-output according to the hp-jack state */
@@ -2675,7 +2736,7 @@ static void alc269_shutup(struct hda_codec *codec)
static struct coef_fw alc282_coefs[] = {
WRITE_COEF(0x03, 0x0002), /* Power Down Control */
- WRITE_COEF(0x05, 0x0700), /* FIFO and filter clock */
+ UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
WRITE_COEF(0x07, 0x0200), /* DMIC control */
UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
@@ -2786,7 +2847,7 @@ static void alc282_shutup(struct hda_codec *codec)
static struct coef_fw alc283_coefs[] = {
WRITE_COEF(0x03, 0x0002), /* Power Down Control */
- WRITE_COEF(0x05, 0x0700), /* FIFO and filter clock */
+ UPDATE_COEF(0x05, 0xff3f, 0x0700), /* FIFO and filter clock */
WRITE_COEF(0x07, 0x0200), /* DMIC control */
UPDATE_COEF(0x06, 0x00f0, 0), /* Analog clock */
UPDATE_COEF(0x08, 0xfffc, 0x0c2c), /* JD */
@@ -2817,6 +2878,7 @@ static struct coef_fw alc283_coefs[] = {
UPDATE_COEF(0x40, 0xf800, 0x9800), /* Class D DC enable */
UPDATE_COEF(0x42, 0xf000, 0x2000), /* DC offset */
WRITE_COEF(0x37, 0xfc06), /* Class D amp control */
+ UPDATE_COEF(0x1b, 0x8000, 0), /* HP JD control */
{}
};
@@ -2884,6 +2946,9 @@ static void alc283_shutup(struct hda_codec *codec)
alc_write_coef_idx(codec, 0x43, 0x9004);
+ /*depop hp during suspend*/
+ alc_write_coef_idx(codec, 0x06, 0x2100);
+
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
@@ -3346,6 +3411,27 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
}
}
+static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Like hp_gpio_mic1_led, but also needs GPIO4 low to enable headphone amp */
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x18 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x18 },
+ {}
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->cap_mute_led_nid = 0x18;
+ snd_hda_add_verbs(codec, gpio_init);
+ codec->power_filter = led_power_filter;
+ }
+}
+
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4213,6 +4299,7 @@ enum {
ALC283_FIXUP_BXBT2807_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
+ ALC280_FIXUP_HP_GPIO4,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -4433,6 +4520,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
},
[ALC269_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
.type = HDA_FIXUP_FUNC,
@@ -4622,6 +4711,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC255_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_headset_mode_alc255,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
},
[ALC255_FIXUP_HEADSET_MODE_NO_HP_MIC] = {
.type = HDA_FIXUP_FUNC,
@@ -4657,8 +4748,6 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_dell_wmi,
- .chained_before = true,
- .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
},
[ALC282_FIXUP_ASPIRE_V5_PINS] = {
.type = HDA_FIXUP_PINS,
@@ -4676,7 +4765,10 @@ static const struct hda_fixup alc269_fixups[] = {
{ },
},
},
-
+ [ALC280_FIXUP_HP_GPIO4] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio4,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -4693,13 +4785,13 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f6, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
- SND_PCI_QUIRK(0x1028, 0x0610, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
SND_PCI_QUIRK(0x1028, 0x0615, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
SND_PCI_QUIRK(0x1028, 0x0616, "Dell Vostro 5470", ALC290_FIXUP_SUBWOOFER_HSJACK),
- SND_PCI_QUIRK(0x1028, 0x061f, "Dell", ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED),
SND_PCI_QUIRK(0x1028, 0x0638, "Dell Inspiron 5439", ALC290_FIXUP_MONO_SPEAKERS_HSJACK),
SND_PCI_QUIRK(0x1028, 0x064a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x064b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -4724,21 +4816,15 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x22cf, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22dc, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x22fb, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x8004, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
/* ALC290 */
SND_PCI_QUIRK(0x103c, 0x221b, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2221, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2225, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2246, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2247, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2248, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2249, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2253, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2254, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2255, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2256, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2257, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2258, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2259, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x225a, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2260, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4747,7 +4833,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2265, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2273, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
- SND_PCI_QUIRK(0x103c, 0x2277, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x2278, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED),
SND_PCI_QUIRK(0x103c, 0x227f, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2282, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4800,7 +4885,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -4980,6 +5065,19 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x17, 0x40000000},
{0x1d, 0x40700001},
{0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
+ {0x12, 0x90a60130},
+ {0x13, 0x40000000},
+ {0x14, 0x90170110},
+ {0x15, 0x0421101f},
+ {0x16, 0x411111f0},
+ {0x17, 0x411111f0},
+ {0x18, 0x411111f0},
+ {0x19, 0x411111f0},
+ {0x1a, 0x04a11020},
+ {0x1b, 0x411111f0},
+ {0x1d, 0x40748605},
+ {0x1e, 0x411111f0}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC269_FIXUP_HP_GPIO_MIC1_LED,
{0x12, 0x90a60140},
{0x13, 0x40000000},
@@ -5190,9 +5288,6 @@ static void alc269_fill_coef(struct hda_codec *codec)
}
}
- /* Class D */
- alc_update_coef_idx(codec, 0xd, 0, 1<<14);
-
/* HP */
alc_update_coef_idx(codec, 0x4, 0, 1<<11);
}
@@ -5610,9 +5705,9 @@ static void alc662_led_gpio1_mute_hook(void *private_data, int enabled)
unsigned int oldval = spec->gpio_led;
if (enabled)
- spec->gpio_led &= ~0x01;
- else
spec->gpio_led |= 0x01;
+ else
+ spec->gpio_led &= ~0x01;
if (spec->gpio_led != oldval)
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led);
@@ -5647,6 +5742,35 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec,
}
}
+static struct coef_fw alc668_coefs[] = {
+ WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0),
+ WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80),
+ WRITE_COEF(0x08, 0x0031), WRITE_COEF(0x0a, 0x0060), WRITE_COEF(0x0b, 0x0),
+ WRITE_COEF(0x0c, 0x7cf7), WRITE_COEF(0x0d, 0x1080), WRITE_COEF(0x0e, 0x7f7f),
+ WRITE_COEF(0x0f, 0xcccc), WRITE_COEF(0x10, 0xddcc), WRITE_COEF(0x11, 0x0001),
+ WRITE_COEF(0x13, 0x0), WRITE_COEF(0x14, 0x2aa0), WRITE_COEF(0x17, 0xa940),
+ WRITE_COEF(0x19, 0x0), WRITE_COEF(0x1a, 0x0), WRITE_COEF(0x1b, 0x0),
+ WRITE_COEF(0x1c, 0x0), WRITE_COEF(0x1d, 0x0), WRITE_COEF(0x1e, 0x7418),
+ WRITE_COEF(0x1f, 0x0804), WRITE_COEF(0x20, 0x4200), WRITE_COEF(0x21, 0x0468),
+ WRITE_COEF(0x22, 0x8ccc), WRITE_COEF(0x23, 0x0250), WRITE_COEF(0x24, 0x7418),
+ WRITE_COEF(0x27, 0x0), WRITE_COEF(0x28, 0x8ccc), WRITE_COEF(0x2a, 0xff00),
+ WRITE_COEF(0x2b, 0x8000), WRITE_COEF(0xa7, 0xff00), WRITE_COEF(0xa8, 0x8000),
+ WRITE_COEF(0xaa, 0x2e17), WRITE_COEF(0xab, 0xa0c0), WRITE_COEF(0xac, 0x0),
+ WRITE_COEF(0xad, 0x0), WRITE_COEF(0xae, 0x2ac6), WRITE_COEF(0xaf, 0xa480),
+ WRITE_COEF(0xb0, 0x0), WRITE_COEF(0xb1, 0x0), WRITE_COEF(0xb2, 0x0),
+ WRITE_COEF(0xb3, 0x0), WRITE_COEF(0xb4, 0x0), WRITE_COEF(0xb5, 0x1040),
+ WRITE_COEF(0xb6, 0xd697), WRITE_COEF(0xb7, 0x902b), WRITE_COEF(0xb8, 0xd697),
+ WRITE_COEF(0xb9, 0x902b), WRITE_COEF(0xba, 0xb8ba), WRITE_COEF(0xbb, 0xaaab),
+ WRITE_COEF(0xbc, 0xaaaf), WRITE_COEF(0xbd, 0x6aaa), WRITE_COEF(0xbe, 0x1c02),
+ WRITE_COEF(0xc0, 0x00ff), WRITE_COEF(0xc1, 0x0fa6),
+ {}
+};
+
+static void alc668_restore_default_value(struct hda_codec *codec)
+{
+ alc_process_coef_fw(codec, alc668_coefs);
+}
+
enum {
ALC662_FIXUP_ASPIRE,
ALC662_FIXUP_LED_GPIO1,
@@ -5919,6 +6043,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x0626, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0696, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800),
SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_BASS_1A),
SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP),
@@ -6072,29 +6197,6 @@ static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
{}
};
-static void alc662_fill_coef(struct hda_codec *codec)
-{
- int coef;
-
- coef = alc_get_coef0(codec);
-
- switch (codec->vendor_id) {
- case 0x10ec0662:
- if ((coef & 0x00f0) == 0x0030)
- alc_update_coef_idx(codec, 0x4, 1<<10, 0); /* EAPD Ctrl */
- break;
- case 0x10ec0272:
- case 0x10ec0273:
- case 0x10ec0663:
- case 0x10ec0665:
- case 0x10ec0670:
- case 0x10ec0671:
- case 0x10ec0672:
- alc_update_coef_idx(codec, 0xd, 0, 1<<14); /* EAPD Ctrl */
- break;
- }
-}
-
/*
*/
static int patch_alc662(struct hda_codec *codec)
@@ -6113,8 +6215,11 @@ static int patch_alc662(struct hda_codec *codec)
alc_fix_pll_init(codec, 0x20, 0x04, 15);
- spec->init_hook = alc662_fill_coef;
- alc662_fill_coef(codec);
+ switch (codec->vendor_id) {
+ case 0x10ec0668:
+ spec->init_hook = alc668_restore_default_value;
+ break;
+ }
snd_hda_pick_fixup(codec, alc662_fixup_models,
alc662_fixup_tbl, alc662_fixups);
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 0e9623368ab0..7d5d6444a837 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -49,7 +49,6 @@ source "sound/soc/mxs/Kconfig"
source "sound/soc/pxa/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
-source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 534714a1ca44..865e090c8061 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -1,10 +1,14 @@
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
snd-soc-core-objs += soc-generic-dmaengine-pcm.o
endif
+ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
+snd-soc-core-objs += soc-ac97.o
+endif
+
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
@@ -26,7 +30,6 @@ obj-$(CONFIG_SND_SOC) += kirkwood/
obj-$(CONFIG_SND_SOC) += pxa/
obj-$(CONFIG_SND_SOC) += rockchip/
obj-$(CONFIG_SND_SOC) += samsung/
-obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 27e3fc4a536b..fb3878312bf8 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -52,12 +52,3 @@ config SND_AT91_SOC_SAM9X5_WM8731
help
Say Y if you want to add support for audio SoC on an
at91sam9x5 based board that is using WM8731 codec.
-
-config SND_AT91_SOC_AFEB9260
- tristate "SoC Audio support for AFEB9260 board"
- depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
- select SND_ATMEL_SOC_PDC
- select SND_ATMEL_SOC_SSC
- select SND_SOC_TLV320AIC23_I2C
- help
- Say Y here to support sound on AFEB9260 board.
diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile
index 5baabc8bde3a..466a821da98c 100644
--- a/sound/soc/atmel/Makefile
+++ b/sound/soc/atmel/Makefile
@@ -17,4 +17,3 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
-obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index f403f399808a..b1cc2a4a7fc0 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
* transmit and receive, so if a value has already
* been set, it must match this value.
*/
- if (ssc_p->cmr_div == 0)
+ if (ssc_p->dir_mask !=
+ (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE))
+ ssc_p->cmr_div = div;
+ else if (ssc_p->cmr_div == 0)
ssc_p->cmr_div = div;
else
if (div != ssc_p->cmr_div)
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
deleted file mode 100644
index 9579799ace54..000000000000
--- a/sound/soc/atmel/snd-soc-afeb9260.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * afeb9260.c -- SoC audio for AFEB9260
- *
- * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <linux/atmel-ssc.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <linux/gpio.h>
-
-#include "../codecs/tlv320aic23.h"
-#include "atmel-pcm.h"
-#include "atmel_ssc_dai.h"
-
-#define CODEC_CLOCK 12000000
-
-static int afeb9260_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 err;
-
- /* Set the codec system clock for DAC and ADC */
- err =
- snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
-
- if (err < 0) {
- printk(KERN_ERR "can't set codec system clock\n");
- return err;
- }
-
- return err;
-}
-
-static struct snd_soc_ops afeb9260_ops = {
- .hw_params = afeb9260_hw_params,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
- SND_SOC_DAPM_HP("Headphone Jack", NULL),
- SND_SOC_DAPM_LINE("Line In", NULL),
- SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
- {"Headphone Jack", NULL, "LHPOUT"},
- {"Headphone Jack", NULL, "RHPOUT"},
-
- {"LLINEIN", NULL, "Line In"},
- {"RLINEIN", NULL, "Line In"},
-
- {"MICIN", NULL, "Mic Jack"},
-};
-
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link afeb9260_dai = {
- .name = "TLV320AIC23",
- .stream_name = "AIC23",
- .cpu_dai_name = "atmel-ssc-dai.0",
- .codec_dai_name = "tlv320aic23-hifi",
- .platform_name = "atmel_pcm-audio",
- .codec_name = "tlv320aic23-codec.0-001a",
- .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBM_CFM,
- .ops = &afeb9260_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_machine_afeb9260 = {
- .name = "AFEB9260",
- .owner = THIS_MODULE,
- .dai_link = &afeb9260_dai,
- .num_links = 1,
-
- .dapm_widgets = tlv320aic23_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
- .dapm_routes = afeb9260_audio_map,
- .num_dapm_routes = ARRAY_SIZE(afeb9260_audio_map),
-};
-
-static struct platform_device *afeb9260_snd_device;
-
-static int __init afeb9260_soc_init(void)
-{
- int err;
- struct device *dev;
-
- if (!(machine_is_afeb9260()))
- return -ENODEV;
-
-
- afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
- if (!afeb9260_snd_device) {
- printk(KERN_ERR "ASoC: Platform device allocation failed\n");
- return -ENOMEM;
- }
-
- platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260);
- err = platform_device_add(afeb9260_snd_device);
- if (err)
- goto err1;
-
- dev = &afeb9260_snd_device->dev;
-
- return 0;
-err1:
- platform_device_put(afeb9260_snd_device);
- return err;
-}
-
-static void __exit afeb9260_soc_exit(void)
-{
- platform_device_unregister(afeb9260_snd_device);
-}
-
-module_init(afeb9260_soc_init);
-module_exit(afeb9260_soc_exit);
-
-MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
-MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
-MODULE_LICENSE("GPL");
-
diff --git a/sound/soc/au1x/ac97c.c b/sound/soc/au1x/ac97c.c
index c8a2de103c5f..5159a50a45a6 100644
--- a/sound/soc/au1x/ac97c.c
+++ b/sound/soc/au1x/ac97c.c
@@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver au1xac97c_dai_driver = {
.name = "alchemy-ac97c",
- .ac97_control = 1,
+ .bus_control = true,
.probe = au1xac97c_dai_probe,
.playback = {
.rates = AC97_RATES,
diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c
index 84f31e1f9d24..c6daec98ff89 100644
--- a/sound/soc/au1x/psc-ac97.c
+++ b/sound/soc/au1x/psc-ac97.c
@@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
};
static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
- .ac97_control = 1,
+ .bus_control = true,
.probe = au1xpsc_ac97_probe,
.playback = {
.rates = AC97_RATES,
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
index e82eb373a731..6bf21a6c02e4 100644
--- a/sound/soc/blackfin/bf5xx-ac97.c
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
#endif
static struct snd_soc_dai_driver bfin_ac97_dai = {
- .ac97_control = 1,
+ .bus_control = true,
.suspend = bf5xx_ac97_suspend,
.resume = bf5xx_ac97_resume,
.playback = {
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
index 3450e8f9080d..0fa81a523b8a 100644
--- a/sound/soc/blackfin/bf5xx-ad1980.c
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -46,8 +46,6 @@
#include <linux/gpio.h>
#include <asm/portmux.h>
-#include "../codecs/ad1980.h"
-
#include "bf5xx-ac97.h"
static struct snd_soc_card bf5xx_board;
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index 5477c5475923..7b7fbcd49e5e 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -36,7 +36,8 @@ config SND_EP93XX_SOC_EDB93XX
tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
select SND_EP93XX_SOC_I2S
- select SND_SOC_CS4271
+ select SND_SOC_CS4271_I2C if I2C
+ select SND_SOC_CS4271_SPI if SPI_MASTER
help
Say Y or M here if you want to add support for I2S audio on the
Cirrus Logic EDB93xx boards.
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index f30dadf85b99..6b8a366b0211 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
static struct snd_soc_dai_driver ep93xx_ac97_dai = {
.name = "ep93xx-ac97",
.id = 0,
- .ac97_control = 1,
+ .bus_control = true,
.probe = ep93xx_ac97_dai_probe,
.playback = {
.stream_name = "AC97 Playback",
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index a68d1731a8fd..1362edd380e5 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -50,7 +50,8 @@ config SND_SOC_ALL_CODECS
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_CS4271_I2C if I2C
+ select SND_SOC_CS4271_SPI if SPI_MASTER
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if I2C
@@ -223,6 +224,7 @@ config SND_SOC_AD193X_I2C
select SND_SOC_AD193X
config SND_SOC_AD1980
+ select REGMAP_AC97
tristate
config SND_SOC_AD73311
@@ -336,7 +338,8 @@ config SND_SOC_CS42L51
tristate
config SND_SOC_CS42L51_I2C
- tristate
+ tristate "Cirrus Logic CS42L51 CODEC (I2C)"
+ depends on I2C
select SND_SOC_CS42L51
config SND_SOC_CS42L52
@@ -370,8 +373,19 @@ config SND_SOC_CS4270_VD33_ERRATA
depends on SND_SOC_CS4270
config SND_SOC_CS4271
- tristate "Cirrus Logic CS4271 CODEC"
- depends on SND_SOC_I2C_AND_SPI
+ tristate
+
+config SND_SOC_CS4271_I2C
+ tristate "Cirrus Logic CS4271 CODEC (I2C)"
+ depends on I2C
+ select SND_SOC_CS4271
+ select REGMAP_I2C
+
+config SND_SOC_CS4271_SPI
+ tristate "Cirrus Logic CS4271 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_CS4271
+ select REGMAP_SPI
config SND_SOC_CS42XX8
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 5dce451661e4..ac7ec31f8cbe 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -41,6 +41,8 @@ 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-cs4271-i2c-objs := cs4271-i2c.o
+snd-soc-cs4271-spi-objs := cs4271-spi.o
snd-soc-cs42xx8-objs := cs42xx8.o
snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
snd-soc-cx20442-objs := cx20442.o
@@ -217,6 +219,8 @@ 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_CS4271_I2C) += snd-soc-cs4271-i2c.o
+obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index fd43827bb856..7dfbc9921e91 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -126,13 +126,13 @@ struct ab8500_codec_drvdata_dbg {
/* Private data for AB8500 device-driver */
struct ab8500_codec_drvdata {
struct regmap *regmap;
+ struct mutex ctrl_lock;
/* Sidetone */
long *sid_fir_values;
enum sid_state sid_status;
/* ANC */
- struct mutex anc_lock;
long *anc_fir_values;
long *anc_iir_values;
enum anc_state anc_status;
@@ -1129,9 +1129,9 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
- mutex_lock(&codec->mutex);
+ mutex_lock(&drvdata->ctrl_lock);
ucontrol->value.integer.value[0] = drvdata->sid_status;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
@@ -1154,7 +1154,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
return -EIO;
}
- mutex_lock(&codec->mutex);
+ mutex_lock(&drvdata->ctrl_lock);
sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
@@ -1185,7 +1185,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
drvdata->sid_status = SID_FIR_CONFIGURED;
out:
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&drvdata->ctrl_lock);
dev_dbg(codec->dev, "%s: Exit\n", __func__);
@@ -1198,9 +1198,9 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
- mutex_lock(&codec->mutex);
+ mutex_lock(&drvdata->ctrl_lock);
ucontrol->value.integer.value[0] = drvdata->anc_status;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
@@ -1217,7 +1217,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
dev_dbg(dev, "%s: Enter.\n", __func__);
- mutex_lock(&drvdata->anc_lock);
+ mutex_lock(&drvdata->ctrl_lock);
req = ucontrol->value.integer.value[0];
if (req >= ARRAY_SIZE(enum_anc_state)) {
@@ -1244,9 +1244,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
}
snd_soc_dapm_sync(&codec->dapm);
- mutex_lock(&codec->mutex);
anc_configure(codec, apply_fir, apply_iir);
- mutex_unlock(&codec->mutex);
if (apply_fir) {
if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1265,7 +1263,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
snd_soc_dapm_sync(&codec->dapm);
cleanup:
- mutex_unlock(&drvdata->anc_lock);
+ mutex_unlock(&drvdata->ctrl_lock);
if (status < 0)
dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
@@ -1294,14 +1292,15 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
unsigned int i;
- mutex_lock(&codec->mutex);
+ mutex_lock(&drvdata->ctrl_lock);
for (i = 0; i < fc->count; i++)
ucontrol->value.integer.value[i] = fc->value[i];
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
@@ -1310,14 +1309,15 @@ static int filter_control_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
struct filter_control *fc =
(struct filter_control *)kcontrol->private_value;
unsigned int i;
- mutex_lock(&codec->mutex);
+ mutex_lock(&drvdata->ctrl_lock);
for (i = 0; i < fc->count; i++)
fc->value[i] = ucontrol->value.integer.value[i];
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&drvdata->ctrl_lock);
return 0;
}
@@ -2545,7 +2545,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
(void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
- mutex_init(&drvdata->anc_lock);
+ mutex_init(&drvdata->ctrl_lock);
return status;
}
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index bd9b1839c8b0..c6e5a313ebf4 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
- return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
+ return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
}
#define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
@@ -53,7 +54,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = {
static struct snd_soc_dai_driver ac97_dai = {
.name = "ac97-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 1,
@@ -71,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = {
static int ac97_soc_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
struct snd_ac97_bus *ac97_bus;
struct snd_ac97_template ac97_template;
int ret;
@@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
return ret;
memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
- ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+ ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
if (ret < 0)
return ret;
+ snd_soc_codec_set_drvdata(codec, ac97);
+
return 0;
}
#ifdef CONFIG_PM
static int ac97_soc_suspend(struct snd_soc_codec *codec)
{
- snd_ac97_suspend(codec->ac97);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_ac97_suspend(ac97);
return 0;
}
static int ac97_soc_resume(struct snd_soc_codec *codec)
{
- snd_ac97_resume(codec->ac97);
+
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_ac97_resume(ac97);
return 0;
}
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 6844d0b2af68..387530b0b0fd 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
};
static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
+ SND_SOC_DAPM_VMID("VMID"),
SND_SOC_DAPM_OUTPUT("DAC1OUT"),
SND_SOC_DAPM_OUTPUT("DAC2OUT"),
SND_SOC_DAPM_OUTPUT("DAC3OUT"),
@@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
static const struct snd_soc_dapm_route audio_paths[] = {
{ "DAC", NULL, "SYSCLK" },
+ { "DAC Output", NULL, "DAC" },
+ { "DAC Output", NULL, "VMID" },
{ "ADC", NULL, "SYSCLK" },
{ "DAC", NULL, "ADC_PWR" },
{ "ADC", NULL, "ADC_PWR" },
- { "DAC1OUT", NULL, "DAC" },
- { "DAC2OUT", NULL, "DAC" },
- { "DAC3OUT", NULL, "DAC" },
- { "DAC4OUT", NULL, "DAC" },
+ { "DAC1OUT", NULL, "DAC Output" },
+ { "DAC2OUT", NULL, "DAC Output" },
+ { "DAC3OUT", NULL, "DAC Output" },
+ { "DAC4OUT", NULL, "DAC Output" },
{ "ADC", NULL, "ADC1IN" },
{ "ADC", NULL, "ADC2IN" },
{ "SYSCLK", NULL, "PLL_PWR" },
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 304d3003339a..2860eef8610c 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -24,34 +24,86 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include "ad1980.h"
+static const struct reg_default ad1980_reg_defaults[] = {
+ { 0x02, 0x8000 },
+ { 0x04, 0x8000 },
+ { 0x06, 0x8000 },
+ { 0x0c, 0x8008 },
+ { 0x0e, 0x8008 },
+ { 0x10, 0x8808 },
+ { 0x12, 0x8808 },
+ { 0x16, 0x8808 },
+ { 0x18, 0x8808 },
+ { 0x1a, 0x0000 },
+ { 0x1c, 0x8000 },
+ { 0x20, 0x0000 },
+ { 0x28, 0x03c7 },
+ { 0x2c, 0xbb80 },
+ { 0x2e, 0xbb80 },
+ { 0x30, 0xbb80 },
+ { 0x32, 0xbb80 },
+ { 0x36, 0x8080 },
+ { 0x38, 0x8080 },
+ { 0x3a, 0x2000 },
+ { 0x60, 0x0000 },
+ { 0x62, 0x0000 },
+ { 0x72, 0x0000 },
+ { 0x74, 0x1001 },
+ { 0x76, 0x0000 },
+};
-/*
- * AD1980 register cache
- */
-static const u16 ad1980_reg[] = {
- 0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6 */
- 0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e */
- 0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
- 0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
- 0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
- 0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
- 0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
- 0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
- 0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
- 0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
- 0x0000, 0x0000, 0x4144, 0x5370 /* 78 - 7e */
+static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_RESET ... AC97_MASTER_MONO:
+ case AC97_PHONE ... AC97_CD:
+ case AC97_AUX ... AC97_GENERAL_PURPOSE:
+ case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+ case AC97_SPDIF:
+ case AC97_CODEC_CLASS_REV:
+ case AC97_PCI_SVID:
+ case AC97_AD_CODEC_CFG:
+ case AC97_AD_JACK_SPDIF:
+ case AC97_AD_SERIAL_CFG:
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case AC97_VENDOR_ID1:
+ case AC97_VENDOR_ID2:
+ return false;
+ default:
+ return ad1980_readable_reg(dev, reg);
+ }
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+ .reg_bits = 16,
+ .reg_stride = 2,
+ .val_bits = 16,
+ .max_register = 0x7e,
+ .cache_type = REGCACHE_RBTREE,
+
+ .volatile_reg = regmap_ac97_default_volatile,
+ .readable_reg = ad1980_readable_reg,
+ .writeable_reg = ad1980_writeable_reg,
+
+ .reg_defaults = ad1980_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
};
static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
@@ -134,45 +186,8 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
{ "HP_OUT_R", NULL, "Playback" },
};
-static unsigned int ac97_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- u16 *cache = codec->reg_cache;
-
- switch (reg) {
- case AC97_RESET:
- case AC97_INT_PAGING:
- case AC97_POWERDOWN:
- case AC97_EXTENDED_STATUS:
- case AC97_VENDOR_ID1:
- case AC97_VENDOR_ID2:
- return soc_ac97_ops->read(codec->ac97, reg);
- default:
- reg = reg >> 1;
-
- if (reg >= ARRAY_SIZE(ad1980_reg))
- return -EINVAL;
-
- return cache[reg];
- }
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- u16 *cache = codec->reg_cache;
-
- soc_ac97_ops->write(codec->ac97, reg, val);
- reg = reg >> 1;
- if (reg < ARRAY_SIZE(ad1980_reg))
- cache[reg] = val;
-
- return 0;
-}
-
static struct snd_soc_dai_driver ad1980_dai = {
.name = "ad1980-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
@@ -189,108 +204,115 @@ static struct snd_soc_dai_driver ad1980_dai = {
static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
unsigned int retry_cnt = 0;
do {
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
- if (ac97_read(codec, AC97_RESET) == 0x0090)
+ soc_ac97_ops->warm_reset(ac97);
+ if (snd_soc_read(codec, AC97_RESET) == 0x0090)
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(ac97);
/*
* Set bit 16slot in register 74h, then every slot will has only
* 16 bits. This command is sent out in 20bit mode, in which
* case the first nibble of data is eaten by the addr. (Tag is
* always 16 bit)
*/
- ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+ snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
- if (ac97_read(codec, AC97_RESET) == 0x0090)
+ if (snd_soc_read(codec, AC97_RESET) == 0x0090)
return 0;
} while (retry_cnt++ < 10);
- printk(KERN_ERR "AD1980 AC97 reset failed\n");
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
return -EIO;
}
static int ad1980_soc_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
+ struct regmap *regmap;
int ret;
u16 vendor_id2;
u16 ext_status;
- printk(KERN_INFO "AD1980 SoC Audio Codec\n");
-
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
return ret;
}
+ regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+ if (IS_ERR(regmap)) {
+ ret = PTR_ERR(regmap);
+ goto err_free_ac97;
+ }
+
+ snd_soc_codec_init_regmap(codec, regmap);
+ snd_soc_codec_set_drvdata(codec, ac97);
+
ret = ad1980_reset(codec, 0);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
+ if (ret < 0)
goto reset_err;
- }
/* Read out vendor ID to make sure it is ad1980 */
- if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
+ if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
ret = -ENODEV;
goto reset_err;
}
- vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+ vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
if (vendor_id2 != 0x5370) {
if (vendor_id2 != 0x5374) {
ret = -ENODEV;
goto reset_err;
} else {
- printk(KERN_WARNING "ad1980: "
- "Found AD1981 - only 2/2 IN/OUT Channels "
- "supported\n");
+ dev_warn(codec->dev,
+ "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
}
}
/* unmute captures and playbacks volume */
- ac97_write(codec, AC97_MASTER, 0x0000);
- ac97_write(codec, AC97_PCM, 0x0000);
- ac97_write(codec, AC97_REC_GAIN, 0x0000);
- ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
- ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+ snd_soc_write(codec, AC97_MASTER, 0x0000);
+ snd_soc_write(codec, AC97_PCM, 0x0000);
+ snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
+ snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+ snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
/*power on LFE/CENTER/Surround DACs*/
- ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
- ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
-
- snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
- ARRAY_SIZE(ad1980_snd_ac97_controls));
+ ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
+ snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_codec_exit_regmap(codec);
+err_free_ac97:
+ snd_soc_free_ac97_codec(ac97);
return ret;
}
static int ad1980_soc_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_codec_exit_regmap(codec);
+ snd_soc_free_ac97_codec(ac97);
return 0;
}
static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
.probe = ad1980_soc_probe,
.remove = ad1980_soc_remove,
- .reg_cache_size = ARRAY_SIZE(ad1980_reg),
- .reg_word_size = sizeof(u16),
- .reg_cache_default = ad1980_reg,
- .reg_cache_step = 2,
- .write = ac97_write,
- .read = ac97_read,
+ .controls = ad1980_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
.dapm_widgets = ad1980_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
.dapm_routes = ad1980_dapm_routes,
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
deleted file mode 100644
index eb0af44ad3df..000000000000
--- a/sound/soc/codecs/ad1980.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * ad1980.h -- ad1980 Soc Audio driver
- *
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#ifndef _AD1980_H
-#define _AD1980_H
-/* Bit definition of Power-Down Control/Status Register */
-#define ADC 0x0001
-#define DAC 0x0002
-#define ANL 0x0004
-#define REF 0x0008
-#define PR0 0x0100
-#define PR1 0x0200
-#define PR2 0x0400
-#define PR3 0x0800
-#define PR4 0x1000
-#define PR5 0x2000
-#define PR6 0x4000
-
-#endif
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 7c784ad3e8b2..783dcb57043a 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int pll_id = w->name[3] - '1';
unsigned int val;
@@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = source->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int dai;
const char *clk;
@@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
static int adau1373_check_src(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = source->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
unsigned int dai;
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index 5518ebd6947c..16093dc89441 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
/* After any power changes have been made the dejitter circuit
* has to be reinitialized. */
@@ -405,6 +406,7 @@ static const struct snd_soc_dapm_widget adau1761_dapm_widgets[] = {
2, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Slew Clock", ADAU1761_CLK_ENABLE0, 6, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ALC Clock", ADAU1761_CLK_ENABLE0, 5, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("Digital Clock 0", 1, ADAU1761_CLK_ENABLE1,
0, 0, NULL, 0),
@@ -436,6 +438,9 @@ static const struct snd_soc_dapm_route adau1761_dapm_routes[] = {
{ "Right Playback Mixer", NULL, "Slew Clock" },
{ "Left Playback Mixer", NULL, "Slew Clock" },
+ { "Left Input Mixer", NULL, "ALC Clock" },
+ { "Right Input Mixer", NULL, "ALC Clock" },
+
{ "Digital Clock 0", NULL, "SYSCLK" },
{ "Digital Clock 1", NULL, "SYSCLK" },
};
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index e9fc00fb13dd..aa6a37cc44b7 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
/* After any power changes have been made the dejitter circuit
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 3e16c1c64115..427ad77bfe56 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct adau *adau = snd_soc_codec_get_drvdata(codec);
int ret;
if (SND_SOC_DAPM_EVENT_ON(event)) {
diff --git a/sound/soc/codecs/adav80x.c b/sound/soc/codecs/adav80x.c
index ce3cdca9fc62..b67480f1b1aa 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = source->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
const char *clk;
@@ -236,7 +236,7 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
- struct snd_soc_codec *codec = source->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 30e297890fec..9130d916f2f4 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -373,33 +373,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
.ops = &ak4535_dai_ops,
};
-static int ak4535_suspend(struct snd_soc_codec *codec)
-{
- ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static int ak4535_resume(struct snd_soc_codec *codec)
{
snd_soc_cache_sync(codec);
- ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- return 0;
-}
-
-static int ak4535_probe(struct snd_soc_codec *codec)
-{
- /* power on device */
- ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- snd_soc_add_codec_controls(codec, ak4535_snd_controls,
- ARRAY_SIZE(ak4535_snd_controls));
- return 0;
-}
-
-/* power down chip */
-static int ak4535_remove(struct snd_soc_codec *codec)
-{
- ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -416,11 +392,12 @@ static const struct regmap_config ak4535_regmap = {
};
static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
- .probe = ak4535_probe,
- .remove = ak4535_remove,
- .suspend = ak4535_suspend,
.resume = ak4535_resume,
.set_bias_level = ak4535_set_bias_level,
+ .suspend_bias_off = true,
+
+ .controls = ak4535_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4535_snd_controls),
.dapm_widgets = ak4535_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
.dapm_routes = ak4535_audio_map,
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index 7afe8f482088..70861c7b1631 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -505,39 +505,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
},
};
-static int ak4641_suspend(struct snd_soc_codec *codec)
-{
- ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
-static int ak4641_resume(struct snd_soc_codec *codec)
-{
- ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- return 0;
-}
-
-static int ak4641_probe(struct snd_soc_codec *codec)
-{
- /* power on device */
- ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-
-static int ak4641_remove(struct snd_soc_codec *codec)
-{
- ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- return 0;
-}
-
-
static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
- .probe = ak4641_probe,
- .remove = ak4641_remove,
- .suspend = ak4641_suspend,
- .resume = ak4641_resume,
.controls = ak4641_snd_controls,
.num_controls = ARRAY_SIZE(ak4641_snd_controls),
.dapm_widgets = ak4641_dapm_widgets,
@@ -545,6 +513,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
.dapm_routes = ak4641_audio_map,
.num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
.set_bias_level = ak4641_set_bias_level,
+ .suspend_bias_off = true,
};
static const struct regmap_config ak4641_regmap = {
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 041712592e29..dde8b49c19ad 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -491,23 +491,7 @@ static int ak4642_resume(struct snd_soc_codec *codec)
return 0;
}
-
-static int ak4642_probe(struct snd_soc_codec *codec)
-{
- ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-
-static int ak4642_remove(struct snd_soc_codec *codec)
-{
- ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
- .probe = ak4642_probe,
- .remove = ak4642_remove,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
.controls = ak4642_snd_controls,
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index 998fa0c5a0b9..686cacb0e835 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -611,20 +611,7 @@ static struct snd_soc_dai_driver ak4671_dai = {
.ops = &ak4671_dai_ops,
};
-static int ak4671_probe(struct snd_soc_codec *codec)
-{
- return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
-static int ak4671_remove(struct snd_soc_codec *codec)
-{
- ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
- .probe = ak4671_probe,
- .remove = ak4671_remove,
.set_bias_level = ak4671_set_bias_level,
.controls = ak4671_snd_controls,
.num_controls = ARRAY_SIZE(ak4671_snd_controls),
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index 9d0755aa1d16..bdf8c5ac8ca4 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -866,7 +866,6 @@ static int alc5623_suspend(struct snd_soc_codec *codec)
{
struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
- alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
regcache_cache_only(alc5623->regmap, true);
return 0;
@@ -887,15 +886,6 @@ static int alc5623_resume(struct snd_soc_codec *codec)
return ret;
}
- alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- /* charge alc5623 caps */
- if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
- alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- codec->dapm.bias_level = SND_SOC_BIAS_ON;
- alc5623_set_bias_level(codec, codec->dapm.bias_level);
- }
-
return 0;
}
@@ -906,9 +896,6 @@ static int alc5623_probe(struct snd_soc_codec *codec)
alc5623_reset(codec);
- /* power on device */
- alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
if (alc5623->add_ctrl) {
snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
alc5623->add_ctrl);
@@ -964,19 +951,12 @@ static int alc5623_probe(struct snd_soc_codec *codec)
return 0;
}
-/* power down chip */
-static int alc5623_remove(struct snd_soc_codec *codec)
-{
- alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
.probe = alc5623_probe,
- .remove = alc5623_remove,
.suspend = alc5623_suspend,
.resume = alc5623_resume,
.set_bias_level = alc5623_set_bias_level,
+ .suspend_bias_off = true,
};
static const struct regmap_config alc5623_regmap = {
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 85942ca36cbf..d1fdbc266631 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1038,23 +1038,15 @@ static struct snd_soc_dai_driver alc5632_dai = {
};
#ifdef CONFIG_PM
-static int alc5632_suspend(struct snd_soc_codec *codec)
-{
- alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static int alc5632_resume(struct snd_soc_codec *codec)
{
struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
regcache_sync(alc5632->regmap);
- alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0;
}
#else
-#define alc5632_suspend NULL
#define alc5632_resume NULL
#endif
@@ -1062,9 +1054,6 @@ static int alc5632_probe(struct snd_soc_codec *codec)
{
struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
- /* power on device */
- alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
switch (alc5632->id) {
case 0x5c:
snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
@@ -1077,19 +1066,12 @@ static int alc5632_probe(struct snd_soc_codec *codec)
return 0;
}
-/* power down chip */
-static int alc5632_remove(struct snd_soc_codec *codec)
-{
- alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_device_alc5632 = {
.probe = alc5632_probe,
- .remove = alc5632_remove,
- .suspend = alc5632_suspend,
.resume = alc5632_resume,
.set_bias_level = alc5632_set_bias_level,
+ .suspend_bias_off = true,
+
.controls = alc5632_snd_controls,
.num_controls = ARRAY_SIZE(alc5632_snd_controls),
.dapm_widgets = alc5632_dapm_widgets,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 0c05e7a7945f..9550d7433ad0 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -61,6 +61,11 @@
#define ARIZONA_FLL_MIN_OUTDIV 2
#define ARIZONA_FLL_MAX_OUTDIV 7
+#define ARIZONA_FMT_DSP_MODE_A 0
+#define ARIZONA_FMT_DSP_MODE_B 1
+#define ARIZONA_FMT_I2S_MODE 2
+#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3
+
#define arizona_fll_err(_fll, fmt, ...) \
dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define arizona_fll_warn(_fll, fmt, ...) \
@@ -648,7 +653,7 @@ SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
static const char * const arizona_in_dmic_osr_text[] = {
- "1.536MHz", "3.072MHz", "6.144MHz",
+ "1.536MHz", "3.072MHz", "6.144MHz", "768kHz",
};
const struct soc_enum arizona_in_dmic_osr[] = {
@@ -946,10 +951,26 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_DSP_A:
- mode = 0;
+ mode = ARIZONA_FMT_DSP_MODE_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ != SND_SOC_DAIFMT_CBM_CFM) {
+ arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = ARIZONA_FMT_DSP_MODE_B;
break;
case SND_SOC_DAIFMT_I2S:
- mode = 2;
+ mode = ARIZONA_FMT_I2S_MODE;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+ != SND_SOC_DAIFMT_CBM_CFM) {
+ arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
+ return -EINVAL;
+ }
+ mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE;
break;
default:
arizona_aif_err(dai, "Unsupported DAI format %d\n",
@@ -1164,13 +1185,13 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
{ 0x80, 0x0 },
};
- mutex_lock(&codec->mutex);
+ mutex_lock(&arizona->dac_comp_lock);
dac_comp[1].def = arizona->dac_comp_coeff;
if (rate >= 176400)
dac_comp[2].def = arizona->dac_comp_enabled;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&arizona->dac_comp_lock);
regmap_multi_reg_write(arizona->regmap,
dac_comp,
@@ -1298,7 +1319,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
/* Force multiple of 2 channels for I2S mode */
val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
- if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) {
+ val &= ARIZONA_AIF1_FMT_MASK;
+ if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
arizona_aif_dbg(dai, "Forcing stereo mode\n");
bclk_target /= channels;
bclk_target *= channels + 1;
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index 537327c7f7f1..8d638e8aa8eb 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -62,14 +62,10 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
static int cq93vc_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = codec_dai->codec;
- struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
switch (freq) {
case 22579200:
case 27000000:
case 33868800:
- davinci_vc->cq93vc.sysclk = freq;
return 0;
}
@@ -126,32 +122,6 @@ static struct snd_soc_dai_driver cq93vc_dai = {
.ops = &cq93vc_dai_ops,
};
-static int cq93vc_resume(struct snd_soc_codec *codec)
-{
- cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-
-static int cq93vc_probe(struct snd_soc_codec *codec)
-{
- struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
- davinci_vc->cq93vc.codec = codec;
-
- /* Off, with power on */
- cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-
-static int cq93vc_remove(struct snd_soc_codec *codec)
-{
- cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- return 0;
-}
-
static struct regmap *cq93vc_get_regmap(struct device *dev)
{
struct davinci_vc *davinci_vc = dev->platform_data;
@@ -161,9 +131,6 @@ static struct regmap *cq93vc_get_regmap(struct device *dev)
static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
.set_bias_level = cq93vc_set_bias_level,
- .probe = cq93vc_probe,
- .remove = cq93vc_remove,
- .resume = cq93vc_resume,
.get_regmap = cq93vc_get_regmap,
.controls = cq93vc_snd_controls,
.num_controls = ARRAY_SIZE(cq93vc_snd_controls),
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 4fdd47d700e3..ce6086835ebd 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -32,7 +32,6 @@
#include "cs4265.h"
struct cs4265_private {
- struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
u8 format;
@@ -598,7 +597,6 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
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)) {
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
new file mode 100644
index 000000000000..b264da030340
--- /dev/null
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -0,0 +1,62 @@
+/*
+ * CS4271 I2C audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap_config config;
+
+ config = cs4271_regmap_config;
+ config.reg_bits = 8;
+ config.val_bits = 8;
+
+ return cs4271_probe(&client->dev,
+ devm_regmap_init_i2c(client, &config));
+}
+
+static int cs4271_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id cs4271_i2c_id[] = {
+ { "cs4271", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static struct i2c_driver cs4271_i2c_driver = {
+ .driver = {
+ .name = "cs4271",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cs4271_dt_ids),
+ },
+ .probe = cs4271_i2c_probe,
+ .remove = cs4271_i2c_remove,
+ .id_table = cs4271_i2c_id,
+};
+module_i2c_driver(cs4271_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 I2C Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
new file mode 100644
index 000000000000..acd49d86e706
--- /dev/null
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -0,0 +1,55 @@
+/*
+ * CS4271 SPI audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * 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/spi/spi.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_spi_probe(struct spi_device *spi)
+{
+ struct regmap_config config;
+
+ config = cs4271_regmap_config;
+ config.reg_bits = 16;
+ config.val_bits = 8;
+ config.read_flag_mask = 0x21;
+ config.write_flag_mask = 0x20;
+
+ return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int cs4271_spi_remove(struct spi_device *spi)
+{
+ snd_soc_unregister_codec(&spi->dev);
+ return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+ .driver = {
+ .name = "cs4271",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cs4271_dt_ids),
+ },
+ .probe = cs4271_spi_probe,
+ .remove = cs4271_spi_remove,
+};
+module_spi_driver(cs4271_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 SPI Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 93cec52f4733..79a4efcb894c 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -23,8 +23,6 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_gpio.h>
@@ -32,6 +30,7 @@
#include <sound/soc.h>
#include <sound/tlv.h>
#include <sound/cs4271.h>
+#include "cs4271.h"
#define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
@@ -527,14 +526,15 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)
#endif /* CONFIG_PM */
#ifdef CONFIG_OF
-static const struct of_device_id cs4271_dt_ids[] = {
+const struct of_device_id cs4271_dt_ids[] = {
{ .compatible = "cirrus,cs4271", },
{ }
};
MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+EXPORT_SYMBOL_GPL(cs4271_dt_ids);
#endif
-static int cs4271_probe(struct snd_soc_codec *codec)
+static int cs4271_codec_probe(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
@@ -587,7 +587,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
return 0;
}
-static int cs4271_remove(struct snd_soc_codec *codec)
+static int cs4271_codec_remove(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
@@ -599,8 +599,8 @@ static int cs4271_remove(struct snd_soc_codec *codec)
};
static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
- .probe = cs4271_probe,
- .remove = cs4271_remove,
+ .probe = cs4271_codec_probe,
+ .remove = cs4271_codec_remove,
.suspend = cs4271_soc_suspend,
.resume = cs4271_soc_resume,
@@ -642,14 +642,8 @@ static int cs4271_common_probe(struct device *dev,
return 0;
}
-#if defined(CONFIG_SPI_MASTER)
-
-static const struct regmap_config cs4271_spi_regmap = {
- .reg_bits = 16,
- .val_bits = 8,
+const struct regmap_config cs4271_regmap_config = {
.max_register = CS4271_LASTREG,
- .read_flag_mask = 0x21,
- .write_flag_mask = 0x20,
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
@@ -657,140 +651,27 @@ static const struct regmap_config cs4271_spi_regmap = {
.volatile_reg = cs4271_volatile_reg,
};
+EXPORT_SYMBOL_GPL(cs4271_regmap_config);
-static int cs4271_spi_probe(struct spi_device *spi)
+int cs4271_probe(struct device *dev, struct regmap *regmap)
{
struct cs4271_private *cs4271;
int ret;
- ret = cs4271_common_probe(&spi->dev, &cs4271);
- if (ret < 0)
- return ret;
-
- spi_set_drvdata(spi, cs4271);
- cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap);
- if (IS_ERR(cs4271->regmap))
- return PTR_ERR(cs4271->regmap);
-
- return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
- &cs4271_dai, 1);
-}
-
-static int cs4271_spi_remove(struct spi_device *spi)
-{
- snd_soc_unregister_codec(&spi->dev);
- return 0;
-}
-
-static struct spi_driver cs4271_spi_driver = {
- .driver = {
- .name = "cs4271",
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(cs4271_dt_ids),
- },
- .probe = cs4271_spi_probe,
- .remove = cs4271_spi_remove,
-};
-#endif /* defined(CONFIG_SPI_MASTER) */
-
-#if IS_ENABLED(CONFIG_I2C)
-static const struct i2c_device_id cs4271_i2c_id[] = {
- {"cs4271", 0},
- {}
-};
-MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
-static const struct regmap_config cs4271_i2c_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = CS4271_LASTREG,
-
- .reg_defaults = cs4271_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
- .volatile_reg = cs4271_volatile_reg,
-};
-
-static int cs4271_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct cs4271_private *cs4271;
- int ret;
-
- ret = cs4271_common_probe(&client->dev, &cs4271);
+ ret = cs4271_common_probe(dev, &cs4271);
if (ret < 0)
return ret;
- i2c_set_clientdata(client, cs4271);
- cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap);
- if (IS_ERR(cs4271->regmap))
- return PTR_ERR(cs4271->regmap);
+ dev_set_drvdata(dev, cs4271);
+ cs4271->regmap = regmap;
- return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
- &cs4271_dai, 1);
-}
-
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
- snd_soc_unregister_codec(&client->dev);
- return 0;
-}
-
-static struct i2c_driver cs4271_i2c_driver = {
- .driver = {
- .name = "cs4271",
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(cs4271_dt_ids),
- },
- .id_table = cs4271_i2c_id,
- .probe = cs4271_i2c_probe,
- .remove = cs4271_i2c_remove,
-};
-#endif /* IS_ENABLED(CONFIG_I2C) */
-
-/*
- * We only register our serial bus driver here without
- * assignment to particular chip. So if any of the below
- * fails, there is some problem with I2C or SPI subsystem.
- * In most cases this module will be compiled with support
- * of only one serial bus.
- */
-static int __init cs4271_modinit(void)
-{
- int ret;
-
-#if IS_ENABLED(CONFIG_I2C)
- ret = i2c_add_driver(&cs4271_i2c_driver);
- if (ret) {
- pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
- return ret;
- }
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
- ret = spi_register_driver(&cs4271_spi_driver);
- if (ret) {
- pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
- return ret;
- }
-#endif
-
- return 0;
-}
-module_init(cs4271_modinit);
-
-static void __exit cs4271_modexit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
- spi_unregister_driver(&cs4271_spi_driver);
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
- i2c_del_driver(&cs4271_i2c_driver);
-#endif
+ return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
+ 1);
}
-module_exit(cs4271_modexit);
+EXPORT_SYMBOL_GPL(cs4271_probe);
MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
new file mode 100644
index 000000000000..9adad8eefdc9
--- /dev/null
+++ b/sound/soc/codecs/cs4271.h
@@ -0,0 +1,11 @@
+#ifndef _CS4271_PRIV_H
+#define _CS4271_PRIV_H
+
+#include <linux/regmap.h>
+
+extern const struct of_device_id cs4271_dt_ids[];
+extern const struct regmap_config cs4271_regmap_config;
+
+int cs4271_probe(struct device *dev, struct regmap *regmap);
+
+#endif
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index cee51ae177c1..c40428f25ba5 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -46,6 +46,7 @@ static struct i2c_driver cs42l51_i2c_driver = {
.driver = {
.name = "cs42l51",
.owner = THIS_MODULE,
+ .of_match_table = cs42l51_of_match,
},
.probe = cs42l51_i2c_probe,
.remove = cs42l51_i2c_remove,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 09488d97de60..b3951524339f 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -153,15 +153,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
static int cs42l51_pdn_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
switch (event) {
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+ snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN,
CS42L51_POWER_CTL1_PDN);
break;
default:
case SND_SOC_DAPM_POST_PMD:
- snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+ snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
CS42L51_POWER_CTL1_PDN, 0);
break;
}
@@ -558,11 +560,13 @@ error:
}
EXPORT_SYMBOL_GPL(cs42l51_probe);
-static const struct of_device_id cs42l51_of_match[] = {
+const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }
};
MODULE_DEVICE_TABLE(of, cs42l51_of_match);
+EXPORT_SYMBOL_GPL(cs42l51_of_match);
+
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("Cirrus Logic CS42L51 ALSA SoC Codec Driver");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l51.h b/sound/soc/codecs/cs42l51.h
index 8c55bf384bc6..0ca805492ac4 100644
--- a/sound/soc/codecs/cs42l51.h
+++ b/sound/soc/codecs/cs42l51.h
@@ -22,6 +22,7 @@ struct device;
extern const struct regmap_config cs42l51_regmap;
int cs42l51_probe(struct device *dev, struct regmap *regmap);
+extern const struct of_device_id cs42l51_of_match[];
#define CS42L51_CHIP_ID 0x1B
#define CS42L51_CHIP_REV_A 0x00
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 2f8b94683e83..7c55537c69cf 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -584,7 +584,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
@@ -600,7 +600,7 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
@@ -618,7 +618,7 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMD:
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index aae410d122ee..2d05b5d3a6ce 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -19,7 +19,7 @@
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "everest,es8328", 0 },
+ { "es8328", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
index 1087fd5f9917..1391ad50f95d 100644
--- a/sound/soc/codecs/hdmi.c
+++ b/sound/soc/codecs/hdmi.c
@@ -47,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 24,
},
.capture = {
.stream_name = "Capture",
@@ -75,6 +76,7 @@ static struct snd_soc_codec_driver hdmi_codec = {
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
.dapm_routes = hdmi_routes,
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+ .ignore_pmdown_time = true,
};
static int hdmi_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index c1ae5764983f..c4dfde9bdf1c 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1395,15 +1395,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
},
};
-/* power down chip */
-static int lm49453_remove(struct snd_soc_codec *codec)
-{
- lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return 0;
-}
-
static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
- .remove = lm49453_remove,
.set_bias_level = lm49453_set_bias_level,
.controls = lm49453_snd_controls,
.num_controls = ARRAY_SIZE(lm49453_snd_controls),
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 2cd3e5427441..805b3f8cd39d 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -875,7 +875,7 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
static int max98088_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
switch (event) {
@@ -905,7 +905,7 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
static int max98088_line_pga(struct snd_soc_dapm_widget *w,
int event, int line, u8 channel)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
u8 *state;
@@ -1887,25 +1887,6 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
max98088_handle_eq_pdata(codec);
}
-#ifdef CONFIG_PM
-static int max98088_suspend(struct snd_soc_codec *codec)
-{
- max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- return 0;
-}
-
-static int max98088_resume(struct snd_soc_codec *codec)
-{
- max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-#else
-#define max98088_suspend NULL
-#define max98088_resume NULL
-#endif
-
static int max98088_probe(struct snd_soc_codec *codec)
{
struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
@@ -1946,9 +1927,6 @@ static int max98088_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
- /* initialize registers cache to hardware default */
- max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
snd_soc_write(codec, M98088_REG_22_MIX_DAC,
@@ -1974,7 +1952,6 @@ static int max98088_remove(struct snd_soc_codec *codec)
{
struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
- max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
kfree(max98088->eq_texts);
return 0;
@@ -1983,9 +1960,9 @@ static int max98088_remove(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
.probe = max98088_probe,
.remove = max98088_remove,
- .suspend = max98088_suspend,
- .resume = max98088_resume,
.set_bias_level = max98088_set_bias_level,
+ .suspend_bias_off = true,
+
.controls = max98088_snd_controls,
.num_controls = ARRAY_SIZE(max98088_snd_controls),
.dapm_widgets = max98088_dapm_widgets,
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index d519294f57c7..151f718241ea 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -806,7 +806,7 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
unsigned int val = snd_soc_read(codec, w->reg);
@@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"MIC1 Input", NULL, "MIC1"},
{"MIC2 Input", NULL, "MIC2"},
+ {"DMICL", NULL, "DMICL_ENA"},
+ {"DMICL", NULL, "DMICR_ENA"},
+ {"DMICR", NULL, "DMICL_ENA"},
+ {"DMICR", NULL, "DMICR_ENA"},
{"DMICL", NULL, "AHPF"},
{"DMICR", NULL, "AHPF"},
@@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"DMIC Mux", "ADC", "ADCR"},
{"DMIC Mux", "DMIC", "DMICL"},
{"DMIC Mux", "DMIC", "DMICR"},
- {"DMIC Mux", "DMIC", "DMICL_ENA"},
- {"DMIC Mux", "DMIC", "DMICR_ENA"},
{"LBENL Mux", "Normal", "DMIC Mux"},
{"LBENL Mux", "Loopback", "LTENL Mux"},
@@ -1395,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"STENL Mux", "Sidetone Left", "DMICL"},
{"STENR Mux", "Sidetone Right", "ADCR"},
{"STENR Mux", "Sidetone Right", "DMICR"},
- {"DACL", "NULL", "STENL Mux"},
- {"DACR", "NULL", "STENL Mux"},
+ {"DACL", NULL, "STENL Mux"},
+ {"DACR", NULL, "STENR Mux"},
{"AIFINL", NULL, "SHDN"},
{"AIFINR", NULL, "SHDN"},
@@ -1826,27 +1828,155 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-static const int comp_pclk_rates[] = {
- 11289600, 12288000, 12000000, 13000000, 19200000
-};
-
-static const int dmic_micclk[] = {
- 2, 2, 2, 2, 4, 2
-};
+static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 };
static const int comp_lrclk_rates[] = {
8000, 16000, 32000, 44100, 48000, 96000
};
-static const int dmic_comp[6][6] = {
- {7, 8, 3, 3, 3, 3},
- {7, 8, 3, 3, 3, 3},
- {7, 8, 3, 3, 3, 3},
- {7, 8, 3, 1, 1, 1},
- {7, 8, 3, 1, 2, 2},
- {7, 8, 3, 3, 3, 3}
+struct dmic_table {
+ int pclk;
+ struct {
+ int freq;
+ int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */
+ } settings[6]; /* One for each dmic divisor. */
};
+static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
+ {
+ .pclk = 11289600,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ },
+ },
+ {
+ .pclk = 12000000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ }
+ },
+ {
+ .pclk = 12288000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+ }
+ },
+ {
+ .pclk = 13000000,
+ .settings = {
+ { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+ }
+ },
+ {
+ .pclk = 19200000,
+ .settings = {
+ { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } },
+ { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } },
+ { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } },
+ { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+ }
+ },
+};
+
+static int max98090_find_divisor(int target_freq, int pclk)
+{
+ int current_diff = INT_MAX;
+ int test_diff = INT_MAX;
+ int divisor_index = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) {
+ test_diff = abs(target_freq - (pclk / dmic_divisors[i]));
+ if (test_diff < current_diff) {
+ current_diff = test_diff;
+ divisor_index = i;
+ }
+ }
+
+ return divisor_index;
+}
+
+static int max98090_find_closest_pclk(int pclk)
+{
+ int m1;
+ int m2;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(dmic_table); i++) {
+ if (pclk == dmic_table[i].pclk)
+ return i;
+ if (pclk < dmic_table[i].pclk) {
+ if (i == 0)
+ return i;
+ m1 = pclk - dmic_table[i-1].pclk;
+ m2 = dmic_table[i].pclk - pclk;
+ if (m1 < m2)
+ return i - 1;
+ else
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int max98090_configure_dmic(struct max98090_priv *max98090,
+ int target_dmic_clk, int pclk, int fs)
+{
+ int micclk_index;
+ int pclk_index;
+ int dmic_freq;
+ int dmic_comp;
+ int i;
+
+ pclk_index = max98090_find_closest_pclk(pclk);
+ if (pclk_index < 0)
+ return pclk_index;
+
+ micclk_index = max98090_find_divisor(target_dmic_clk, pclk);
+
+ for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+ if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2)
+ break;
+ }
+
+ dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
+ dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
+
+ regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
+ M98090_MICCLK_MASK,
+ micclk_index << M98090_MICCLK_SHIFT);
+
+ regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG,
+ M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
+ dmic_comp << M98090_DMIC_COMP_SHIFT |
+ dmic_freq << M98090_DMIC_FREQ_SHIFT);
+
+ return 0;
+}
+
static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -1854,7 +1984,6 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
struct max98090_cdata *cdata;
- int i, j;
cdata = &max98090->dai[0];
max98090->bclk = snd_soc_params_to_bclk(params);
@@ -1893,27 +2022,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
M98090_DHF_MASK, M98090_DHF_MASK);
- /* Check for supported PCLK to LRCLK ratios */
- for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) {
- if (comp_pclk_rates[j] == max98090->sysclk) {
- break;
- }
- }
-
- for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
- if (max98090->lrclk <= (comp_lrclk_rates[i] +
- comp_lrclk_rates[i + 1]) / 2) {
- break;
- }
- }
-
- snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE,
- M98090_MICCLK_MASK,
- dmic_micclk[j] << M98090_MICCLK_SHIFT);
-
- snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG,
- M98090_DMIC_COMP_MASK,
- dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT);
+ max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
+ max98090->lrclk);
return 0;
}
@@ -1941,15 +2051,18 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
* 0x02 (when master clk is 20MHz to 40MHz)..
* 0x03 (when master clk is 40MHz to 60MHz)..
*/
- if ((freq >= 10000000) && (freq < 20000000)) {
+ if ((freq >= 10000000) && (freq <= 20000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV1);
- } else if ((freq >= 20000000) && (freq < 40000000)) {
+ max98090->pclk = freq;
+ } else if ((freq > 20000000) && (freq <= 40000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV2);
- } else if ((freq >= 40000000) && (freq < 60000000)) {
+ max98090->pclk = freq >> 1;
+ } else if ((freq > 40000000) && (freq <= 60000000)) {
snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
M98090_PSCLK_DIV4);
+ max98090->pclk = freq >> 2;
} else {
dev_err(codec->dev, "Invalid master clock frequency\n");
return -EINVAL;
@@ -2324,6 +2437,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
/* Initialize private data */
max98090->sysclk = (unsigned)-1;
+ max98090->pclk = (unsigned)-1;
max98090->master = false;
cdata = &max98090->dai[0];
@@ -2463,6 +2577,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, max98090);
max98090->pdata = i2c->dev.platform_data;
+ ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq",
+ &max98090->dmic_freq);
+ if (ret < 0)
+ max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ;
+
max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
if (IS_ERR(max98090->regmap)) {
ret = PTR_ERR(max98090->regmap);
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index a5f6bada06da..21ff743f5af2 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -12,6 +12,12 @@
#define _MAX98090_H
/*
+ * The default operating frequency for a DMIC attached to the codec.
+ * This can be overridden by a device tree property.
+ */
+#define MAX98090_DEFAULT_DMIC_FREQ 2500000
+
+/*
* MAX98090 Register Definitions
*/
@@ -1518,8 +1524,10 @@ struct max98090_priv {
struct max98090_pdata *pdata;
struct clk *mclk;
unsigned int sysclk;
+ unsigned int pclk;
unsigned int bclk;
unsigned int lrclk;
+ u32 dmic_freq;
struct max98090_cdata dai[1];
int jack_state;
struct delayed_work jack_work;
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 0ee6797d5083..8fba0c3db798 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -16,6 +16,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/clk.h>
+#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -57,6 +58,7 @@ struct max98095_priv {
unsigned int mic2pre;
struct snd_soc_jack *headphone_jack;
struct snd_soc_jack *mic_jack;
+ struct mutex lock;
};
static const struct reg_default max98095_reg_def[] = {
@@ -864,7 +866,7 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
static int max98095_mic_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
switch (event) {
@@ -894,7 +896,7 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
static int max98095_line_pga(struct snd_soc_dapm_widget *w,
int event, u8 channel)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
u8 *state;
@@ -942,7 +944,7 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
- struct snd_soc_codec *codec = w->codec;
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
@@ -1803,7 +1805,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
- mutex_lock(&codec->mutex);
+ mutex_lock(&max98095->lock);
snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
m98095_eq_band(codec, channel, 0, coef_set->band1);
m98095_eq_band(codec, channel, 1, coef_set->band2);
@@ -1811,7 +1813,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
m98095_eq_band(codec, channel, 3, coef_set->band4);
m98095_eq_band(codec, channel, 4, coef_set->band5);
snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&max98095->lock);
/* Restore the original on/off state */
snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -1957,12 +1959,12 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
- mutex_lock(&codec->mutex);
+ mutex_lock(&max98095->lock);
snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
m98095_biquad_band(codec, channel, 0, coef_set->band1);
m98095_biquad_band(codec, channel, 1, coef_set->band2);
snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&max98095->lock);
/* Restore the original on/off state */
snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -2317,9 +2319,6 @@ static int max98095_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
- /* initialize registers cache to hardware default */
- max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
snd_soc_write(codec, M98095_048_MIX_DAC_LR,
M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
@@ -2359,8 +2358,6 @@ static int max98095_remove(struct snd_soc_codec *codec)
struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
struct i2c_client *client = to_i2c_client(codec->dev);
- max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
if (max98095->headphone_jack || max98095->mic_jack)
max98095_jack_detect_disable(codec);
@@ -2395,6 +2392,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
if (max98095 == NULL)
return -ENOMEM;
+ mutex_init(&max98095->lock);
+
max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap);
if (IS_ERR(max98095->regmap)) {
ret = PTR_ERR(max98095->regmap);
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index 4fdf5aaa236f..10f8e47ce2c2 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -291,25 +291,6 @@ static struct snd_soc_dai_driver max9850_dai = {
.ops = &max9850_dai_ops,
};
-#ifdef CONFIG_PM
-static int max9850_suspend(struct snd_soc_codec *codec)
-{
- max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
- return 0;
-}
-
-static int max9850_resume(struct snd_soc_codec *codec)
-{
- max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
- return 0;
-}
-#else
-#define max9850_suspend NULL
-#define max9850_resume NULL
-#endif
-
static int max9850_probe(struct snd_soc_codec *codec)
{
/* enable zero-detect */
@@ -324,9 +305,8 @@ static int max9850_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
.probe = max9850_probe,
- .suspend = max9850_suspend,
- .resume = max9850_resume,
.set_bias_level = max9850_set_bias_level,
+ .suspend_bias_off = true,
.controls = max9850_controls,
.num_controls = ARRAY_SIZE(max9850_controls),
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 3fb83bf09768..d16331e0b64d 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -139,6 +139,7 @@ static const struct reg_default rt5645_reg[] = {
{ 0x76, 0x000a },
{ 0x77, 0x0c00 },
{ 0x78, 0x0000 },
+ { 0x79, 0x0123 },
{ 0x80, 0x0000 },
{ 0x81, 0x0000 },
{ 0x82, 0x0000 },
@@ -334,6 +335,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
case RT5645_DMIC_CTRL2:
case RT5645_TDM_CTRL_1:
case RT5645_TDM_CTRL_2:
+ case RT5645_TDM_CTRL_3:
case RT5645_GLB_CLK:
case RT5645_PLL_CTRL1:
case RT5645_PLL_CTRL2:
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index ba9d9b4d4857..9bd8b4f63303 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -100,18 +100,18 @@ static const struct reg_default rt5670_reg[] = {
{ 0x4c, 0x5380 },
{ 0x4f, 0x0073 },
{ 0x52, 0x00d3 },
- { 0x53, 0xf0f0 },
+ { 0x53, 0xf000 },
{ 0x61, 0x0000 },
{ 0x62, 0x0001 },
{ 0x63, 0x00c3 },
{ 0x64, 0x0000 },
- { 0x65, 0x0000 },
+ { 0x65, 0x0001 },
{ 0x66, 0x0000 },
{ 0x6f, 0x8000 },
{ 0x70, 0x8000 },
{ 0x71, 0x8000 },
{ 0x72, 0x8000 },
- { 0x73, 0x1110 },
+ { 0x73, 0x7770 },
{ 0x74, 0x0e00 },
{ 0x75, 0x1505 },
{ 0x76, 0x0015 },
@@ -125,21 +125,21 @@ static const struct reg_default rt5670_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x86, 0x0008 },
+ { 0x86, 0x0004 },
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
{ 0x8a, 0x0000 },
{ 0x8b, 0x0000 },
- { 0x8c, 0x0007 },
+ { 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
{ 0x91, 0x0c06 },
{ 0x93, 0x0000 },
- { 0x94, 0x0000 },
- { 0x95, 0x0000 },
+ { 0x94, 0x1270 },
+ { 0x95, 0x1000 },
{ 0x97, 0x0000 },
{ 0x98, 0x0000 },
{ 0x99, 0x0000 },
@@ -150,11 +150,11 @@ static const struct reg_default rt5670_reg[] = {
{ 0x9e, 0x0400 },
{ 0xae, 0x7000 },
{ 0xaf, 0x0000 },
- { 0xb0, 0x6000 },
+ { 0xb0, 0x7000 },
{ 0xb1, 0x0000 },
{ 0xb2, 0x0000 },
{ 0xb3, 0x001f },
- { 0xb4, 0x2206 },
+ { 0xb4, 0x220c },
{ 0xb5, 0x1f00 },
{ 0xb6, 0x0000 },
{ 0xb7, 0x0000 },
@@ -171,25 +171,25 @@ static const struct reg_default rt5670_reg[] = {
{ 0xcf, 0x1813 },
{ 0xd0, 0x0690 },
{ 0xd1, 0x1c17 },
- { 0xd3, 0xb320 },
+ { 0xd3, 0xa220 },
{ 0xd4, 0x0000 },
{ 0xd6, 0x0400 },
{ 0xd9, 0x0809 },
{ 0xda, 0x0000 },
{ 0xdb, 0x0001 },
{ 0xdc, 0x0049 },
- { 0xdd, 0x0009 },
+ { 0xdd, 0x0024 },
{ 0xe6, 0x8000 },
{ 0xe7, 0x0000 },
- { 0xec, 0xb300 },
+ { 0xec, 0xa200 },
{ 0xed, 0x0000 },
- { 0xee, 0xb300 },
+ { 0xee, 0xa200 },
{ 0xef, 0x0000 },
{ 0xf8, 0x0000 },
{ 0xf9, 0x0000 },
{ 0xfa, 0x8010 },
{ 0xfb, 0x0033 },
- { 0xfc, 0x0080 },
+ { 0xfc, 0x0100 },
};
static bool rt5670_volatile_register(struct device *dev, unsigned int reg)
@@ -1877,6 +1877,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "DAC1 MIXR", "DAC1 Switch", "DAC1 R Mux" },
{ "DAC1 MIXR", NULL, "DAC Stereo1 Filter" },
+ { "DAC Stereo1 Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Left Filter", NULL, "PLL1", is_sys_clk_from_pll },
+ { "DAC Mono Right Filter", NULL, "PLL1", is_sys_clk_from_pll },
+
{ "DAC MIX", NULL, "DAC1 MIXL" },
{ "DAC MIX", NULL, "DAC1 MIXR" },
@@ -1926,14 +1930,10 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
{ "DAC L1", NULL, "DAC L1 Power" },
{ "DAC L1", NULL, "Stereo DAC MIXL" },
- { "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC R1", NULL, "DAC R1 Power" },
{ "DAC R1", NULL, "Stereo DAC MIXR" },
- { "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC L2", NULL, "Mono DAC MIXL" },
- { "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
{ "DAC R2", NULL, "Mono DAC MIXR" },
- { "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
{ "OUT MIXL", "BST1 Switch", "BST1" },
{ "OUT MIXL", "INL Switch", "INL VOL" },
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 6bb77d76561b..dab9b15304af 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1299,8 +1299,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
/* enable small pop, introduce 400ms delay in turning off */
snd_soc_update_bits(codec, SGTL5000_CHIP_REF_CTRL,
- SGTL5000_SMALL_POP,
- SGTL5000_SMALL_POP);
+ SGTL5000_SMALL_POP, 1);
/* disable short cut detector */
snd_soc_write(codec, SGTL5000_CHIP_SHORT_CTRL, 0);
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 2f8c88931f69..bd7a344bf8c5 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -275,7 +275,7 @@
#define SGTL5000_BIAS_CTRL_MASK 0x000e
#define SGTL5000_BIAS_CTRL_SHIFT 1
#define SGTL5000_BIAS_CTRL_WIDTH 3
-#define SGTL5000_SMALL_POP 0x0001
+#define SGTL5000_SMALL_POP 0
/*
* SGTL5000_CHIP_MIC_CTRL
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index f2de7e049bc6..81a38dd9af1f 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -159,6 +159,13 @@ int _process_sigma_firmware(struct device *dev,
goto done;
}
+ if (ssfw_head->version != 1) {
+ dev_err(dev,
+ "Failed to load firmware: Invalid version %d. Supported firmware versions: 1\n",
+ ssfw_head->version);
+ goto done;
+ }
+
crc = crc32(0, fw->data + sizeof(*ssfw_head),
fw->size - sizeof(*ssfw_head));
pr_debug("%s: crc=%x\n", __func__, crc);
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 53b810d23fea..f37a79ec45e6 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(ac97, reg, val);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(ac97, reg, val);
cache[reg / 2] = val;
return 0;
}
@@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 val = 0, *cache = codec->reg_cache;
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
+ val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
@@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
reg == AC97_VENDOR_ID2) {
- val = soc_ac97_ops->read(codec->ac97, reg);
+ val = soc_ac97_ops->read(ac97, reg);
return val;
}
return cache[reg / 2];
@@ -240,15 +242,17 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(ac97);
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(ac97);
if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(ac97);
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
return -EIO;
return 0;
@@ -262,6 +266,7 @@ static int stac9766_codec_suspend(struct snd_soc_codec *codec)
static int stac9766_codec_resume(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 id, reset;
reset = 0;
@@ -271,8 +276,8 @@ reset:
printk(KERN_ERR "stac9766 failed to resume");
return -EIO;
}
- codec->ac97->bus->ops->warm_reset(codec->ac97);
- id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
+ ac97->bus->ops->warm_reset(ac97);
+ id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
if (id != 0x4c13) {
stac9766_reset(codec, 0);
reset++;
@@ -294,7 +299,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
static struct snd_soc_dai_driver stac9766_dai[] = {
{
.name = "stac9766-hifi-analog",
- .ac97_control = 1,
/* stream cababilities */
.playback = {
@@ -316,7 +320,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
},
{
.name = "stac9766-hifi-IEC958",
- .ac97_control = 1,
/* stream cababilities */
.playback = {
@@ -334,11 +337,14 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
static int stac9766_codec_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0)
- goto codec_err;
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97))
+ return PTR_ERR(ac97);
+
+ snd_soc_codec_set_drvdata(codec, ac97);
/* do a cold reset for the controller and then try
* a warm reset followed by an optional cold reset for codec */
@@ -357,13 +363,15 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
return 0;
codec_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_free_ac97_codec(ac97);
return ret;
}
static int stac9766_codec_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(ac97);
return 0;
}
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 145fe5b253d4..93de5dd0a7b9 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -911,12 +911,13 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
}
aic31xx->p_div = i;
- for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) {
- if (i == ARRAY_SIZE(aic31xx_divs)) {
- dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
- __func__, freq);
- return -EINVAL;
- }
+ for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
+ aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
+ ;
+ if (i == ARRAY_SIZE(aic31xx_divs)) {
+ dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
+ __func__, freq);
+ return -EINVAL;
}
/* set clock on MCLK, BCLK, or GPIO1 as PLL input */
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index f60234962527..d78fb8dffc8c 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -619,10 +619,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
uint16_t data;
- mutex_lock(&codec->mutex);
+ mutex_lock(&arizona->dac_comp_lock);
data = cpu_to_be16(arizona->dac_comp_coeff);
memcpy(ucontrol->value.bytes.data, &data, sizeof(data));
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&arizona->dac_comp_lock);
return 0;
}
@@ -633,11 +633,11 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- mutex_lock(&codec->mutex);
+ mutex_lock(&arizona->dac_comp_lock);
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);
+ mutex_unlock(&arizona->dac_comp_lock);
return 0;
}
@@ -648,9 +648,9 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- mutex_lock(&codec->mutex);
+ mutex_lock(&arizona->dac_comp_lock);
ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&arizona->dac_comp_lock);
return 0;
}
@@ -661,9 +661,9 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- mutex_lock(&codec->mutex);
+ mutex_lock(&arizona->dac_comp_lock);
arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&arizona->dac_comp_lock);
return 0;
}
@@ -1900,6 +1900,8 @@ static int wm5102_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm5102);
+ mutex_init(&arizona->dac_comp_lock);
+
wm5102->core.arizona = arizona;
wm5102->core.num_inputs = 6;
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index eebb3280bfad..5dae9a6f8076 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -24,6 +24,7 @@
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/of_device.h>
+#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -50,6 +51,8 @@ struct wm8731_priv {
int sysclk_type;
int playback_fs;
bool deemph;
+
+ struct mutex lock;
};
@@ -138,7 +141,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
if (deemph > 1)
return -EINVAL;
- mutex_lock(&codec->mutex);
+ mutex_lock(&wm8731->lock);
if (wm8731->deemph != deemph) {
wm8731->deemph = deemph;
@@ -146,7 +149,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
ret = 1;
}
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&wm8731->lock);
return ret;
}
@@ -685,6 +688,8 @@ static int wm8731_spi_probe(struct spi_device *spi)
if (wm8731 == NULL)
return -ENOMEM;
+ mutex_init(&wm8731->lock);
+
wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
if (IS_ERR(wm8731->regmap)) {
ret = PTR_ERR(wm8731->regmap);
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index c038b3e04398..ffbe6df3453a 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -26,6 +26,7 @@
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/irq.h>
+#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -123,6 +124,7 @@ struct wm8903_priv {
int sysclk;
int irq;
+ struct mutex lock;
int fs;
int deemph;
@@ -457,7 +459,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
if (deemph > 1)
return -EINVAL;
- mutex_lock(&codec->mutex);
+ mutex_lock(&wm8903->lock);
if (wm8903->deemph != deemph) {
wm8903->deemph = deemph;
@@ -465,7 +467,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
ret = 1;
}
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&wm8903->lock);
return ret;
}
@@ -2023,6 +2025,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
GFP_KERNEL);
if (wm8903 == NULL)
return -ENOMEM;
+
+ mutex_init(&wm8903->lock);
wm8903->dev = &i2c->dev;
wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap);
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index 0dada7f0105e..3cbc82b33292 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -867,9 +867,9 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
- mutex_lock(&codec->mutex);
+ mutex_lock(&wm8994->fw_lock);
wm8994->enh_eq = fw;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&wm8994->fw_lock);
}
}
@@ -879,9 +879,9 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
- mutex_lock(&codec->mutex);
+ mutex_lock(&wm8994->fw_lock);
wm8994->mbc_vss = fw;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&wm8994->fw_lock);
}
}
@@ -891,9 +891,9 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) {
- mutex_lock(&codec->mutex);
+ mutex_lock(&wm8994->fw_lock);
wm8994->mbc = fw;
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&wm8994->fw_lock);
}
}
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 9077411e62ce..61ca4a7cb6ea 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -26,6 +26,7 @@
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
+#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -67,6 +68,7 @@ struct wm8962_priv {
int fll_fref;
int fll_fout;
+ struct mutex dsp2_ena_lock;
u16 dsp2_ena;
struct delayed_work mic_work;
@@ -1570,7 +1572,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
WM8962_DSP2_ENA;
- mutex_lock(&codec->mutex);
+ mutex_lock(&wm8962->dsp2_ena_lock);
if (ucontrol->value.integer.value[0])
wm8962->dsp2_ena |= 1 << shift;
@@ -1590,7 +1592,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
}
out:
- mutex_unlock(&codec->mutex);
+ mutex_unlock(&wm8962->dsp2_ena_lock);
return ret;
}
@@ -3557,6 +3559,8 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
if (wm8962 == NULL)
return -ENOMEM;
+ mutex_init(&wm8962->dsp2_ena_lock);
+
i2c_set_clientdata(i2c, wm8962);
INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 1fcb9f3f3097..dbca6e0cc93a 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4457,6 +4457,8 @@ static int wm8994_probe(struct platform_device *pdev)
return -ENOMEM;
platform_set_drvdata(pdev, wm8994);
+ mutex_init(&wm8994->fw_lock);
+
wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
pm_runtime_enable(&pdev->dev);
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h
index 6536f8d45ac6..dd73387b1cc4 100644
--- a/sound/soc/codecs/wm8994.h
+++ b/sound/soc/codecs/wm8994.h
@@ -13,6 +13,7 @@
#include <linux/firmware.h>
#include <linux/completion.h>
#include <linux/workqueue.h>
+#include <linux/mutex.h>
#include "wm_hubs.h"
@@ -156,6 +157,7 @@ struct wm8994_priv {
unsigned int aif1clk_disable:1;
unsigned int aif2clk_disable:1;
+ struct mutex fw_lock;
int dsp_active;
const struct firmware *cur_fw;
const struct firmware *mbc;
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index c0b7f45dfa37..d3a800fa6f06 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
/* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
switch (reg) {
case AC97_RESET:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops->read(codec->ac97, reg);
+ return soc_ac97_ops->read(ac97, reg);
default:
reg = reg >> 1;
@@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9705_reg)))
cache[reg] = val;
@@ -263,7 +265,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = {
static struct snd_soc_dai_driver wm9705_dai[] = {
{
.name = "wm9705-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
static int wm9705_reset(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
if (soc_ac97_ops->reset) {
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(ac97);
if (ac97_read(codec, 0) == wm9705_reg[0])
return 0; /* Success */
}
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
return -EIO;
}
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
{
- soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
return 0;
}
static int wm9705_soc_resume(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
ret = wm9705_reset(codec);
- if (ret < 0) {
- printk(KERN_ERR "could not reset AC97 codec\n");
+ if (ret < 0)
return ret;
- }
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
- soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(ac97, i, cache[i>>1]);
}
return 0;
@@ -335,31 +341,34 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
static int wm9705_soc_probe(struct snd_soc_codec *codec)
{
+ struct snd_ac97 *ac97;
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
+ ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(ac97)) {
+ ret = PTR_ERR(ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec\n");
return ret;
}
+ snd_soc_codec_set_drvdata(codec, ac97);
+
ret = wm9705_reset(codec);
if (ret)
goto reset_err;
- snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
- ARRAY_SIZE(wm9705_snd_ac97_controls));
-
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_free_ac97_codec(ac97);
return ret;
}
static int wm9705_soc_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(ac97);
return 0;
}
@@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9705_reg,
+
+ .controls = wm9705_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
.dapm_widgets = wm9705_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
.dapm_routes = wm9705_audio_map,
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index c5eb746087b4..52a211be5b47 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -23,6 +23,12 @@
#include <sound/tlv.h>
#include "wm9712.h"
+struct wm9712_priv {
+ struct snd_ac97 *ac97;
+ unsigned int hp_mixer[2];
+ struct mutex lock;
+};
+
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg);
static int ac97_write(struct snd_soc_codec *codec,
@@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = {
0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
- 0x0000, 0x0000 /* virtual hp mixers */
};
-/* virtual HP mixers regs */
-#define HPL_MIXER 0x80
-#define HPR_MIXER 0x82
+#define HPL_MIXER 0x0
+#define HPR_MIXER 0x1
static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
@@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
};
+static const unsigned int wm9712_mixer_mute_regs[] = {
+ AC97_VIDEO,
+ AC97_PCM,
+ AC97_LINE,
+ AC97_PHONE,
+ AC97_CD,
+ AC97_PC_BEEP,
+};
+
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
* This makes it impossible to determine the audio path.
*/
-static int mixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
+static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- u16 l, r, beep, line, phone, mic, pcm, aux;
-
- l = ac97_read(w->codec, HPL_MIXER);
- r = ac97_read(w->codec, HPR_MIXER);
- beep = ac97_read(w->codec, AC97_PC_BEEP);
- mic = ac97_read(w->codec, AC97_VIDEO);
- phone = ac97_read(w->codec, AC97_PHONE);
- line = ac97_read(w->codec, AC97_LINE);
- pcm = ac97_read(w->codec, AC97_PCM);
- aux = ac97_read(w->codec, AC97_CD);
-
- if (l & 0x1 || r & 0x1)
- ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, mask, shift, old;
+ struct snd_soc_dapm_update update;
+ bool change;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+ mask = 1 << shift;
+
+ mutex_lock(&wm9712->lock);
+ old = wm9712->hp_mixer[mixer];
+ if (ucontrol->value.enumerated.item[0])
+ wm9712->hp_mixer[mixer] |= mask;
else
- ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
+ wm9712->hp_mixer[mixer] &= ~mask;
+
+ change = old != wm9712->hp_mixer[mixer];
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = wm9712_mixer_mute_regs[shift];
+ update.mask = 0x8000;
+ if ((wm9712->hp_mixer[0] & mask) ||
+ (wm9712->hp_mixer[1] & mask))
+ update.val = 0x0;
+ else
+ update.val = 0x8000;
+
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+ &update);
+ }
- if (l & 0x2 || r & 0x2)
- ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
- else
- ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+ mutex_unlock(&wm9712->lock);
- if (l & 0x4 || r & 0x4)
- ac97_write(w->codec, AC97_LINE, line & 0x7fff);
- else
- ac97_write(w->codec, AC97_LINE, line | 0x8000);
+ return change;
+}
- if (l & 0x8 || r & 0x8)
- ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
- else
- ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int shift, mixer;
- if (l & 0x10 || r & 0x10)
- ac97_write(w->codec, AC97_CD, aux & 0x7fff);
- else
- ac97_write(w->codec, AC97_CD, aux | 0x8000);
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
- if (l & 0x20 || r & 0x20)
- ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
- else
- ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+ ucontrol->value.enumerated.item[0] =
+ (wm9712->hp_mixer[mixer] >> shift) & 1;
return 0;
}
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
+ .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
+ (xmixer << 8) | xshift, 1, 0, 0) \
+}
+
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
- SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
- SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
- SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
- SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
- SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
- SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
+ WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
+ WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
+ WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
+ WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
+ WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
+ WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
};
/* Right Headphone Mixers */
static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
- SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
- SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
- SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
- SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
- SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
- SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
+ WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
+ WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
+ WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
+ WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
+ WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
+ WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
};
/* Speaker Mixer */
@@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
&wm9712_diff_sel_controls),
SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
- &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
- &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+ &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+ &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
&wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_REC_GAIN)
- return soc_ac97_ops->read(codec->ac97, reg);
+ return soc_ac97_ops->read(wm9712->ac97, reg);
else {
reg = reg >> 1;
@@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
- if (reg < 0x7c)
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(wm9712->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
@@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
static struct snd_soc_dai_driver wm9712_dai[] = {
{
.name = "wm9712-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -581,21 +616,23 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(wm9712->ac97);
if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(wm9712->ac97);
if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(wm9712->ac97);
if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
err:
- printk(KERN_ERR "WM9712 AC97 reset failed\n");
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
return -EIO;
}
@@ -607,14 +644,13 @@ static int wm9712_soc_suspend(struct snd_soc_codec *codec)
static int wm9712_soc_resume(struct snd_soc_codec *codec)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
int i, ret;
u16 *cache = codec->reg_cache;
ret = wm9712_reset(codec, 1);
- if (ret < 0) {
- printk(KERN_ERR "could not reset AC97 codec\n");
+ if (ret < 0)
return ret;
- }
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -624,7 +660,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
(i > 0x58 && i != 0x5c))
continue;
- soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
}
}
@@ -633,37 +669,37 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0) {
- printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
+ wm9712->ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(wm9712->ac97)) {
+ ret = PTR_ERR(wm9712->ac97);
+ dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
return ret;
}
ret = wm9712_reset(codec, 0);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
+ if (ret < 0)
goto reset_err;
- }
/* set alc mux to none */
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
- snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
- ARRAY_SIZE(wm9712_snd_ac97_controls));
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
+ snd_soc_free_ac97_codec(wm9712->ac97);
return ret;
}
static int wm9712_soc_remove(struct snd_soc_codec *codec)
{
- snd_soc_free_ac97_codec(codec);
+ struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+ snd_soc_free_ac97_codec(wm9712->ac97);
return 0;
}
@@ -679,6 +715,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9712_reg,
+
+ .controls = wm9712_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
.dapm_widgets = wm9712_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
.dapm_routes = wm9712_audio_map,
@@ -687,6 +726,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
static int wm9712_probe(struct platform_device *pdev)
{
+ struct wm9712_priv *wm9712;
+
+ wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
+ if (wm9712 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm9712->lock);
+
+ platform_set_drvdata(pdev, wm9712);
+
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
}
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index bddee30a4bc7..6c95d98b0eb1 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -30,7 +30,10 @@
#include "wm9713.h"
struct wm9713_priv {
+ struct snd_ac97 *ac97;
u32 pll_in; /* PLL input frequency */
+ unsigned int hp_mixer[2];
+ struct mutex lock;
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
@@ -59,13 +62,10 @@ static const u16 wm9713_reg[] = {
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0006,
0x0001, 0x0000, 0x574d, 0x4c13,
- 0x0000, 0x0000, 0x0000
};
-/* virtual HP mixers regs */
-#define HPL_MIXER 0x80
-#define HPR_MIXER 0x82
-#define MICB_MUX 0x82
+#define HPL_MIXER 0
+#define HPR_MIXER 1
static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
@@ -110,7 +110,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
-SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
+SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
};
static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
@@ -234,6 +234,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
return 0;
}
+static const unsigned int wm9713_mixer_mute_regs[] = {
+ AC97_PC_BEEP,
+ AC97_MASTER_TONE,
+ AC97_PHONE,
+ AC97_REC_SEL,
+ AC97_PCM,
+ AC97_AUX,
+};
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
@@ -241,73 +249,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
* register map, thus we add a new (virtual) register to help determine the
* audio route within the device.
*/
-static int mixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
{
- u16 l, r, beep, tone, phone, rec, pcm, aux;
-
- l = ac97_read(w->codec, HPL_MIXER);
- r = ac97_read(w->codec, HPR_MIXER);
- beep = ac97_read(w->codec, AC97_PC_BEEP);
- tone = ac97_read(w->codec, AC97_MASTER_TONE);
- phone = ac97_read(w->codec, AC97_PHONE);
- rec = ac97_read(w->codec, AC97_REC_SEL);
- pcm = ac97_read(w->codec, AC97_PCM);
- aux = ac97_read(w->codec, AC97_AUX);
-
- if (event & SND_SOC_DAPM_PRE_REG)
- return 0;
- if ((l & 0x1) || (r & 0x1))
- ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = ucontrol->value.enumerated.item[0];
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, mask, shift, old;
+ struct snd_soc_dapm_update update;
+ bool change;
+
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
+ mask = (1 << shift);
+
+ mutex_lock(&wm9713->lock);
+ old = wm9713->hp_mixer[mixer];
+ if (ucontrol->value.enumerated.item[0])
+ wm9713->hp_mixer[mixer] |= mask;
else
- ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+ wm9713->hp_mixer[mixer] &= ~mask;
+
+ change = old != wm9713->hp_mixer[mixer];
+ if (change) {
+ update.kcontrol = kcontrol;
+ update.reg = wm9713_mixer_mute_regs[shift];
+ update.mask = 0x8000;
+ if ((wm9713->hp_mixer[0] & mask) ||
+ (wm9713->hp_mixer[1] & mask))
+ update.val = 0x0;
+ else
+ update.val = 0x8000;
+
+ snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+ &update);
+ }
- if ((l & 0x2) || (r & 0x2))
- ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
- else
- ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
+ mutex_unlock(&wm9713->lock);
- if ((l & 0x4) || (r & 0x4))
- ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
- else
- ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+ return change;
+}
- if ((l & 0x8) || (r & 0x8))
- ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
- else
- ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
+static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int mixer, shift;
- if ((l & 0x10) || (r & 0x10))
- ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
- else
- ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+ mixer = mc->shift >> 8;
+ shift = mc->shift & 0xff;
- if ((l & 0x20) || (r & 0x20))
- ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
- else
- ac97_write(w->codec, AC97_AUX, aux | 0x8000);
+ ucontrol->value.enumerated.item[0] =
+ (wm9713->hp_mixer[mixer] >> shift) & 1;
return 0;
}
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_volsw, \
+ .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
+ .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
+ xshift, xmixer, 1, 0, 0) \
+}
+
/* Left Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
};
/* Right Headphone Mixers */
static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
};
/* headphone capture mux */
@@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
&wm9713_mic_sel_mux_controls),
SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
&wm9713_micb_sel_mux_controls),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
- &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
- &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
- mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+ &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+ &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
&wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
@@ -647,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
u16 *cache = codec->reg_cache;
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_CD)
- return soc_ac97_ops->read(codec->ac97, reg);
+ return soc_ac97_ops->read(wm9713->ac97, reg);
else {
reg = reg >> 1;
@@ -666,9 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
u16 *cache = codec->reg_cache;
- if (reg < 0x7c)
- soc_ac97_ops->write(codec->ac97, reg, val);
+ soc_ac97_ops->write(wm9713->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
@@ -689,7 +719,8 @@ struct _pll_div {
* to allow rounding later */
#define FIXED_PLL_SIZE ((1 << 22) * 10)
-static void pll_factors(struct _pll_div *pll_div, unsigned int source)
+static void pll_factors(struct snd_soc_codec *codec,
+ struct _pll_div *pll_div, unsigned int source)
{
u64 Kpart;
unsigned int K, Ndiv, Nmod, target;
@@ -724,7 +755,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
Ndiv = target / source;
if ((Ndiv < 5) || (Ndiv > 12))
- printk(KERN_WARNING
+ dev_warn(codec->dev,
"WM9713 PLL N value %u out of recommended range!\n",
Ndiv);
@@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
return 0;
}
- pll_factors(&pll_div, freq_in);
+ pll_factors(codec, &pll_div, freq_in);
if (pll_div.k == 0) {
reg = (pll_div.n << 12) | (pll_div.lf << 11) |
@@ -1049,7 +1080,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
static struct snd_soc_dai_driver wm9713_dai[] = {
{
.name = "wm9713-hifi",
- .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
@@ -1095,17 +1125,22 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
if (try_warm && soc_ac97_ops->warm_reset) {
- soc_ac97_ops->warm_reset(codec->ac97);
+ soc_ac97_ops->warm_reset(wm9713->ac97);
if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
- soc_ac97_ops->reset(codec->ac97);
+ soc_ac97_ops->reset(wm9713->ac97);
if (soc_ac97_ops->warm_reset)
- soc_ac97_ops->warm_reset(codec->ac97);
- if (ac97_read(codec, 0) != wm9713_reg[0])
+ soc_ac97_ops->warm_reset(wm9713->ac97);
+ if (ac97_read(codec, 0) != wm9713_reg[0]) {
+ dev_err(codec->dev, "Failed to reset: AC97 link error\n");
return -EIO;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(wm9713_reset);
@@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
u16 *cache = codec->reg_cache;
ret = wm9713_reset(codec, 1);
- if (ret < 0) {
- printk(KERN_ERR "could not reset AC97 codec\n");
+ if (ret < 0)
return ret;
- }
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1180,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
continue;
- soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
}
}
@@ -1189,26 +1222,19 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
static int wm9713_soc_probe(struct snd_soc_codec *codec)
{
- struct wm9713_priv *wm9713;
+ struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
int ret = 0, reg;
- wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
- if (wm9713 == NULL)
- return -ENOMEM;
- snd_soc_codec_set_drvdata(codec, wm9713);
-
- ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
- if (ret < 0)
- goto codec_err;
+ wm9713->ac97 = snd_soc_new_ac97_codec(codec);
+ if (IS_ERR(wm9713->ac97))
+ return PTR_ERR(wm9713->ac97);
/* do a cold reset for the controller and then try
* a warm reset followed by an optional cold reset for codec */
wm9713_reset(codec, 0);
ret = wm9713_reset(codec, 1);
- if (ret < 0) {
- printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
+ if (ret < 0)
goto reset_err;
- }
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1216,23 +1242,18 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
reg = ac97_read(codec, AC97_CD) & 0x7fff;
ac97_write(codec, AC97_CD, reg);
- snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
- ARRAY_SIZE(wm9713_snd_ac97_controls));
-
return 0;
reset_err:
- snd_soc_free_ac97_codec(codec);
-codec_err:
- kfree(wm9713);
+ snd_soc_free_ac97_codec(wm9713->ac97);
return ret;
}
static int wm9713_soc_remove(struct snd_soc_codec *codec)
{
struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
- snd_soc_free_ac97_codec(codec);
- kfree(wm9713);
+
+ snd_soc_free_ac97_codec(wm9713->ac97);
return 0;
}
@@ -1248,6 +1269,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
.reg_word_size = sizeof(u16),
.reg_cache_step = 2,
.reg_cache_default = wm9713_reg,
+
+ .controls = wm9713_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
.dapm_widgets = wm9713_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
.dapm_routes = wm9713_audio_map,
@@ -1256,6 +1280,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
static int wm9713_probe(struct platform_device *pdev)
{
+ struct wm9713_priv *wm9713;
+
+ wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
+ if (wm9713 == NULL)
+ return -ENOMEM;
+
+ mutex_init(&wm9713->lock);
+
+ platform_set_drvdata(pdev, wm9713);
+
return snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
}
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index f412a9911a75..720d6e852986 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/vmalloc.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -169,11 +170,12 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
if (buf == NULL)
return NULL;
- buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+ buf->buf = vmalloc(len);
if (!buf->buf) {
- kfree(buf);
+ vfree(buf);
return NULL;
}
+ memcpy(buf->buf, src, len);
if (list)
list_add_tail(&buf->list, list);
@@ -188,7 +190,7 @@ static void wm_adsp_buf_free(struct list_head *list)
struct wm_adsp_buf,
list);
list_del(&buf->list);
- kfree(buf->buf);
+ vfree(buf->buf);
kfree(buf);
}
}
@@ -684,38 +686,24 @@ static int wm_adsp_load(struct wm_adsp *dsp)
}
if (reg) {
- size_t to_write = PAGE_SIZE;
- size_t remain = le32_to_cpu(region->len);
- const u8 *data = region->data;
-
- while (remain > 0) {
- if (remain < PAGE_SIZE)
- to_write = remain;
-
- buf = wm_adsp_buf_alloc(data,
- to_write,
- &buf_list);
- if (!buf) {
- adsp_err(dsp, "Out of memory\n");
- ret = -ENOMEM;
- goto out_fw;
- }
-
- ret = regmap_raw_write_async(regmap, reg,
- buf->buf,
- to_write);
- if (ret != 0) {
- adsp_err(dsp,
- "%s.%d: Failed to write %zd bytes at %d in %s: %d\n",
- file, regions,
- to_write, offset,
- region_name, ret);
- goto out_fw;
- }
+ buf = wm_adsp_buf_alloc(region->data,
+ le32_to_cpu(region->len),
+ &buf_list);
+ if (!buf) {
+ adsp_err(dsp, "Out of memory\n");
+ ret = -ENOMEM;
+ goto out_fw;
+ }
- data += to_write;
- reg += to_write / 2;
- remain -= to_write;
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(region->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+ file, regions,
+ le32_to_cpu(region->len), offset,
+ region_name, ret);
+ goto out_fw;
}
}
@@ -1065,8 +1053,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
be32_to_cpu(adsp1_alg[i].zm));
region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].dm);
@@ -1083,8 +1073,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
region->type = WMFW_ADSP1_ZM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].zm);
@@ -1113,8 +1105,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
be32_to_cpu(adsp2_alg[i].zm));
region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
region->type = WMFW_ADSP2_XM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].xm);
@@ -1131,8 +1125,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
region->type = WMFW_ADSP2_YM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].ym);
@@ -1149,8 +1145,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
}
region = kzalloc(sizeof(*region), GFP_KERNEL);
- if (!region)
- return -ENOMEM;
+ if (!region) {
+ ret = -ENOMEM;
+ goto out;
+ }
region->type = WMFW_ADSP2_ZM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].zm);
@@ -1355,6 +1353,7 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
file, blocks, pos - firmware->size);
out_fw:
+ regmap_async_complete(regmap);
release_firmware(firmware);
wm_adsp_buf_free(&buf_list);
out:
@@ -1594,13 +1593,6 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0)
goto err;
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CONTROL,
- ADSP2_CORE_ENA,
- ADSP2_CORE_ENA);
- if (ret != 0)
- goto err;
-
dsp->running = true;
return;
@@ -1650,8 +1642,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
- ADSP2_START,
- ADSP2_START);
+ ADSP2_CORE_ENA | ADSP2_START,
+ ADSP2_CORE_ENA | ADSP2_START);
if (ret != 0)
goto err;
break;
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 0eed9b1b24e1..0dab382ba147 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -70,6 +70,7 @@ struct davinci_mcasp {
void __iomem *base;
u32 fifo_base;
struct device *dev;
+ struct snd_pcm_substream *substreams[2];
/* McASP specific data */
int tdm_slots;
@@ -80,6 +81,7 @@ struct davinci_mcasp {
u8 bclk_div;
u16 bclk_lrclk_ratio;
int streams;
+ u32 irq_request[2];
int sysclk_freq;
bool bclk_master;
@@ -90,6 +92,9 @@ struct davinci_mcasp {
bool dat_port;
+ /* Used for comstraint setting on the second stream */
+ u32 channels;
+
#ifdef CONFIG_PM_SLEEP
struct davinci_mcasp_context context;
#endif
@@ -154,9 +159,16 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
static void mcasp_start_rx(struct davinci_mcasp *mcasp)
{
+ if (mcasp->rxnumevt) { /* enable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ /* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
-
/*
* When ASYNC == 0 the transmit and receive sections operate
* synchronously from the transmit clock and frame sync. We need to make
@@ -167,74 +179,69 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
}
+ /* Activate serializer(s) */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
+ /* Release RX state machine */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+ /* Release Frame Sync generator */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-
if (mcasp_is_synchronous(mcasp))
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+ /* enable receive IRQs */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
}
static void mcasp_start_tx(struct davinci_mcasp *mcasp)
{
- u8 offset = 0, i;
u32 cnt;
+ if (mcasp->txnumevt) { /* enable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+ }
+
+ /* Start clocks */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+ /* Activate serializer(s) */
mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
- mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
- for (i = 0; i < mcasp->num_serializer; i++) {
- if (mcasp->serial_dir[i] == TX_MODE) {
- offset = i;
- break;
- }
- }
-
- /* wait for TX ready */
+ /* wait for XDATA to be cleared */
cnt = 0;
- while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
- TXSTATE) && (cnt < 100000))
+ while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
+ ~XRDATA) && (cnt < 100000))
cnt++;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+ /* Release TX state machine */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+ /* Release Frame Sync generator */
+ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+ /* enable transmit IRQs */
+ mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
}
static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
{
- u32 reg;
-
mcasp->streams++;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (mcasp->txnumevt) { /* enable FIFO */
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
- }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
mcasp_start_tx(mcasp);
- } else {
- if (mcasp->rxnumevt) { /* enable FIFO */
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
- }
+ else
mcasp_start_rx(mcasp);
- }
}
static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
{
+ /* disable IRQ sources */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
+
/*
* In synchronous mode stop the TX clocks if no other stream is
* running
@@ -244,12 +251,22 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+ if (mcasp->rxnumevt) { /* disable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
}
static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
{
u32 val = 0;
+ /* disable IRQ sources */
+ mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
+
/*
* In synchronous mode keep TX clocks running if the capture stream is
* still running.
@@ -259,27 +276,92 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+ if (mcasp->txnumevt) { /* disable FIFO */
+ u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+ mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+ }
}
static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
{
- u32 reg;
-
mcasp->streams--;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (mcasp->txnumevt) { /* disable FIFO */
- reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- }
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
mcasp_stop_tx(mcasp);
- } else {
- if (mcasp->rxnumevt) { /* disable FIFO */
- reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
- mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
- }
+ else
mcasp_stop_rx(mcasp);
+}
+
+static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ struct snd_pcm_substream *substream;
+ u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK];
+ u32 handled_mask = 0;
+ u32 stat;
+
+ stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
+ if (stat & XUNDRN & irq_mask) {
+ dev_warn(mcasp->dev, "Transmit buffer underflow\n");
+ handled_mask |= XUNDRN;
+
+ substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream) {
+ snd_pcm_stream_lock_irq(substream);
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irq(substream);
+ }
}
+
+ if (!handled_mask)
+ dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n",
+ stat);
+
+ if (stat & XRERR)
+ handled_mask |= XRERR;
+
+ /* Ack the handled event only */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask);
+
+ return IRQ_RETVAL(handled_mask);
+}
+
+static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
+{
+ struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+ struct snd_pcm_substream *substream;
+ u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE];
+ u32 handled_mask = 0;
+ u32 stat;
+
+ stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
+ if (stat & ROVRN & irq_mask) {
+ dev_warn(mcasp->dev, "Receive buffer overflow\n");
+ handled_mask |= ROVRN;
+
+ substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream) {
+ snd_pcm_stream_lock_irq(substream);
+ if (snd_pcm_running(substream))
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock_irq(substream);
+ }
+ }
+
+ if (!handled_mask)
+ dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n",
+ stat);
+
+ if (stat & XRERR)
+ handled_mask |= XRERR;
+
+ /* Ack the handled event only */
+ mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask);
+
+ return IRQ_RETVAL(handled_mask);
}
static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -500,8 +582,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
* both left and right channels), so it has to be divided by number of
* tdm-slots (for I2S - divided by 2).
*/
- if (mcasp->bclk_lrclk_ratio)
- word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+ if (mcasp->bclk_lrclk_ratio) {
+ u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+
+ /*
+ * When we have more bclk then it is needed for the data, we
+ * need to use the rotation to move the received samples to have
+ * correct alignment.
+ */
+ rx_rotate = (slot_length - word_length) / 4;
+ word_length = slot_length;
+ }
/* mapping of the XSSZ bit-field as described in the datasheet */
fmt = (word_length >> 1) - 1;
@@ -635,19 +726,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
return 0;
}
-static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
+ int channels)
{
int i, active_slots;
+ int total_slots;
+ int active_serializers;
u32 mask = 0;
u32 busel = 0;
- if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) {
- dev_err(mcasp->dev, "tdm slot %d not supported\n",
- mcasp->tdm_slots);
- return -EINVAL;
- }
+ total_slots = mcasp->tdm_slots;
+
+ /*
+ * If more than one serializer is needed, then use them with
+ * their specified tdm_slots count. Otherwise, one serializer
+ * can cope with the transaction using as many slots as channels
+ * in the stream, requires channels symmetry
+ */
+ active_serializers = (channels + total_slots - 1) / total_slots;
+ if (active_serializers == 1)
+ active_slots = channels;
+ else
+ active_slots = total_slots;
- active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
for (i = 0; i < active_slots; i++)
mask |= (1 << i);
@@ -659,12 +760,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
- FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
+ FSXMOD(total_slots), FSXMOD(0x1FF));
mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
- FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
+ FSRMOD(total_slots), FSRMOD(0x1FF));
return 0;
}
@@ -778,7 +879,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
ret = mcasp_dit_hw_param(mcasp, params_rate(params));
else
- ret = mcasp_i2s_hw_param(mcasp, substream->stream);
+ ret = mcasp_i2s_hw_param(mcasp, substream->stream,
+ channels);
if (ret)
return ret;
@@ -826,6 +928,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
davinci_config_channel_size(mcasp, word_length);
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+ mcasp->channels = channels;
+
return 0;
}
@@ -854,7 +959,65 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+ u32 max_channels = 0;
+ int i, dir;
+
+ mcasp->substreams[substream->stream] = substream;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ return 0;
+
+ /*
+ * Limit the maximum allowed channels for the first stream:
+ * number of serializers for the direction * tdm slots per serializer
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dir = TX_MODE;
+ else
+ dir = RX_MODE;
+
+ for (i = 0; i < mcasp->num_serializer; i++) {
+ if (mcasp->serial_dir[i] == dir)
+ max_channels++;
+ }
+ max_channels *= mcasp->tdm_slots;
+ /*
+ * If the already active stream has less channels than the calculated
+ * limnit based on the seirializers * tdm_slots, we need to use that as
+ * a constraint for the second stream.
+ * Otherwise (first stream or less allowed channels) we use the
+ * calculated constraint.
+ */
+ if (mcasp->channels && mcasp->channels < max_channels)
+ max_channels = mcasp->channels;
+
+ snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 2, max_channels);
+ return 0;
+}
+
+static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+
+ mcasp->substreams[substream->stream] = NULL;
+
+ if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+ return;
+
+ if (!cpu_dai->active)
+ mcasp->channels = 0;
+}
+
static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+ .startup = davinci_mcasp_startup,
+ .shutdown = davinci_mcasp_shutdown,
.trigger = davinci_mcasp_trigger,
.hw_params = davinci_mcasp_hw_params,
.set_fmt = davinci_mcasp_set_dai_fmt,
@@ -971,6 +1134,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
},
.ops = &davinci_mcasp_dai_ops,
+ .symmetric_samplebits = 1,
},
{
.name = "davinci-mcasp.1",
@@ -1194,6 +1358,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
struct resource *mem, *ioarea, *res, *dat;
struct davinci_mcasp_pdata *pdata;
struct davinci_mcasp *mcasp;
+ char *irq_name;
+ int irq;
int ret;
if (!pdev->dev.platform_data && !pdev->dev.of_node) {
@@ -1235,6 +1401,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
ret = pm_runtime_get_sync(&pdev->dev);
if (IS_ERR_VALUE(ret)) {
dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+ pm_runtime_disable(&pdev->dev);
return ret;
}
@@ -1246,7 +1413,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
}
mcasp->op_mode = pdata->op_mode;
- mcasp->tdm_slots = pdata->tdm_slots;
+ /* sanity check for tdm slots parameter */
+ if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+ if (pdata->tdm_slots < 2) {
+ dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+ pdata->tdm_slots);
+ mcasp->tdm_slots = 2;
+ } else if (pdata->tdm_slots > 32) {
+ dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+ pdata->tdm_slots);
+ mcasp->tdm_slots = 32;
+ } else {
+ mcasp->tdm_slots = pdata->tdm_slots;
+ }
+ }
+
mcasp->num_serializer = pdata->num_serializer;
#ifdef CONFIG_PM_SLEEP
mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
@@ -1260,6 +1441,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
mcasp->dev = &pdev->dev;
+ irq = platform_get_irq_byname(pdev, "rx");
+ if (irq >= 0) {
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n",
+ dev_name(&pdev->dev));
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_rx_irq_handler,
+ IRQF_ONESHOT, irq_name, mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "RX IRQ request failed\n");
+ goto err;
+ }
+
+ mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
+ }
+
+ irq = platform_get_irq_byname(pdev, "tx");
+ if (irq >= 0) {
+ irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n",
+ dev_name(&pdev->dev));
+ ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+ davinci_mcasp_tx_irq_handler,
+ IRQF_ONESHOT, irq_name, mcasp);
+ if (ret) {
+ dev_err(&pdev->dev, "TX IRQ request failed\n");
+ goto err;
+ }
+
+ mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
+ }
+
dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
if (dat)
mcasp->dat_port = true;
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 98fbc451892a..79dc511180bf 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -253,6 +253,13 @@
#define TXFSRST BIT(12) /* Frame Sync Generator Reset */
/*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRERR BIT(8) /* Transmit/Receive error */
+#define XRDATA BIT(5) /* Transmit/Receive data ready */
+
+/*
* DAVINCI_MCASP_AMUTE_REG - Mute Control Register Bits
*/
#define MUTENA(val) (val)
@@ -279,6 +286,16 @@
#define TXDATADMADIS BIT(0)
/*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN BIT(0)
+
+/*
* DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
*/
#define FIFO_ENABLE BIT(16)
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index e961388e6e9c..08f0229f8d68 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev)
return -EINVAL;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "no i2s resource defined\n");
- return -ENODEV;
- }
-
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "i2s region already claimed\n");
- return -EBUSY;
- }
-
dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
if (!dev) {
dev_warn(&pdev->dev, "kzalloc fail\n");
return -ENOMEM;
}
- dev->i2s_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- if (!dev->i2s_base) {
- dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+ dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+ if (!dw_i2s_dai) {
+ dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
return -ENOMEM;
}
+ dw_i2s_dai->ops = &dw_i2s_dai_ops;
+ dw_i2s_dai->suspend = dw_i2s_suspend;
+ dw_i2s_dai->resume = dw_i2s_resume;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "no i2s resource defined\n");
+ return -ENODEV;
+ }
+
+ dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(dev->i2s_base)) {
+ dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+ return PTR_ERR(dev->i2s_base);
+ }
+
cap = pdata->cap;
dev->capability = cap;
dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
@@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
if (ret < 0)
goto err_clk_put;
- dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
- if (!dw_i2s_dai) {
- dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
- ret = -ENOMEM;
- goto err_clk_disable;
- }
-
if (cap & DWC_I2S_PLAY) {
dev_dbg(&pdev->dev, " designware: play supported\n");
dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
@@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
dw_i2s_dai->capture.rates = pdata->snd_rates;
}
- dw_i2s_dai->ops = &dw_i2s_dai_ops;
- dw_i2s_dai->suspend = dw_i2s_suspend;
- dw_i2s_dai->resume = dw_i2s_resume;
-
dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dev);
ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component,
diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index eb093d5b85c4..b175b0145a42 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -105,7 +105,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
int ret;
int int_port = 0, ext_port;
struct device_node *np = pdev->dev.of_node;
- struct device_node *ssi_np, *codec_np;
+ struct device_node *ssi_np = NULL, *codec_np = NULL;
eukrea_tlv320.dev = &pdev->dev;
if (np) {
@@ -217,8 +217,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
err:
if (ret)
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- if (np)
- of_node_put(ssi_np);
+ of_node_put(ssi_np);
return ret;
}
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index 007c772f3cef..3f6959c8e2f7 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -51,6 +51,7 @@ struct codec_priv {
* @sysclk_freq[2]: SYSCLK rates for set_sysclk()
* @sysclk_dir[2]: SYSCLK directions for set_sysclk()
* @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ * @slot_width: Slot width of each frame
*
* Note: [1] for tx and [0] for rx
*/
@@ -58,6 +59,7 @@ struct cpu_priv {
unsigned long sysclk_freq[2];
u32 sysclk_dir[2];
u32 sysclk_id[2];
+ u32 slot_width;
};
/**
@@ -125,7 +127,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
priv->sample_rate = params_rate(params);
priv->sample_format = params_format(params);
- if (priv->card.set_bias_level)
+ /*
+ * If codec-dai is DAI Master and all configurations are already in the
+ * set_bias_level(), bypass the remaining settings in hw_params().
+ * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
+ */
+ if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
return 0;
/* Specific configurations of DAIs starts from here */
@@ -137,6 +144,15 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
return ret;
}
+ if (cpu_priv->slot_width) {
+ ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2,
+ cpu_priv->slot_width);
+ if (ret) {
+ dev_err(dev, "failed to set TDM slot for cpu dai\n");
+ return ret;
+ }
+ }
+
return 0;
}
@@ -448,6 +464,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
+ priv->cpu_priv.slot_width = 32;
priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
} else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 3b145313f93e..9deabdd2b1a2 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -684,12 +684,38 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
}
}
+static struct reg_default fsl_asrc_reg[] = {
+ { REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
+ { REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
+ { REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
+ { REG_ASRCDR2, 0x0000 }, { REG_ASRSTR, 0x0000 },
+ { REG_ASRRA, 0x0000 }, { REG_ASRRB, 0x0000 },
+ { REG_ASRRC, 0x0000 }, { REG_ASRPM1, 0x0000 },
+ { REG_ASRPM2, 0x0000 }, { REG_ASRPM3, 0x0000 },
+ { REG_ASRPM4, 0x0000 }, { REG_ASRPM5, 0x0000 },
+ { REG_ASRTFR1, 0x0000 }, { REG_ASRCCR, 0x0000 },
+ { REG_ASRDIA, 0x0000 }, { REG_ASRDOA, 0x0000 },
+ { REG_ASRDIB, 0x0000 }, { REG_ASRDOB, 0x0000 },
+ { REG_ASRDIC, 0x0000 }, { REG_ASRDOC, 0x0000 },
+ { REG_ASRIDRHA, 0x0000 }, { REG_ASRIDRLA, 0x0000 },
+ { REG_ASRIDRHB, 0x0000 }, { REG_ASRIDRLB, 0x0000 },
+ { REG_ASRIDRHC, 0x0000 }, { REG_ASRIDRLC, 0x0000 },
+ { REG_ASR76K, 0x0A47 }, { REG_ASR56K, 0x0DF3 },
+ { REG_ASRMCRA, 0x0000 }, { REG_ASRFSTA, 0x0000 },
+ { REG_ASRMCRB, 0x0000 }, { REG_ASRFSTB, 0x0000 },
+ { REG_ASRMCRC, 0x0000 }, { REG_ASRFSTC, 0x0000 },
+ { REG_ASRMCR1A, 0x0000 }, { REG_ASRMCR1B, 0x0000 },
+ { REG_ASRMCR1C, 0x0000 },
+};
+
static const struct regmap_config fsl_asrc_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = REG_ASRMCR1C,
+ .reg_defaults = fsl_asrc_reg,
+ .num_reg_defaults = ARRAY_SIZE(fsl_asrc_reg),
.readable_reg = fsl_asrc_readable_reg,
.volatile_reg = fsl_asrc_volatile_reg,
.writeable_reg = fsl_asrc_writeable_reg,
@@ -792,7 +818,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return -ENOMEM;
asrc_priv->pdev = pdev;
- strcpy(asrc_priv->name, np->name);
+ strncpy(asrc_priv->name, np->name, sizeof(asrc_priv->name) - 1);
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 8bcdfda09d7a..ca319d59f843 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -513,10 +513,15 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
u32 width = snd_pcm_format_width(params_format(params));
u32 channels = params_channels(params);
u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
+ u32 slot_width = width;
u32 bclk, mask, val;
int ret;
- bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots;
+ /* Override slot_width if being specifially set */
+ if (esai_priv->slot_width)
+ slot_width = esai_priv->slot_width;
+
+ bclk = params_rate(params) * slot_width * esai_priv->slots;
ret = fsl_esai_set_bclk(dai, tx, bclk);
if (ret)
@@ -538,7 +543,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
- val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
+ val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
@@ -734,7 +739,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
return -ENOMEM;
esai_priv->pdev = pdev;
- strcpy(esai_priv->name, np->name);
+ strncpy(esai_priv->name, np->name, sizeof(esai_priv->name) - 1);
/* Get the addresses and IRQ */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -780,9 +785,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
return ret;
}
- /* Set a default slot size */
- esai_priv->slot_width = 32;
-
/* Set a default slot number */
esai_priv->slots = 2;
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index e6955170dc42..b6b0d25f6ace 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -67,8 +67,6 @@
/**
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI
*
- * This driver currently only supports the SSI running in I2S slave mode.
- *
* The SSI has a limitation in that the samples must be in the same byte
* order as the host CPU. This is because when multiple bytes are written
* to the STX register, the bytes and bits must be written in the same
@@ -1099,7 +1097,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
};
static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -1363,7 +1361,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
return PTR_ERR(ssi_private->regs);
}
- ssi_private->irq = irq_of_parse_and_map(np, 0);
+ ssi_private->irq = platform_get_irq(pdev, 0);
if (!ssi_private->irq) {
dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
return -ENXIO;
@@ -1389,7 +1387,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
if (ssi_private->soc->imx) {
ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem);
if (ret)
- goto error_irqmap;
+ return ret;
}
ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
@@ -1412,7 +1410,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
if (ret)
- goto error_asoc_register;
+ goto error_irq;
/*
* If codec-handle property is missing from SSI node, we assume
@@ -1460,10 +1458,6 @@ error_asoc_register:
if (ssi_private->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private);
-error_irqmap:
- if (ssi_private->use_dma)
- irq_dispose_mapping(ssi_private->irq);
-
return ret;
}
@@ -1480,9 +1474,6 @@ static int fsl_ssi_remove(struct platform_device *pdev)
if (ssi_private->soc->imx)
fsl_ssi_imx_clean(pdev, ssi_private);
- if (ssi_private->use_dma)
- irq_dispose_mapping(ssi_private->irq);
-
return 0;
}
diff --git a/sound/soc/fsl/imx-sgtl5000.c b/sound/soc/fsl/imx-sgtl5000.c
index 1cb22dd034eb..1dab963a59f7 100644
--- a/sound/soc/fsl/imx-sgtl5000.c
+++ b/sound/soc/fsl/imx-sgtl5000.c
@@ -175,10 +175,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
fail:
if (data && !IS_ERR(data->codec_clk))
clk_put(data->codec_clk);
- if (ssi_np)
- of_node_put(ssi_np);
- if (codec_np)
- of_node_put(codec_np);
+ of_node_put(ssi_np);
+ of_node_put(codec_np);
return ret;
}
diff --git a/sound/soc/fsl/imx-spdif.c b/sound/soc/fsl/imx-spdif.c
index e1dc40143600..0c9068ebe1e7 100644
--- a/sound/soc/fsl/imx-spdif.c
+++ b/sound/soc/fsl/imx-spdif.c
@@ -74,8 +74,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
end:
- if (spdif_np)
- of_node_put(spdif_np);
+ of_node_put(spdif_np);
return ret;
}
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index ab2fdd76b693..60b0a5b1f1f1 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = {
static struct snd_soc_dai_driver imx_ac97_dai = {
.probe = imx_ssi_dai_probe,
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index 3a3d17ce6ba4..48179ffe1543 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -281,10 +281,8 @@ static int imx_wm8962_probe(struct platform_device *pdev)
clk_fail:
clk_disable_unprepare(data->codec_clk);
fail:
- if (ssi_np)
- of_node_put(ssi_np);
- if (codec_np)
- of_node_put(codec_np);
+ of_node_put(ssi_np);
+ of_node_put(codec_np);
return ret;
}
diff --git a/sound/soc/fsl/mpc5200_dma.c b/sound/soc/fsl/mpc5200_dma.c
index f2b5d756b1f3..0b82e209b6e3 100644
--- a/sound/soc/fsl/mpc5200_dma.c
+++ b/sound/soc/fsl/mpc5200_dma.c
@@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
goto capture_alloc_err;
}
- if (rtd->codec->ac97)
- rtd->codec->ac97->private_data = psc_dma;
-
return 0;
capture_alloc_err:
diff --git a/sound/soc/fsl/mpc5200_psc_ac97.c b/sound/soc/fsl/mpc5200_psc_ac97.c
index 24eafa2cfbf4..c6ed6ba965a9 100644
--- a/sound/soc/fsl/mpc5200_psc_ac97.c
+++ b/sound/soc/fsl/mpc5200_psc_ac97.c
@@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
static struct snd_soc_dai_driver psc_ac97_dai[] = {
{
.name = "mpc5200-psc-ac97.0",
- .ac97_control = 1,
+ .bus_control = true,
.probe = psc_ac97_probe,
.playback = {
.stream_name = "AC97 Playback",
@@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
},
{
.name = "mpc5200-psc-ac97.1",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 SPDIF",
.channels_min = 1,
@@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = {
static int psc_ac97_of_probe(struct platform_device *op)
{
int rc;
- struct snd_ac97 ac97;
struct mpc52xx_psc __iomem *regs;
rc = mpc5200_audio_dma_create(op);
@@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op)
psc_dma = dev_get_drvdata(&op->dev);
regs = psc_dma->psc_regs;
- ac97.private_data = psc_dma;
psc_dma->imr = 0;
out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index f5b4a9c79cdf..e989ecf046c9 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -3,6 +3,7 @@ config SND_MFLD_MACHINE
depends on INTEL_SCU_IPC
select SND_SOC_SN95031
select SND_SST_MFLD_PLATFORM
+ select SND_SST_IPC_PCI
help
This adds support for ASoC machine driver for Intel(R) MID Medfield platform
used as alsa device in audio substem in Intel(R) MID devices
@@ -12,10 +13,23 @@ config SND_MFLD_MACHINE
config SND_SST_MFLD_PLATFORM
tristate
+config SND_SST_IPC
+ tristate
+
+config SND_SST_IPC_PCI
+ tristate
+ select SND_SST_IPC
+
+config SND_SST_IPC_ACPI
+ tristate
+ select SND_SST_IPC
+ depends on ACPI
+
config SND_SOC_INTEL_SST
tristate "ASoC support for Intel(R) Smart Sound Technology"
select SND_SOC_INTEL_SST_ACPI if ACPI
depends on (X86 || COMPILE_TEST)
+ depends on DW_DMAC_CORE
help
This adds support for Intel(R) Smart Sound Technology (SST).
Say Y if you have such a device
@@ -32,7 +46,8 @@ config SND_SOC_INTEL_BAYTRAIL
config SND_SOC_INTEL_HASWELL_MACH
tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
- depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+ depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\
+ I2C_DESIGNWARE_PLATFORM
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT5640
help
@@ -61,7 +76,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
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
+ depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\
+ I2C_DESIGNWARE_PLATFORM
select SND_SOC_INTEL_HASWELL
select SND_COMPRESS_OFFLOAD
select SND_SOC_RT286
@@ -70,3 +86,27 @@ config SND_SOC_INTEL_BROADWELL_MACH
Ultrabook platforms.
Say Y if you have such a device
If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_RT5640_MACH
+ tristate "ASoC Audio DSP Support for MID BYT Platform"
+ depends on X86
+ select SND_SOC_RT5640
+ select SND_SST_MFLD_PLATFORM
+ select SND_SST_IPC_ACPI
+ help
+ This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
+ used as alsa device in audio substem in Intel(R) MID devices
+ Say Y if you have such a device
+ If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+ tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+ depends on X86_INTEL_LPSS
+ select SND_SOC_RT5670
+ select SND_SST_MFLD_PLATFORM
+ select SND_SST_IPC_ACPI
+ help
+ This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+ platforms with RT5672 audio codec.
+ Say Y if you have such a device
+ If unsure select "N".
diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile
index f841786dad15..e928ec385300 100644
--- a/sound/soc/intel/Makefile
+++ b/sound/soc/intel/Makefile
@@ -26,8 +26,15 @@ snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.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
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
diff --git a/sound/soc/intel/broadwell.c b/sound/soc/intel/broadwell.c
index 0e550f14028f..c256764e3c4b 100644
--- a/sound/soc/intel/broadwell.c
+++ b/sound/soc/intel/broadwell.c
@@ -19,6 +19,7 @@
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
+#include <sound/jack.h>
#include <sound/pcm_params.h>
#include "sst-dsp.h"
@@ -26,8 +27,26 @@
#include "../codecs/rt286.h"
+static struct snd_soc_jack broadwell_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin broadwell_headset_pins[] = {
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+
+static const struct snd_kcontrol_new broadwell_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
static const struct snd_soc_dapm_widget broadwell_widgets[] = {
- SND_SOC_DAPM_HP("Headphones", NULL),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("DMIC1", NULL),
@@ -42,7 +61,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
{"Speaker", NULL, "SPOL"},
/* HP jack connectors - unknown if we have jack deteck */
- {"Headphones", NULL, "HPO Pin"},
+ {"Headphone Jack", NULL, "HPO Pin"},
/* other jacks */
{"MIC1", NULL, "Mic Jack"},
@@ -57,6 +76,27 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
};
+static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ int ret = 0;
+ ret = snd_soc_jack_new(codec, "Headset",
+ SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset);
+
+ if (ret)
+ return ret;
+
+ ret = snd_soc_jack_add_pins(&broadwell_headset,
+ ARRAY_SIZE(broadwell_headset_pins),
+ broadwell_headset_pins);
+ if (ret)
+ return ret;
+
+ rt286_mic_detect(codec, &broadwell_headset);
+ return 0;
+}
+
+
static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -116,7 +156,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
}
/* always connected - check HP for jack detect */
- snd_soc_dapm_enable_pin(dapm, "Headphones");
+ snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
snd_soc_dapm_enable_pin(dapm, "Speaker");
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
snd_soc_dapm_enable_pin(dapm, "Line Jack");
@@ -131,7 +171,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
/* Front End DAI links */
{
.name = "System PCM",
- .stream_name = "System Playback",
+ .stream_name = "System Playback/Capture",
.cpu_dai_name = "System Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 1,
@@ -140,6 +180,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
.init = broadwell_rtd_init,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
+ .dpcm_capture = 1,
},
{
.name = "Offload0",
@@ -174,18 +215,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
.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 */
@@ -196,6 +225,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
.no_pcm = 1,
.codec_name = "i2c-INT343A:00",
.codec_dai_name = "rt286-aif1",
+ .init = broadwell_rt286_codec_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ignore_suspend = 1,
@@ -213,6 +243,8 @@ static struct snd_soc_card broadwell_rt286 = {
.owner = THIS_MODULE,
.dai_link = broadwell_rt286_dais,
.num_links = ARRAY_SIZE(broadwell_rt286_dais),
+ .controls = broadwell_controls,
+ .num_controls = ARRAY_SIZE(broadwell_controls),
.dapm_widgets = broadwell_widgets,
.num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
.dapm_routes = broadwell_rt286_map,
diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c
new file mode 100644
index 000000000000..f5d0fc1ab10c
--- /dev/null
+++ b/sound/soc/intel/bytcr_dpcm_rt5640.c
@@ -0,0 +1,230 @@
+/*
+ * byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
+ *
+ * Copyright (C) 2014 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5640.h"
+#include "sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_audio_map[] = {
+ {"IN2P", NULL, "Headset Mic"},
+ {"IN2N", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "MICBIAS1"},
+ {"IN1P", NULL, "MICBIAS1"},
+ {"LDO2", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Ext Spk", NULL, "SPOLP"},
+ {"Ext Spk", NULL, "SPOLN"},
+ {"Ext Spk", NULL, "SPORP"},
+ {"Ext Spk", NULL, "SPORN"},
+
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_kcontrol_new byt_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int byt_aif1_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;
+
+ snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec clock %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+ params_rate(params) * 50,
+ params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_dai_params = {
+ .formats = SNDRV_PCM_FMTBIT_S24_LE,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+};
+
+static int byt_codec_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 DSP will covert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int byt_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static struct snd_soc_ops byt_aif1_ops = {
+ .startup = byt_aif1_startup,
+};
+
+static struct snd_soc_ops byt_be_ssp2_ops = {
+ .hw_params = byt_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link byt_dailink[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Baytrail Audio Port",
+ .stream_name = "Baytrail Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .ignore_suspend = 1,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &byt_aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Baytrail Compressed Port",
+ .stream_name = "Baytrail Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .be_id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "rt5640-aif1",
+ .codec_name = "i2c-10EC5640:00",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .be_hw_params_fixup = byt_codec_fixup,
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &byt_be_ssp2_ops,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_byt = {
+ .name = "baytrailcraudio",
+ .dai_link = byt_dailink,
+ .num_links = ARRAY_SIZE(byt_dailink),
+ .dapm_widgets = byt_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
+ .dapm_routes = byt_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(byt_audio_map),
+ .controls = byt_mc_controls,
+ .num_controls = ARRAY_SIZE(byt_mc_controls),
+};
+
+static int snd_byt_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+
+ /* register the soc card */
+ snd_soc_card_byt.dev = &pdev->dev;
+
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
+ if (ret_val) {
+ dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_byt);
+ return ret_val;
+}
+
+static struct platform_driver snd_byt_mc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bytt100_rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = snd_byt_mc_probe,
+};
+
+module_platform_driver(snd_byt_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytrt5640-audio");
diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c
new file mode 100644
index 000000000000..9b8b561171b7
--- /dev/null
+++ b/sound/soc/intel/cht_bsw_rt5672.c
@@ -0,0 +1,285 @@
+/*
+ * cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
+ * Cherrytrail and Braswell, with RT5672 codec.
+ *
+ * Copyright (C) 2014 Intel Corp
+ * Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ * Mengdong Lin <mengdong.lin@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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5670.h"
+#include "sst-atom-controls.h"
+
+/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
+#define CHT_PLAT_CLK_3_HZ 19200000
+#define CHT_CODEC_DAI "rt5670-aif1"
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+ int i;
+
+ for (i = 0; i < card->num_rtd; i++) {
+ struct snd_soc_pcm_runtime *rtd;
+
+ rtd = card->rtd + i;
+ if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+ strlen(CHT_CODEC_DAI)))
+ return rtd->codec_dai;
+ }
+ return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *codec_dai;
+
+ codec_dai = cht_get_codec_dai(card);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+ return -EIO;
+ }
+
+ if (!SND_SOC_DAPM_EVENT_OFF(event))
+ return 0;
+
+ /* Set codec sysclk source to its internal clock because codec PLL will
+ * be off when idle and MCLK will also be off by ACPI when codec is
+ * runtime suspended. Codec needs clock for jack detection and button
+ * press.
+ */
+ snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+ 0, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Int Mic", NULL),
+ SND_SOC_DAPM_SPK("Ext Spk", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+ {"IN1P", NULL, "Headset Mic"},
+ {"IN1N", NULL, "Headset Mic"},
+ {"DMIC L1", NULL, "Int Mic"},
+ {"DMIC R1", NULL, "Int Mic"},
+ {"Headphone", NULL, "HPOL"},
+ {"Headphone", NULL, "HPOR"},
+ {"Ext Spk", NULL, "SPOLP"},
+ {"Ext Spk", NULL, "SPOLN"},
+ {"Ext Spk", NULL, "SPORP"},
+ {"Ext Spk", NULL, "SPORN"},
+ {"AIF1 Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+ {"ssp2 Rx", NULL, "AIF1 Capture"},
+ {"Headphone", NULL, "Platform Clock"},
+ {"Headset Mic", NULL, "Platform Clock"},
+ {"Int Mic", NULL, "Platform Clock"},
+ {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+ SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_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;
+
+ /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+ CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+ return ret;
+ }
+
+ /* set codec sysclk source to PLL */
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+ params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ int ret;
+ struct snd_soc_dai *codec_dai = runtime->codec_dai;
+
+ /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+ ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+ if (ret < 0) {
+ dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cht_codec_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 DSP will covert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ SNDRV_PCM_FORMAT_S24_LE);
+ return 0;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+ .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+ .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+ /* Front End DAI links */
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .ignore_suspend = 1,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Compressed Port",
+ .stream_name = "Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP2 - Codec */
+ .name = "SSP2-Codec",
+ .be_id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "rt5670-aif1",
+ .codec_name = "i2c-10EC5670:00",
+ .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .init = cht_codec_init,
+ .be_hw_params_fixup = cht_codec_fixup,
+ .ignore_suspend = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &cht_be_ssp2_ops,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+ .name = "cherrytrailcraudio",
+ .dai_link = cht_dailink,
+ .num_links = ARRAY_SIZE(cht_dailink),
+ .dapm_widgets = cht_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+ .dapm_routes = cht_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+ .controls = cht_mc_controls,
+ .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+
+ /* register the soc card */
+ snd_soc_card_cht.dev = &pdev->dev;
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &snd_soc_card_cht);
+ return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cht-bsw-rt5672",
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5672");
diff --git a/sound/soc/intel/haswell.c b/sound/soc/intel/haswell.c
index 3981982674ac..cb8a482b5f30 100644
--- a/sound/soc/intel/haswell.c
+++ b/sound/soc/intel/haswell.c
@@ -109,7 +109,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
/* Front End DAI links */
{
.name = "System",
- .stream_name = "System Playback",
+ .stream_name = "System Playback/Capture",
.cpu_dai_name = "System Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 1,
@@ -118,6 +118,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
.init = haswell_rtd_init,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
+ .dpcm_capture = 1,
},
{
.name = "Offload0",
@@ -152,17 +153,6 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
},
- {
- .name = "Capture",
- .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 */
{
diff --git a/sound/soc/intel/sst-atom-controls.c b/sound/soc/intel/sst-atom-controls.c
index 7104a34181a9..90aa5c0476f3 100644
--- a/sound/soc/intel/sst-atom-controls.c
+++ b/sound/soc/intel/sst-atom-controls.c
@@ -15,6 +15,9 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
+ * In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ * we forward the settings and parameters, rest we keep the values in
+ * driver and forward when DAPM enables them
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv,
return ret;
}
+/**
+ * tx map value is a bitfield where each bit represents a FW channel
+ *
+ * 3 2 1 0 # 0 = codec0, 1 = codec1
+ * RLRLRLRL # 3, 4 = reserved
+ *
+ * e.g. slot 0 rx map = 00001100b -> data from slot 0 goes into codec_in1 L,R
+ */
+static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/**
+ * rx map value is a bitfield where each bit represents a slot
+ *
+ * 76543210 # 0 = slot 0, 1 = slot 1
+ *
+ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
+ */
+static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
+ 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/**
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+ struct sst_param_sba_ssp_slot_map cmd;
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
+ cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
+ - sizeof(struct sst_dsp_header);
+
+ cmd.param_id = SBA_SET_SSP_SLOT_MAP;
+ cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
+ + sizeof(cmd.ssp_index);
+ cmd.ssp_index = SSP_CODEC;
+
+ memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
+ memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+ return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+ uinfo->value.enumerated.items = e->max;
+
+ if (uinfo->value.enumerated.item > e->max - 1)
+ uinfo->value.enumerated.item = e->max - 1;
+ strcpy(uinfo->value.enumerated.name,
+ e->texts[uinfo->value.enumerated.item]);
+
+ return 0;
+}
+
+/**
+ * sst_slot_get - get the status of the interleaver/deinterleaver control
+ *
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ unsigned int ctl_no = e->reg;
+ unsigned int is_tx = e->tx;
+ unsigned int val, mux;
+ u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+ mutex_lock(&drv->lock);
+ val = 1 << ctl_no;
+ /* search which slot/channel has this bit set - there should be only one */
+ for (mux = e->max; mux > 0; mux--)
+ if (map[mux - 1] & val)
+ break;
+
+ ucontrol->value.enumerated.item[0] = mux;
+ mutex_unlock(&drv->lock);
+
+ dev_dbg(c->dev, "%s - %s map = %#x\n",
+ is_tx ? "tx channel" : "rx slot",
+ e->texts[mux], mux ? map[mux - 1] : -1);
+ return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ int ret = 0;
+
+ if (e->w && e->w->power)
+ ret = sst_send_slot_map(drv);
+ else
+ dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+ kcontrol->id.name);
+ return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ *
+ * (de)interleaver controls are defined in opposite sense to be user-friendly
+ *
+ * Instead of the enum value being the value written to the register, it is the
+ * register address; and the kcontrol number (register num) is the value written
+ * to the register. This is so that there can be only one value for each
+ * slot/channel since there is only one control for each slot/channel.
+ *
+ * This means that whenever an enum is set, we need to clear the bit
+ * for that kcontrol_no for all the interleaver OR deinterleaver registers
+ */
+static int sst_slot_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_enum *e = (void *)kcontrol->private_value;
+ int i, ret = 0;
+ unsigned int ctl_no = e->reg;
+ unsigned int is_tx = e->tx;
+ unsigned int slot_channel_no;
+ unsigned int val, mux;
+ u8 *map;
+
+ map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+ val = 1 << ctl_no;
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > e->max - 1)
+ return -EINVAL;
+
+ mutex_lock(&drv->lock);
+ /* first clear all registers of this bit */
+ for (i = 0; i < e->max; i++)
+ map[i] &= ~val;
+
+ if (mux == 0) {
+ /* kctl set to 'none' and we reset the bits so send IPC */
+ ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+ mutex_unlock(&drv->lock);
+ return ret;
+ }
+
+ /* offset by one to take "None" into account */
+ slot_channel_no = mux - 1;
+ map[slot_channel_no] |= val;
+
+ dev_dbg(c->dev, "%s %s map = %#x\n",
+ is_tx ? "tx channel" : "rx slot",
+ e->texts[mux], map[slot_channel_no]);
+
+ ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+ mutex_unlock(&drv->lock);
+ return ret;
+}
+
static int sst_send_algo_cmd(struct sst_data *drv,
struct sst_algo_control *bc)
{
@@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv,
return ret;
}
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ *
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+ const char *pipe, struct sst_ids *ids)
+{
+ int ret = 0;
+ struct sst_algo_control *bc;
+ struct sst_module *algo = NULL;
+
+ dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+ list_for_each_entry(algo, &ids->algo_list, node) {
+ bc = (void *)algo->kctl->private_value;
+
+ dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+ algo->kctl->id.name, pipe);
+ ret = sst_send_algo_cmd(drv, bc);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -162,6 +370,743 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
return ret;
}
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = mc->stereo ? 2 : 1;
+ uinfo->value.integer.min = mc->min;
+ uinfo->value.integer.max = mc->max;
+
+ return 0;
+}
+
+/**
+ * sst_send_gain_cmd - send the gain algorithm IPC to the FW
+ * @gv: the stored value of gain (also contains rampduration)
+ * @mute: flag that indicates whether this was called from the
+ * digital_mute callback or directly. If called from the
+ * digital_mute callback, module will be muted/unmuted based on this
+ * flag. The flag is always 0 if called directly.
+ *
+ * Called with sst_data.lock held
+ *
+ * The user-set gain value is sent only if the user-controllable 'mute' control
+ * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
+ * sent.
+ */
+static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+ u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+ struct sst_cmd_set_gain_dual cmd;
+
+ dev_dbg(&drv->pdev->dev, "Enter\n");
+
+ cmd.header.command_id = MMX_SET_GAIN;
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.gain_cell_num = 1;
+
+ if (mute || gv->mute) {
+ cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+ cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+ } else {
+ cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+ cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+ }
+
+ SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+ loc_id, module_id);
+ cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+ cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+ - sizeof(struct sst_dsp_header);
+
+ /* we are with lock held, so call the unlocked api to send */
+ return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+ SST_FLAG_BLOCKED, task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+ struct sst_gain_value *gv = mc->gain_val;
+
+ switch (mc->type) {
+ case SST_GAIN_TLV:
+ ucontrol->value.integer.value[0] = gv->l_gain;
+ ucontrol->value.integer.value[1] = gv->r_gain;
+ break;
+
+ case SST_GAIN_MUTE:
+ ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
+ break;
+
+ case SST_GAIN_RAMP_DURATION:
+ ucontrol->value.integer.value[0] = gv->ramp_duration;
+ break;
+
+ default:
+ dev_err(component->dev, "Invalid Input- gain type:%d\n",
+ mc->type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret = 0;
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+ struct sst_gain_value *gv = mc->gain_val;
+
+ mutex_lock(&drv->lock);
+
+ switch (mc->type) {
+ case SST_GAIN_TLV:
+ gv->l_gain = ucontrol->value.integer.value[0];
+ gv->r_gain = ucontrol->value.integer.value[1];
+ dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+ mc->pname, gv->l_gain, gv->r_gain);
+ break;
+
+ case SST_GAIN_MUTE:
+ gv->mute = !!ucontrol->value.integer.value[0];
+ dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+ break;
+
+ case SST_GAIN_RAMP_DURATION:
+ gv->ramp_duration = ucontrol->value.integer.value[0];
+ dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+ mc->pname, gv->ramp_duration);
+ break;
+
+ default:
+ mutex_unlock(&drv->lock);
+ dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+ mc->type);
+ return -EINVAL;
+ }
+
+ if (mc->w && mc->w->power)
+ ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+ mc->pipe_id | mc->instance_id, mc->module_id, 0);
+ mutex_unlock(&drv->lock);
+
+ return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+ struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ mutex_lock(&drv->lock);
+ sst_find_and_send_pipe_algo(drv, w->name, ids);
+ sst_set_pipe_gain(ids, drv, 0);
+ mutex_unlock(&drv->lock);
+
+ return 0;
+}
+
+static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ return sst_send_pipe_module_params(w, k);
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+ [SST_IP_CODEC0] = SST_SWM_IN_CODEC0,
+ [SST_IP_CODEC1] = SST_SWM_IN_CODEC1,
+ [SST_IP_LOOP0] = SST_SWM_IN_SPROT_LOOP,
+ [SST_IP_LOOP1] = SST_SWM_IN_MEDIA_LOOP1,
+ [SST_IP_LOOP2] = SST_SWM_IN_MEDIA_LOOP2,
+ [SST_IP_PCM0] = SST_SWM_IN_PCM0,
+ [SST_IP_PCM1] = SST_SWM_IN_PCM1,
+ [SST_IP_MEDIA0] = SST_SWM_IN_MEDIA0,
+ [SST_IP_MEDIA1] = SST_SWM_IN_MEDIA1,
+ [SST_IP_MEDIA2] = SST_SWM_IN_MEDIA2,
+ [SST_IP_MEDIA3] = SST_SWM_IN_MEDIA3,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ *
+ * The register value is a bit-field inicated which mixer inputs are ON. Use the
+ * lookup table to get the input-id and fill it in the structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+ struct swm_input_ids *swm_input, unsigned int reg)
+{
+ uint i, is_set, nb_inputs = 0;
+ u16 input_loc_id;
+
+ dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+ for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+ is_set = reg & BIT(i);
+ if (!is_set)
+ continue;
+
+ input_loc_id = swm_mixer_input_ids[i];
+ SST_FILL_DESTINATION(2, swm_input->input_id,
+ input_loc_id, SST_DEFAULT_MODULE_ID);
+ nb_inputs++;
+ swm_input++;
+ dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+ input_loc_id, nb_inputs);
+
+ if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+ dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+ break;
+ }
+ }
+ return nb_inputs;
+}
+
+
+/**
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+ struct sst_data *drv, int mute)
+{
+ int ret = 0;
+ struct sst_gain_mixer_control *mc;
+ struct sst_gain_value *gv;
+ struct sst_module *gain = NULL;
+
+ list_for_each_entry(gain, &ids->gain_list, node) {
+ struct snd_kcontrol *kctl = gain->kctl;
+
+ dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+ mc = (void *)kctl->private_value;
+ gv = mc->gain_val;
+
+ ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+ mc->pipe_id | mc->instance_id, mc->module_id, mute);
+ if (ret)
+ return ret;
+ }
+ return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct sst_cmd_set_swm cmd;
+ struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+ struct sst_ids *ids = w->priv;
+ bool set_mixer = false;
+ struct soc_mixer_control *mc;
+ int val = 0;
+ int i = 0;
+
+ dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+ /*
+ * Identify which mixer input is on and send the bitmap of the
+ * inputs as an IPC to the DSP.
+ */
+ for (i = 0; i < w->num_kcontrols; i++) {
+ if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+ mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+ val |= 1 << mc->shift;
+ }
+ }
+ dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ set_mixer = true;
+ break;
+ case SND_SOC_DAPM_POST_REG:
+ if (w->power)
+ set_mixer = true;
+ break;
+ default:
+ set_mixer = false;
+ }
+
+ if (set_mixer == false)
+ return 0;
+
+ if (SND_SOC_DAPM_EVENT_ON(event) ||
+ event == SND_SOC_DAPM_POST_REG)
+ cmd.switch_state = SST_SWM_ON;
+ else
+ cmd.switch_state = SST_SWM_OFF;
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ /* MMX_SET_SWM == SBA_SET_SWM */
+ cmd.header.command_id = SBA_SET_SWM;
+
+ SST_FILL_DESTINATION(2, cmd.output_id,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+ cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);
+ cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
+ - sizeof(struct sst_dsp_header)
+ + (cmd.nb_inputs * sizeof(cmd.input[0]));
+
+ return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ ids->task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name) \
+ static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0), \
+ SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0), \
+ SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0), \
+ SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0), \
+ SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0), \
+ SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0), \
+ SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0), \
+ }
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name) \
+ { mix_name, "codec_in0 Switch", "codec_in0" }, \
+ { mix_name, "codec_in1 Switch", "codec_in1" }, \
+ { mix_name, "sprot_loop_in Switch", "sprot_loop_in" }, \
+ { mix_name, "media_loop1_in Switch", "media_loop1_in" }, \
+ { mix_name, "media_loop2_in Switch", "media_loop2_in" }, \
+ { mix_name, "pcm0_in Switch", "pcm0_in" }, \
+ { mix_name, "pcm1_in Switch", "pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name) \
+ static const struct snd_kcontrol_new kctl_name[] = { \
+ SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
+ SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
+ SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
+ SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
+ }
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+ int ret = 0;
+ struct sst_cmd_generic cmd;
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ static int timer_usage;
+
+ if (enable)
+ cmd.header.command_id = SBA_VB_START;
+ else
+ cmd.header.command_id = SBA_IDLE;
+ dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.length = 0;
+
+ if (enable) {
+ ret = sst->ops->power(sst->dev, true);
+ if (ret < 0)
+ return ret;
+ }
+
+ mutex_lock(&drv->lock);
+ if (enable)
+ timer_usage++;
+ else
+ timer_usage--;
+
+ /*
+ * Send the command only if this call is the first enable or last
+ * disable
+ */
+ if ((enable && (timer_usage == 1)) ||
+ (!enable && (timer_usage == 0))) {
+ ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
+ SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+ if (ret && enable) {
+ timer_usage--;
+ enable = false;
+ }
+ }
+ mutex_unlock(&drv->lock);
+
+ if (!enable)
+ sst->ops->power(sst->dev, false);
+ return ret;
+}
+
+/**
+ * sst_ssp_config - contains SSP configuration for media UC
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+ .ssp_id = SSP_CODEC,
+ .bits_per_slot = 24,
+ .slots = 4,
+ .ssp_mode = SSP_MODE_MASTER,
+ .pcm_mode = SSP_PCM_MODE_NETWORK,
+ .duplex = SSP_DUPLEX,
+ .ssp_protocol = SSP_MODE_PCM,
+ .fs_width = 1,
+ .fs_frequency = SSP_FS_48_KHZ,
+ .active_slot_map = 0xF,
+ .start_delay = 0,
+};
+
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
+{
+ struct sst_cmd_sba_hw_set_ssp cmd;
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ const struct sst_ssp_config *config;
+
+ dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+
+ SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+ cmd.header.command_id = SBA_HW_SET_SSP;
+ cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+ - sizeof(struct sst_dsp_header);
+
+ config = &sst_ssp_configs;
+ dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+
+ if (enable)
+ cmd.switch_state = SST_SWITCH_ON;
+ else
+ cmd.switch_state = SST_SWITCH_OFF;
+
+ cmd.selection = config->ssp_id;
+ cmd.nb_bits_per_slots = config->bits_per_slot;
+ cmd.nb_slots = config->slots;
+ cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+ cmd.duplex = config->duplex;
+ cmd.active_tx_slot_map = config->active_slot_map;
+ cmd.active_rx_slot_map = config->active_slot_map;
+ cmd.frame_sync_frequency = config->fs_frequency;
+ cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
+ cmd.data_polarity = 1;
+ cmd.frame_sync_width = config->fs_width;
+ cmd.ssp_protocol = config->ssp_protocol;
+ cmd.start_delay = config->start_delay;
+ cmd.reserved1 = cmd.reserved2 = 0xFF;
+
+ return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+ dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = sst_send_slot_map(drv);
+ if (ret)
+ return ret;
+ ret = sst_send_pipe_module_params(w, k);
+ }
+ return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct sst_cmd_set_media_path cmd;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(c->dev, "widget=%s\n", w->name);
+ dev_dbg(c->dev, "task=%u, location=%#x\n",
+ ids->task_id, ids->location_id);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ cmd.switch_state = SST_PATH_ON;
+ else
+ cmd.switch_state = SST_PATH_OFF;
+
+ SST_FILL_DESTINATION(2, cmd.header.dst,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+
+ /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+ cmd.header.command_id = MMX_SET_MEDIA_PATH;
+ cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+ - sizeof(struct sst_dsp_header);
+
+ ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ ids->task_id, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+ if (ret)
+ return ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = sst_send_pipe_module_params(w, k);
+ return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct sst_cmd_sba_set_media_loop_map cmd;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_data *drv = snd_soc_component_get_drvdata(c);
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ cmd.switch_state = SST_SWITCH_ON;
+ else
+ cmd.switch_state = SST_SWITCH_OFF;
+
+ SST_FILL_DESTINATION(2, cmd.header.dst,
+ ids->location_id, SST_DEFAULT_MODULE_ID);
+
+ cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+ cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+ - sizeof(struct sst_dsp_header);
+ cmd.param.part.cfg.rate = 2; /* 48khz */
+
+ cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+ cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+ cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+ ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+ SST_TASK_SBA, 0, &cmd,
+ sizeof(cmd.header) + cmd.header.length);
+ if (ret)
+ return ret;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = sst_send_pipe_module_params(w, k);
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+ SST_AIF_IN("codec_in0", sst_set_be_modules),
+ SST_AIF_IN("codec_in1", sst_set_be_modules),
+ SST_AIF_OUT("codec_out0", sst_set_be_modules),
+ SST_AIF_OUT("codec_out1", sst_set_be_modules),
+
+ /* Media Paths */
+ /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
+ SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
+ SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
+ SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
+ SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
+ SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
+ SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
+
+ /* SBA PCM Paths */
+ SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
+ SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
+ SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
+ SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
+ SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
+
+ /* SBA Loops */
+ SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
+ SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
+ SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
+ SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+ SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
+
+ /* Media Mixers */
+ SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+ sst_mix_media0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+ sst_mix_media1_controls, sst_swm_mixer_event),
+
+ /* SBA PCM mixers */
+ SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+ sst_mix_pcm0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+ sst_mix_pcm1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+ sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+ /* SBA Loop mixers */
+ SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+ sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+ sst_mix_media_l1_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+ sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+ /* SBA Backend mixers */
+ SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+ sst_mix_codec0_controls, sst_swm_mixer_event),
+ SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+ sst_mix_codec1_controls, sst_swm_mixer_event),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+ {"media0_in", NULL, "Compress Playback"},
+ {"media1_in", NULL, "Headset Playback"},
+ {"media2_in", NULL, "pcm0_out"},
+
+ {"media0_out mix 0", "media0_in Switch", "media0_in"},
+ {"media0_out mix 0", "media1_in Switch", "media1_in"},
+ {"media0_out mix 0", "media2_in Switch", "media2_in"},
+ {"media0_out mix 0", "media3_in Switch", "media3_in"},
+ {"media1_out mix 0", "media0_in Switch", "media0_in"},
+ {"media1_out mix 0", "media1_in Switch", "media1_in"},
+ {"media1_out mix 0", "media2_in Switch", "media2_in"},
+ {"media1_out mix 0", "media3_in Switch", "media3_in"},
+
+ {"media0_out", NULL, "media0_out mix 0"},
+ {"media1_out", NULL, "media1_out mix 0"},
+ {"pcm0_in", NULL, "media0_out"},
+ {"pcm1_in", NULL, "media1_out"},
+
+ {"Headset Capture", NULL, "pcm1_out"},
+ {"Headset Capture", NULL, "pcm2_out"},
+ {"pcm0_out", NULL, "pcm0_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
+ {"pcm1_out", NULL, "pcm1_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
+ {"pcm2_out", NULL, "pcm2_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
+
+ {"media_loop1_in", NULL, "media_loop1_out"},
+ {"media_loop1_out", NULL, "media_loop1_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
+ {"media_loop2_in", NULL, "media_loop2_out"},
+ {"media_loop2_out", NULL, "media_loop2_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
+ {"sprot_loop_in", NULL, "sprot_loop_out"},
+ {"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
+
+ {"codec_out0", NULL, "codec_out0 mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
+ {"codec_out1", NULL, "codec_out1 mix 0"},
+ SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+
+};
+static const char * const slot_names[] = {
+ "none",
+ "slot 0", "slot 1", "slot 2", "slot 3",
+ "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
+};
+
+static const char * const channel_names[] = {
+ "none",
+ "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
+ "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
+};
+
+#define SST_INTERLEAVER(xpname, slot_name, slotno) \
+ SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
+ channel_names, sst_slot_get, sst_slot_put)
+
+#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
+ SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
+ slot_names, sst_slot_get, sst_slot_put)
+
+static const struct snd_kcontrol_new sst_slot_controls[] = {
+ SST_INTERLEAVER("codec_out", "slot 0", 0),
+ SST_INTERLEAVER("codec_out", "slot 1", 1),
+ SST_INTERLEAVER("codec_out", "slot 2", 2),
+ SST_INTERLEAVER("codec_out", "slot 3", 3),
+ SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
+ SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
+ SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
+ SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
+};
+
+/* Gain helper with min/max set */
+#define SST_GAIN(name, path_id, task_id, instance, gain_var) \
+ SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
+ SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
+ sst_gain_get, sst_gain_put, \
+ SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id, \
+ sst_gain_tlv_common, gain_var)
+
+#define SST_VOLUME(name, path_id, task_id, instance, gain_var) \
+ SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE, \
+ SST_GAIN_TC_MIN, SST_GAIN_TC_MAX, \
+ sst_gain_get, sst_gain_put, \
+ SST_MODULE_ID_VOLUME, path_id, instance, task_id, \
+ sst_gain_tlv_common, gain_var)
+
+static struct sst_gain_value sst_gains[];
+
+static const struct snd_kcontrol_new sst_gain_controls[] = {
+ SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
+ SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
+ SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
+ SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
+
+ SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
+ SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
+ SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
+ SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+ SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+ SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+ SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+ SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+ SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+ SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+ SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+ SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
static const struct snd_kcontrol_new sst_algo_controls[] = {
SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
@@ -198,21 +1143,280 @@ static int sst_algo_control_init(struct device *dev)
return 0;
}
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+ switch (w->id) {
+ case snd_soc_dapm_pga:
+ case snd_soc_dapm_aif_in:
+ case snd_soc_dapm_aif_out:
+ case snd_soc_dapm_input:
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_mixer:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+ struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p = NULL;
+
+ dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_dbg(dai->dev, "Stream name=%s\n",
+ dai->playback_widget->name);
+ w = dai->playback_widget;
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
+ if (p->connect && p->sink->power &&
+ is_sst_dapm_widget(p->sink)) {
+ struct sst_ids *ids = p->sink->priv;
+
+ dev_dbg(dai->dev, "send gains for widget=%s\n",
+ p->sink->name);
+ mutex_lock(&drv->lock);
+ sst_set_pipe_gain(ids, drv, mute);
+ mutex_unlock(&drv->lock);
+ }
+ }
+ } else {
+ dev_dbg(dai->dev, "Stream name=%s\n",
+ dai->capture_widget->name);
+ w = dai->capture_widget;
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->connected && !p->connected(w, p->sink))
+ continue;
+
+ if (p->connect && p->source->power &&
+ is_sst_dapm_widget(p->source)) {
+ struct sst_ids *ids = p->source->priv;
+
+ dev_dbg(dai->dev, "send gain for widget=%s\n",
+ p->source->name);
+ mutex_lock(&drv->lock);
+ sst_set_pipe_gain(ids, drv, mute);
+ mutex_unlock(&drv->lock);
+ }
+ }
+ }
+ return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ *
+ *
+ * Fills the widget pointer in the kcontrol private data, and also fills the
+ * kcontrol pointer in the widget private data.
+ *
+ * Widget pointer is used to send the algo/gain in the .put() handler if the
+ * widget is powerd on.
+ *
+ * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
+ * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
+ */
+static int sst_fill_module_list(struct snd_kcontrol *kctl,
+ struct snd_soc_dapm_widget *w, int type)
{
+ struct sst_module *module = NULL;
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct sst_ids *ids = w->priv;
int ret = 0;
+
+ module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return -ENOMEM;
+
+ if (type == SST_MODULE_GAIN) {
+ struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+ mc->w = w;
+ module->kctl = kctl;
+ list_add_tail(&module->node, &ids->gain_list);
+ } else if (type == SST_MODULE_ALGO) {
+ struct sst_algo_control *bc = (void *)kctl->private_value;
+
+ bc->w = w;
+ module->kctl = kctl;
+ list_add_tail(&module->node, &ids->algo_list);
+ } else {
+ dev_err(c->dev, "invoked for unknown type %d module %s",
+ type, kctl->id.name);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @widget: pipe modelled as a DAPM widget
+ *
+ * Fill the list of gains/algos for the widget by looking at all the card
+ * controls and comparing the name of the widget with the first part of control
+ * name. First part of control name contains the pipe name (widget name).
+ */
+static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
+ struct snd_soc_platform *platform)
+{
+ struct snd_kcontrol *kctl;
+ int index, ret = 0;
+ struct snd_card *card = platform->component.card->snd_card;
+ char *idx;
+
+ down_read(&card->controls_rwsem);
+
+ list_for_each_entry(kctl, &card->controls, list) {
+ idx = strstr(kctl->id.name, " ");
+ if (idx == NULL)
+ continue;
+ index = strlen(kctl->id.name) - strlen(idx);
+
+ if (strstr(kctl->id.name, "Volume") &&
+ !strncmp(kctl->id.name, w->name, index))
+ ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+ else if (strstr(kctl->id.name, "params") &&
+ !strncmp(kctl->id.name, w->name, index))
+ ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+ else if (strstr(kctl->id.name, "Switch") &&
+ !strncmp(kctl->id.name, w->name, index) &&
+ strstr(kctl->id.name, "Gain")) {
+ struct sst_gain_mixer_control *mc =
+ (void *)kctl->private_value;
+
+ mc->w = w;
+
+ } else if (strstr(kctl->id.name, "interleaver") &&
+ !strncmp(kctl->id.name, w->name, index)) {
+ struct sst_enum *e = (void *)kctl->private_value;
+
+ e->w = w;
+
+ } else if (strstr(kctl->id.name, "deinterleaver") &&
+ !strncmp(kctl->id.name, w->name, index)) {
+
+ struct sst_enum *e = (void *)kctl->private_value;
+
+ e->w = w;
+ }
+
+ if (ret < 0) {
+ up_read(&card->controls_rwsem);
+ return ret;
+ }
+ }
+
+ up_read(&card->controls_rwsem);
+ return 0;
+}
+
+/**
+ * sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ */
+static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+ struct sst_ids *ids)
+{
+ struct snd_soc_dapm_widget *w;
+ unsigned int len = strlen(ids->parent_wname);
+
+ list_for_each_entry(w, &platform->component.card->widgets, list) {
+ if (!strncmp(ids->parent_wname, w->name, len)) {
+ ids->parent_w = w;
+ break;
+ }
+ }
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+{
+ struct snd_soc_dapm_widget *w;
+ int ret = 0;
+
+ list_for_each_entry(w, &platform->component.card->widgets, list) {
+ if (is_sst_dapm_widget(w) && (w->priv)) {
+ struct sst_ids *ids = w->priv;
+
+ dev_dbg(platform->dev, "widget type=%d name=%s\n",
+ w->id, w->name);
+ INIT_LIST_HEAD(&ids->algo_list);
+ INIT_LIST_HEAD(&ids->gain_list);
+ ret = sst_fill_widget_module_info(w, platform);
+
+ if (ret < 0)
+ return ret;
+
+ /* fill linked widgets */
+ if (ids->parent_wname != NULL)
+ sst_fill_linked_widgets(platform, ids);
+ }
+ }
+ return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+ int i, ret = 0;
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_get_dapm(&platform->component);
struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+ unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
drv->byte_stream = devm_kzalloc(platform->dev,
SST_MAX_BIN_BYTES, GFP_KERNEL);
if (!drv->byte_stream)
return -ENOMEM;
- /*Initialize algo control params*/
+ snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+ ARRAY_SIZE(sst_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm, intercon,
+ ARRAY_SIZE(intercon));
+ snd_soc_dapm_new_widgets(dapm->card);
+
+ for (i = 0; i < gains; i++) {
+ sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
+ sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
+ sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
+ sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
+ }
+
+ ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+ ARRAY_SIZE(sst_gain_controls));
+ if (ret)
+ return ret;
+
+ /* Initialize algo control params */
ret = sst_algo_control_init(platform->dev);
if (ret)
return ret;
ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
ARRAY_SIZE(sst_algo_controls));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+ ARRAY_SIZE(sst_slot_controls));
+ if (ret)
+ return ret;
+
+ ret = sst_map_modules_to_pipe(platform);
+
return ret;
}
diff --git a/sound/soc/intel/sst-atom-controls.h b/sound/soc/intel/sst-atom-controls.h
index a73e894b175c..dfebfdd5eb2a 100644
--- a/sound/soc/intel/sst-atom-controls.h
+++ b/sound/soc/intel/sst-atom-controls.h
@@ -23,6 +23,9 @@
#ifndef __SST_ATOM_CONTROLS_H__
#define __SST_ATOM_CONTROLS_H__
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
enum {
MERR_DPCM_AUDIO = 0,
MERR_DPCM_COMPR,
@@ -360,16 +363,416 @@ struct sst_dsp_header {
struct sst_cmd_generic {
struct sst_dsp_header header;
} __packed;
+
+struct swm_input_ids {
+ struct sst_destination_id input_id;
+} __packed;
+
+struct sst_cmd_set_swm {
+ struct sst_dsp_header header;
+ struct sst_destination_id output_id;
+ u16 switch_state;
+ u16 nb_inputs;
+ struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
+} __packed;
+
+struct sst_cmd_set_media_path {
+ struct sst_dsp_header header;
+ u16 switch_state;
+} __packed;
+
+struct pcm_cfg {
+ u8 s_length:2;
+ u8 rate:3;
+ u8 format:3;
+} __packed;
+
+struct sst_cmd_set_speech_path {
+ struct sst_dsp_header header;
+ u16 switch_state;
+ struct {
+ u16 rsvd:8;
+ struct pcm_cfg cfg;
+ } config;
+} __packed;
+
+struct gain_cell {
+ struct sst_destination_id dest;
+ s16 cell_gain_left;
+ s16 cell_gain_right;
+ u16 gain_time_constant;
+} __packed;
+
+#define NUM_GAIN_CELLS 1
+struct sst_cmd_set_gain_dual {
+ struct sst_dsp_header header;
+ u16 gain_cell_num;
+ struct gain_cell cell_gains[NUM_GAIN_CELLS];
+} __packed;
struct sst_cmd_set_params {
struct sst_destination_id dst;
u16 command_id;
char params[0];
} __packed;
+
+
+struct sst_cmd_sba_vb_start {
+ struct sst_dsp_header header;
+} __packed;
+
+union sba_media_loop_params {
+ struct {
+ u16 rsvd:8;
+ struct pcm_cfg cfg;
+ } part;
+ u16 full;
+} __packed;
+
+struct sst_cmd_sba_set_media_loop_map {
+ struct sst_dsp_header header;
+ u16 switch_state;
+ union sba_media_loop_params param;
+ u16 map;
+} __packed;
+
+struct sst_cmd_tone_stop {
+ struct sst_dsp_header header;
+ u16 switch_state;
+} __packed;
+
+enum sst_ssp_mode {
+ SSP_MODE_MASTER = 0,
+ SSP_MODE_SLAVE = 1,
+};
+
+enum sst_ssp_pcm_mode {
+ SSP_PCM_MODE_NORMAL = 0,
+ SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+ SSP_DUPLEX = 0,
+ SSP_RX = 1,
+ SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+ SSP_FS_8_KHZ = 0,
+ SSP_FS_16_KHZ = 1,
+ SSP_FS_44_1_KHZ = 2,
+ SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+ SSP_FS_ACTIVE_LOW = 0,
+ SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+ SSP_MODE_PCM = 0,
+ SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+ SSP_MODEM = 0,
+ SSP_BT = 1,
+ SSP_FM = 2,
+ SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+ struct sst_dsp_header header;
+ u16 selection; /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+ u16 switch_state;
+
+ u16 nb_bits_per_slots:6; /* 0-32 bits, 24 (def) */
+ u16 nb_slots:4; /* 0-8: slots per frame */
+ u16 mode:3; /* 0:Master, 1: Slave */
+ u16 duplex:3;
+
+ u16 active_tx_slot_map:8; /* Bit map, 0:off, 1:on */
+ u16 reserved1:8;
+
+ u16 active_rx_slot_map:8; /* Bit map 0: Off, 1:On */
+ u16 reserved2:8;
+
+ u16 frame_sync_frequency;
+
+ u16 frame_sync_polarity:8;
+ u16 data_polarity:8;
+
+ u16 frame_sync_width; /* 1 to N clocks */
+ u16 ssp_protocol:8;
+ u16 start_delay:8; /* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+ struct sst_dsp_header header;
+
+ u16 param_id;
+ u16 param_len;
+ u16 ssp_index;
+
+ u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+ u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+ SST_PROBE_EXTRACTOR = 0,
+ SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#define SST_MODULE_GAIN 1
+#define SST_MODULE_ALGO 2
+
+#define SST_FMT_MONO 0
+#define SST_FMT_STEREO 3
+
+/* physical SSP numbers */
+enum {
+ SST_SSP0 = 0,
+ SST_SSP1,
+ SST_SSP2,
+ SST_SSP_LAST = SST_SSP2,
+};
+
+#define SST_NUM_SSPS (SST_SSP_LAST + 1) /* physical SSPs */
+#define SST_MAX_SSP_MUX 2 /* single SSP muxed between pipes */
+#define SST_MAX_SSP_DOMAINS 2 /* domains present in each pipe */
+
+struct sst_module {
+ struct snd_kcontrol *kctl;
+ struct list_head node;
+};
+
+struct sst_ssp_config {
+ u8 ssp_id;
+ u8 bits_per_slot;
+ u8 slots;
+ u8 ssp_mode;
+ u8 pcm_mode;
+ u8 duplex;
+ u8 ssp_protocol;
+ u8 fs_frequency;
+ u8 active_slot_map;
+ u8 start_delay;
+ u16 fs_width;
+};
+
+struct sst_ssp_cfg {
+ const u8 ssp_number;
+ const int *mux_shift;
+ const int (*domain_shift)[SST_MAX_SSP_MUX];
+ const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
+};
+
+struct sst_ids {
+ u16 location_id;
+ u16 module_id;
+ u8 task_id;
+ u8 format;
+ u8 reg;
+ const char *parent_wname;
+ struct snd_soc_dapm_widget *parent_w;
+ struct list_head algo_list;
+ struct list_head gain_list;
+ const struct sst_pcm_format *pcm_fmt;
+};
+
+
+#define SST_AIF_IN(wname, wevent) \
+{ .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_AIF_OUT(wname, wevent) \
+{ .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_INPUT(wname, wevent) \
+{ .id = snd_soc_dapm_input, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_OUTPUT(wname, wevent) \
+{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 } \
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent) \
+{ .id = snd_soc_dapm_output, .name = wname, .sname = NULL, \
+ .reg = SND_SOC_NOPM, .shift = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
+ .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+ .pcm_fmt = wformat, } \
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .on_val = 1, .off_val = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .parent_wname = linked_wname} \
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = NULL, .num_kcontrols = 0, \
+ .event = wevent, .event_flags = wflags, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .format = wformat,} \
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event) \
+ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event) \
+ SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event) \
+ SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event) \
+ SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event, \
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event) \
+ SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent) \
+{ .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0, \
+ .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+ .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD | \
+ SND_SOC_DAPM_POST_REG, \
+ .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, \
+ .reg = wreg } \
+}
+
+enum sst_gain_kcontrol_type {
+ SST_GAIN_TLV,
+ SST_GAIN_MUTE,
+ SST_GAIN_RAMP_DURATION,
+};
+
+struct sst_gain_mixer_control {
+ bool stereo;
+ enum sst_gain_kcontrol_type type;
+ struct sst_gain_value *gain_val;
+ int max;
+ int min;
+ u16 instance_id;
+ u16 module_id;
+ u16 pipe_id;
+ u16 task_id;
+ char pname[44];
+ struct snd_soc_dapm_widget *w;
+};
+
+struct sst_gain_value {
+ u16 ramp_duration;
+ s16 l_gain;
+ s16 r_gain;
+ bool mute;
+};
+#define SST_GAIN_VOLUME_DEFAULT (-1440)
+#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
+#define SST_GAIN_MUTE_DEFAULT true
+
+#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
+ xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
+ xmin, xmax, xpname) \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+ SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+ .tlv.p = (tlv_array), \
+ .info = sst_gain_ctl_info,\
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+ { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
+ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
+ xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
+ xmin, xmax, xpname) \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = sst_gain_ctl_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+ { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
+ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
+ xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_info_bool_ext, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+ { .stereo = false, .type = SST_GAIN_MUTE, \
+ .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+ .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
#define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
xpname " " xmname " " #xinstance " " xtype
#define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+
+/*
+ * 3 Controls for each Gain module
+ * e.g. - pcm0_in Gain 0 Volume
+ * - pcm0_in Gain 0 Ramp Delay
+ * - pcm0_in Gain 0 Switch
+ */
+#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
+ xhandler_get, xhandler_put, \
+ xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
+ { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
+ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
+ xgain_val, xmin_tc, xmax_tc, xpname) }, \
+ { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
+ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
+ xgain_val, xpname) } ,\
+ { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
+ xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
+ xgain_val, xmin_gain, xmax_gain, xpname) }
+
+#define SST_GAIN_TC_MIN 5
+#define SST_GAIN_TC_MAX 5000
+#define SST_GAIN_MIN_VALUE -1440 /* in 0.1 DB units */
+#define SST_GAIN_MAX_VALUE 360
+
enum sst_algo_kcontrol_type {
SST_ALGO_PARAMS,
SST_ALGO_BYPASS,
@@ -439,4 +842,29 @@ struct sst_enum {
struct snd_soc_dapm_widget *w;
};
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+ (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+ xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+ .info = sst_slot_enum_info, \
+ .get = xget, .put = xput, \
+ .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+ xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+ (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+ SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+ SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
#endif
diff --git a/sound/soc/intel/sst-baytrail-dsp.c b/sound/soc/intel/sst-baytrail-dsp.c
index fc588764ffa3..5a9e56700f31 100644
--- a/sound/soc/intel/sst-baytrail-dsp.c
+++ b/sound/soc/intel/sst-baytrail-dsp.c
@@ -67,17 +67,12 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
{
struct dma_block_info *block;
struct sst_module *mod;
- struct sst_module_data block_data;
struct sst_module_template template;
int count;
memset(&template, 0, sizeof(template));
template.id = module->type;
template.entry = module->entry_point;
- template.p.type = SST_MEM_DRAM;
- template.p.data_type = SST_DATA_P;
- template.s.type = SST_MEM_DRAM;
- template.s.data_type = SST_DATA_S;
mod = sst_module_new(fw, &template, NULL);
if (mod == NULL)
@@ -94,19 +89,19 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
switch (block->type) {
case SST_BYT_IRAM:
- block_data.offset = block->ram_offset +
+ mod->offset = block->ram_offset +
dsp->addr.iram_offset;
- block_data.type = SST_MEM_IRAM;
+ mod->type = SST_MEM_IRAM;
break;
case SST_BYT_DRAM:
- block_data.offset = block->ram_offset +
+ mod->offset = block->ram_offset +
dsp->addr.dram_offset;
- block_data.type = SST_MEM_DRAM;
+ mod->type = SST_MEM_DRAM;
break;
case SST_BYT_CACHE:
- block_data.offset = block->ram_offset +
+ mod->offset = block->ram_offset +
(dsp->addr.fw_ext - dsp->addr.lpe);
- block_data.type = SST_MEM_CACHE;
+ mod->type = SST_MEM_CACHE;
break;
default:
dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
@@ -114,11 +109,10 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
return -EINVAL;
}
- block_data.size = block->size;
- block_data.data_type = SST_DATA_M;
- block_data.data = (void *)block + sizeof(*block);
+ mod->size = block->size;
+ mod->data = (void *)block + sizeof(*block);
- sst_module_insert_fixed_block(mod, &block_data);
+ sst_module_alloc_blocks(mod);
block = (void *)block + sizeof(*block) + block->size;
}
diff --git a/sound/soc/intel/sst-dsp-priv.h b/sound/soc/intel/sst-dsp-priv.h
index ffb308bd81ce..b9da030e312d 100644
--- a/sound/soc/intel/sst-dsp-priv.h
+++ b/sound/soc/intel/sst-dsp-priv.h
@@ -26,6 +26,9 @@ struct sst_mem_block;
struct sst_module;
struct sst_fw;
+/* do we need to remove or keep */
+#define DSP_DRAM_ADDR_OFFSET 0x400000
+
/*
* DSP Operations exported by platform Audio DSP driver.
*/
@@ -33,6 +36,9 @@ struct sst_ops {
/* DSP core boot / reset */
void (*boot)(struct sst_dsp *);
void (*reset)(struct sst_dsp *);
+ int (*wake)(struct sst_dsp *);
+ void (*sleep)(struct sst_dsp *);
+ void (*stall)(struct sst_dsp *);
/* Shim IO */
void (*write)(void __iomem *addr, u32 offset, u32 value);
@@ -67,6 +73,8 @@ struct sst_addr {
u32 shim_offset;
u32 iram_offset;
u32 dram_offset;
+ u32 dsp_iram_offset;
+ u32 dsp_dram_offset;
void __iomem *lpe;
void __iomem *shim;
void __iomem *pci_cfg;
@@ -84,15 +92,6 @@ struct sst_mailbox {
};
/*
- * Audio DSP Firmware data types.
- */
-enum sst_data_type {
- SST_DATA_M = 0, /* module block data */
- SST_DATA_P = 1, /* peristant data (text, data) */
- SST_DATA_S = 2, /* scratch data (usually buffers) */
-};
-
-/*
* Audio DSP memory block types.
*/
enum sst_mem_type {
@@ -125,23 +124,6 @@ struct sst_fw {
};
/*
- * Audio DSP Generic Module data.
- *
- * This is used to dsecribe any sections of persistent (text and data) and
- * scratch (buffers) of module data in ADSP memory space.
- */
-struct sst_module_data {
-
- enum sst_mem_type type; /* destination memory type */
- enum sst_data_type data_type; /* type of module data */
-
- u32 size; /* size in bytes */
- int32_t offset; /* offset in FW file */
- u32 data_offset; /* offset in ADSP memory space */
- void *data; /* module data */
-};
-
-/*
* Audio DSP Generic Module Template.
*
* Used to define and register a new FW module. This data is extracted from
@@ -150,15 +132,52 @@ struct sst_module_data {
struct sst_module_template {
u32 id;
u32 entry; /* entry point */
- struct sst_module_data s; /* scratch data */
- struct sst_module_data p; /* peristant data */
+ u32 scratch_size;
+ u32 persistent_size;
+};
+
+/*
+ * Block Allocator - Used to allocate blocks of DSP memory.
+ */
+struct sst_block_allocator {
+ u32 id;
+ u32 offset;
+ int size;
+ enum sst_mem_type type;
+};
+
+/*
+ * Runtime Module Instance - A module object can be instanciated multiple
+ * times within the DSP FW.
+ */
+struct sst_module_runtime {
+ struct sst_dsp *dsp;
+ int id;
+ struct sst_module *module; /* parent module we belong too */
+
+ u32 persistent_offset; /* private memory offset */
+ void *private;
+
+ struct list_head list;
+ struct list_head block_list; /* list of blocks used */
+};
+
+/*
+ * Runtime Module Context - The runtime context must be manually stored by the
+ * driver prior to enter S3 and restored after leaving S3. This should really be
+ * part of the memory context saved by the enter D3 message IPC ???
+ */
+struct sst_module_runtime_context {
+ dma_addr_t dma_buffer;
+ u32 *buffer;
};
/*
* Audio DSP Generic Module.
*
* Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module.
+ * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
+ * can be instanciated multiple times in the DSP.
*/
struct sst_module {
struct sst_dsp *dsp;
@@ -167,10 +186,13 @@ struct sst_module {
/* module configuration */
u32 id;
u32 entry; /* module entry point */
- u32 offset; /* module offset in firmware file */
+ s32 offset; /* module offset in firmware file */
u32 size; /* module size */
- struct sst_module_data s; /* scratch data */
- struct sst_module_data p; /* peristant data */
+ u32 scratch_size; /* global scratch memory required */
+ u32 persistent_size; /* private memory required */
+ enum sst_mem_type type; /* destination memory type */
+ u32 data_offset; /* offset in ADSP memory space */
+ void *data; /* module data */
/* runtime */
u32 usage_count; /* can be unloaded if count == 0 */
@@ -180,6 +202,7 @@ struct sst_module {
struct list_head block_list; /* Module list of blocks in use */
struct list_head list; /* DSP list of modules */
struct list_head list_fw; /* FW list of modules */
+ struct list_head runtime_list; /* list of runtime module objects*/
};
/*
@@ -208,7 +231,6 @@ struct sst_mem_block {
struct sst_block_ops *ops; /* block operations, if any */
/* block status */
- enum sst_data_type data_type; /* data type held in this block */
u32 bytes_used; /* bytes in use by modules */
void *private; /* generic core does not touch this */
int users; /* number of modules using this block */
@@ -253,6 +275,11 @@ struct sst_dsp {
struct list_head module_list;
struct list_head fw_list;
+ /* scratch buffer */
+ struct list_head scratch_block_list;
+ u32 scratch_offset;
+ u32 scratch_size;
+
/* platform data */
struct sst_pdata *pdata;
@@ -290,18 +317,33 @@ void sst_fw_unload(struct sst_fw *sst_fw);
/* Create/Free firmware modules */
struct sst_module *sst_module_new(struct sst_fw *sst_fw,
struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *sst_module);
-int sst_module_insert(struct sst_module *sst_module);
-int sst_module_remove(struct sst_module *sst_module);
-int sst_module_insert_fixed_block(struct sst_module *module,
- struct sst_module_data *data);
+void sst_module_free(struct sst_module *module);
struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-
-/* allocate/free pesistent/scratch memory regions managed by drv */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
- struct sst_module *scratch);
-int sst_block_module_remove(struct sst_module *module);
+int sst_module_alloc_blocks(struct sst_module *module);
+int sst_module_free_blocks(struct sst_module *module);
+
+/* Create/Free firmware module runtime instances */
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+ int id, void *private);
+void sst_module_runtime_free(struct sst_module_runtime *runtime);
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+ struct sst_module *module, u32 id);
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+ int offset);
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context);
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context);
+
+/* generic block allocation */
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list);
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
+
+/* scratch allocation */
+int sst_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_block_free_scratch(struct sst_dsp *dsp);
/* Register the DSPs memory blocks - would be nice to read from ACPI */
struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -309,4 +351,10 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
void *private);
void sst_mem_block_unregister_all(struct sst_dsp *dsp);
+/* Create/Free DMA resources */
+int sst_dma_new(struct sst_dsp *sst);
+void sst_dma_free(struct sst_dma *dma);
+
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+ enum sst_mem_type type);
#endif
diff --git a/sound/soc/intel/sst-dsp.c b/sound/soc/intel/sst-dsp.c
index cd23060a0d86..86e410845670 100644
--- a/sound/soc/intel/sst-dsp.c
+++ b/sound/soc/intel/sst-dsp.c
@@ -245,6 +245,29 @@ int sst_dsp_boot(struct sst_dsp *sst)
}
EXPORT_SYMBOL_GPL(sst_dsp_boot);
+int sst_dsp_wake(struct sst_dsp *sst)
+{
+ if (sst->ops->wake)
+ return sst->ops->wake(sst);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_wake);
+
+void sst_dsp_sleep(struct sst_dsp *sst)
+{
+ if (sst->ops->sleep)
+ sst->ops->sleep(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_sleep);
+
+void sst_dsp_stall(struct sst_dsp *sst)
+{
+ if (sst->ops->stall)
+ sst->ops->stall(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_stall);
+
void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
{
sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
@@ -352,6 +375,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
INIT_LIST_HEAD(&sst->free_block_list);
INIT_LIST_HEAD(&sst->module_list);
INIT_LIST_HEAD(&sst->fw_list);
+ INIT_LIST_HEAD(&sst->scratch_block_list);
/* Initialise SST Audio DSP */
if (sst->ops->init) {
@@ -366,6 +390,10 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
if (err)
goto irq_err;
+ err = sst_dma_new(sst);
+ if (err)
+ dev_warn(dev, "sst_dma_new failed %d\n", err);
+
return sst;
irq_err:
@@ -381,6 +409,9 @@ void sst_dsp_free(struct sst_dsp *sst)
free_irq(sst->irq, sst);
if (sst->ops->free)
sst->ops->free(sst);
+
+ if (sst->dma)
+ sst_dma_free(sst->dma);
}
EXPORT_SYMBOL_GPL(sst_dsp_free);
diff --git a/sound/soc/intel/sst-dsp.h b/sound/soc/intel/sst-dsp.h
index 3165dfa97408..f291e32f0077 100644
--- a/sound/soc/intel/sst-dsp.h
+++ b/sound/soc/intel/sst-dsp.h
@@ -30,6 +30,9 @@
#define SST_DMA_TYPE_DW 1
#define SST_DMA_TYPE_MID 2
+/* autosuspend delay 5s*/
+#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000)
+
/* SST Shim register map
* The register naming can differ between products. Some products also
* contain extra functionality.
@@ -156,12 +159,18 @@
#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_D3PGD (1 << 0)
+#define SST_VDRTCL0_D3SRAMPGD (1 << 1)
+#define SST_VDRTCL0_DSRAMPGE_SHIFT 12
+#define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT 2
#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
+/* VDRTCTL2 */
+#define SST_VDRTCL2_DCLCGE (1 << 1)
+#define SST_VDRTCL2_DTCGE (1 << 10)
+#define SST_VDRTCL2_APLLSE_MASK (1 << 31)
+
/* PMCS */
#define SST_PMCS 0x84
#define SST_PMCS_PS_MASK 0x3
@@ -245,6 +254,17 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst,
/* DSP reset & boot */
void sst_dsp_reset(struct sst_dsp *sst);
int sst_dsp_boot(struct sst_dsp *sst);
+int sst_dsp_wake(struct sst_dsp *sst);
+void sst_dsp_sleep(struct sst_dsp *sst);
+void sst_dsp_stall(struct sst_dsp *sst);
+
+/* DMA */
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size);
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size);
/* Msg IO */
void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
diff --git a/sound/soc/intel/sst-firmware.c b/sound/soc/intel/sst-firmware.c
index 3bb43dac892d..4a5bde9c686b 100644
--- a/sound/soc/intel/sst-firmware.c
+++ b/sound/soc/intel/sst-firmware.c
@@ -23,6 +23,11 @@
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/pci.h>
+#include <linux/acpi.h>
+
+/* supported DMA engine drivers */
+#include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
#include <asm/page.h>
#include <asm/pgtable.h>
@@ -30,16 +35,301 @@
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
-static void block_module_remove(struct sst_module *module);
+#define SST_DMA_RESOURCES 2
+#define SST_DSP_DMA_MAX_BURST 0x3
+#define SST_HSW_BLOCK_ANY 0xffffffff
+
+#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
+
+struct sst_dma {
+ struct sst_dsp *sst;
+
+ struct dw_dma_chip *chip;
+
+ struct dma_async_tx_descriptor *desc;
+ struct dma_chan *ch;
+};
+
+static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+ /* __iowrite32_copy use 32bit size values so divide by 4 */
+ __iowrite32_copy((void *)dest, src, bytes/4);
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+ struct sst_dsp *sst = (struct sst_dsp *)arg;
+
+ dev_dbg(sst->dev, "DMA: callback\n");
+}
+
+static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size)
+{
+ struct dma_async_tx_descriptor *desc;
+ struct sst_dma *dma = sst->dma;
+
+ if (dma->ch == NULL) {
+ dev_err(sst->dev, "error: no DMA channel\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
+ (unsigned long)src_addr, (unsigned long)dest_addr, size);
+
+ desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
+ src_addr, size, DMA_CTRL_ACK);
+ if (!desc){
+ dev_err(sst->dev, "error: dma prep memcpy failed\n");
+ return -EINVAL;
+ }
+
+ desc->callback = sst_dma_transfer_complete;
+ desc->callback_param = sst;
+
+ desc->tx_submit(desc);
+ dma_wait_for_async_tx(desc);
+
+ return 0;
+}
+
+/* copy to DSP */
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size)
+{
+ return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
+ src_addr, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
+
+/* copy from DSP */
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+ dma_addr_t src_addr, size_t size)
+{
+ return sst_dsp_dma_copy(sst, dest_addr,
+ src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
+
+/* remove module from memory - callers hold locks */
+static void block_list_remove(struct sst_dsp *dsp,
+ struct list_head *block_list)
+{
+ struct sst_mem_block *block, *tmp;
+ int err;
+
+ /* disable each block */
+ list_for_each_entry(block, block_list, module_list) {
+
+ if (block->ops && block->ops->disable) {
+ err = block->ops->disable(block);
+ if (err < 0)
+ dev_err(dsp->dev,
+ "error: cant disable block %d:%d\n",
+ block->type, block->index);
+ }
+ }
+
+ /* mark each block as free */
+ list_for_each_entry_safe(block, tmp, block_list, module_list) {
+ list_del(&block->module_list);
+ list_move(&block->list, &dsp->free_block_list);
+ dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+ }
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_list_prepare(struct sst_dsp *dsp,
+ struct list_head *block_list)
+{
+ struct sst_mem_block *block;
+ int ret = 0;
+
+ /* enable each block so that's it'e ready for data */
+ list_for_each_entry(block, block_list, module_list) {
+
+ if (block->ops && block->ops->enable && !block->users) {
+ ret = block->ops->enable(block);
+ if (ret < 0) {
+ dev_err(dsp->dev,
+ "error: cant disable block %d:%d\n",
+ block->type, block->index);
+ goto err;
+ }
+ }
+ }
+ return ret;
+
+err:
+ list_for_each_entry(block, block_list, module_list) {
+ if (block->ops && block->ops->disable)
+ block->ops->disable(block);
+ }
+ return ret;
+}
+
+static struct dw_dma_platform_data dw_pdata = {
+ .is_private = 1,
+ .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+ .chan_priority = CHAN_PRIORITY_ASCENDING,
+};
+
+static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
+ int irq)
+{
+ struct dw_dma_chip *chip;
+ int err;
+
+ chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+ if (!chip)
+ return ERR_PTR(-ENOMEM);
+
+ chip->irq = irq;
+ chip->regs = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(chip->regs))
+ return ERR_CAST(chip->regs);
+
+ err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+ if (err)
+ return ERR_PTR(err);
+
+ chip->dev = dev;
+ err = dw_dma_probe(chip, &dw_pdata);
+ if (err)
+ return ERR_PTR(err);
+
+ return chip;
+}
+
+static void dw_remove(struct dw_dma_chip *chip)
+{
+ dw_dma_remove(chip);
+}
+
+static bool dma_chan_filter(struct dma_chan *chan, void *param)
+{
+ struct sst_dsp *dsp = (struct sst_dsp *)param;
+
+ return chan->device->dev == dsp->dma_dev;
+}
-static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
{
- u32 i;
+ struct sst_dma *dma = dsp->dma;
+ struct dma_slave_config slave;
+ dma_cap_mask_t mask;
+ int ret;
+
+ /* The Intel MID DMA engine driver needs the slave config set but
+ * Synopsis DMA engine driver safely ignores the slave config */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+
+ dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
+ if (dma->ch == NULL) {
+ dev_err(dsp->dev, "error: DMA request channel failed\n");
+ return -EIO;
+ }
+
+ memset(&slave, 0, sizeof(slave));
+ slave.direction = DMA_MEM_TO_DEV;
+ slave.src_addr_width =
+ slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
+
+ ret = dmaengine_slave_config(dma->ch, &slave);
+ if (ret) {
+ dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
+ ret);
+ dma_release_channel(dma->ch);
+ dma->ch = NULL;
+ }
- /* copy one 32 bit word at a time as 64 bit access is not supported */
- for (i = 0; i < bytes; i += 4)
- memcpy_toio(dest + i, src + i, 4);
+ return ret;
}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
+{
+ struct sst_dma *dma = dsp->dma;
+
+ if (!dma->ch)
+ return;
+
+ dma_release_channel(dma->ch);
+ dma->ch = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+
+int sst_dma_new(struct sst_dsp *sst)
+{
+ struct sst_pdata *sst_pdata = sst->pdata;
+ struct sst_dma *dma;
+ struct resource mem;
+ const char *dma_dev_name;
+ int ret = 0;
+
+ /* configure the correct platform data for whatever DMA engine
+ * is attached to the ADSP IP. */
+ switch (sst->pdata->dma_engine) {
+ case SST_DMA_TYPE_DW:
+ dma_dev_name = "dw_dmac";
+ break;
+ case SST_DMA_TYPE_MID:
+ dma_dev_name = "Intel MID DMA";
+ break;
+ default:
+ dev_err(sst->dev, "error: invalid DMA engine %d\n",
+ sst->pdata->dma_engine);
+ return -EINVAL;
+ }
+
+ dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
+ if (!dma)
+ return -ENOMEM;
+
+ dma->sst = sst;
+
+ memset(&mem, 0, sizeof(mem));
+
+ mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
+ mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
+ mem.flags = IORESOURCE_MEM;
+
+ /* now register DMA engine device */
+ dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
+ if (IS_ERR(dma->chip)) {
+ dev_err(sst->dev, "error: DMA device register failed\n");
+ ret = PTR_ERR(dma->chip);
+ goto err_dma_dev;
+ }
+
+ sst->dma = dma;
+ sst->fw_use_dma = true;
+ return 0;
+
+err_dma_dev:
+ devm_kfree(sst->dev, dma);
+ return ret;
+}
+EXPORT_SYMBOL(sst_dma_new);
+
+void sst_dma_free(struct sst_dma *dma)
+{
+
+ if (dma == NULL)
+ return;
+
+ if (dma->ch)
+ dma_release_channel(dma->ch);
+
+ if (dma->chip)
+ dw_remove(dma->chip);
+
+}
+EXPORT_SYMBOL(sst_dma_free);
/* create new generic firmware object */
struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
@@ -71,6 +361,12 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
/* copy FW data to DMA-able memory */
memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
+ if (dsp->fw_use_dma) {
+ err = sst_dsp_dma_get_channel(dsp, 0);
+ if (err < 0)
+ goto chan_err;
+ }
+
/* call core specific FW paser to load FW data into DSP */
err = dsp->ops->parse_fw(sst_fw);
if (err < 0) {
@@ -78,6 +374,9 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
goto parse_err;
}
+ if (dsp->fw_use_dma)
+ sst_dsp_dma_put_channel(dsp);
+
mutex_lock(&dsp->mutex);
list_add(&sst_fw->list, &dsp->fw_list);
mutex_unlock(&dsp->mutex);
@@ -85,9 +384,13 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
return sst_fw;
parse_err:
- dma_free_coherent(dsp->dev, sst_fw->size,
+ if (dsp->fw_use_dma)
+ sst_dsp_dma_put_channel(dsp);
+chan_err:
+ dma_free_coherent(dsp->dma_dev, sst_fw->size,
sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
+ sst_fw->dma_buf = NULL;
kfree(sst_fw);
return NULL;
}
@@ -111,21 +414,37 @@ EXPORT_SYMBOL_GPL(sst_fw_reload);
void sst_fw_unload(struct sst_fw *sst_fw)
{
- struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_module *module, *tmp;
+ struct sst_dsp *dsp = sst_fw->dsp;
+ struct sst_module *module, *mtmp;
+ struct sst_module_runtime *runtime, *rtmp;
+
+ dev_dbg(dsp->dev, "unloading firmware\n");
+
+ mutex_lock(&dsp->mutex);
+
+ /* check module by module */
+ list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
+ if (module->sst_fw == sst_fw) {
+
+ /* remove runtime modules */
+ list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
- dev_dbg(dsp->dev, "unloading firmware\n");
+ block_list_remove(dsp, &runtime->block_list);
+ list_del(&runtime->list);
+ kfree(runtime);
+ }
+
+ /* now remove the module */
+ block_list_remove(dsp, &module->block_list);
+ list_del(&module->list);
+ kfree(module);
+ }
+ }
- mutex_lock(&dsp->mutex);
- list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
- if (module->sst_fw == sst_fw) {
- block_module_remove(module);
- list_del(&module->list);
- kfree(module);
- }
- }
+ /* remove all scratch blocks */
+ block_list_remove(dsp, &dsp->scratch_block_list);
- mutex_unlock(&dsp->mutex);
+ mutex_unlock(&dsp->mutex);
}
EXPORT_SYMBOL_GPL(sst_fw_unload);
@@ -138,7 +457,8 @@ void sst_fw_free(struct sst_fw *sst_fw)
list_del(&sst_fw->list);
mutex_unlock(&dsp->mutex);
- dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
+ if (sst_fw->dma_buf)
+ dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
sst_fw->dmable_fw_paddr);
kfree(sst_fw);
}
@@ -175,11 +495,11 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
sst_module->id = template->id;
sst_module->dsp = dsp;
sst_module->sst_fw = sst_fw;
-
- memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
- memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
+ sst_module->scratch_size = template->scratch_size;
+ sst_module->persistent_size = template->persistent_size;
INIT_LIST_HEAD(&sst_module->block_list);
+ INIT_LIST_HEAD(&sst_module->runtime_list);
mutex_lock(&dsp->mutex);
list_add(&sst_module->list, &dsp->module_list);
@@ -202,73 +522,122 @@ void sst_module_free(struct sst_module *sst_module)
}
EXPORT_SYMBOL_GPL(sst_module_free);
-static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type,
- u32 offset)
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+ int id, void *private)
+{
+ struct sst_dsp *dsp = module->dsp;
+ struct sst_module_runtime *runtime;
+
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (runtime == NULL)
+ return NULL;
+
+ runtime->id = id;
+ runtime->dsp = dsp;
+ runtime->module = module;
+ INIT_LIST_HEAD(&runtime->block_list);
+
+ mutex_lock(&dsp->mutex);
+ list_add(&runtime->list, &module->runtime_list);
+ mutex_unlock(&dsp->mutex);
+
+ return runtime;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_new);
+
+void sst_module_runtime_free(struct sst_module_runtime *runtime)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+
+ mutex_lock(&dsp->mutex);
+ list_del(&runtime->list);
+ mutex_unlock(&dsp->mutex);
+
+ kfree(runtime);
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp,
+ struct sst_block_allocator *ba)
{
struct sst_mem_block *block;
list_for_each_entry(block, &dsp->free_block_list, list) {
- if (block->type == type && block->offset == offset)
+ if (block->type == ba->type && block->offset == ba->offset)
return block;
}
return NULL;
}
-static int block_alloc_contiguous(struct sst_module *module,
- struct sst_module_data *data, u32 offset, int size)
+/* Block allocator must be on block boundary */
+static int block_alloc_contiguous(struct sst_dsp *dsp,
+ struct sst_block_allocator *ba, struct list_head *block_list)
{
struct list_head tmp = LIST_HEAD_INIT(tmp);
- struct sst_dsp *dsp = module->dsp;
struct sst_mem_block *block;
+ u32 block_start = SST_HSW_BLOCK_ANY;
+ int size = ba->size, offset = ba->offset;
+
+ while (ba->size > 0) {
- while (size > 0) {
- block = find_block(dsp, data->type, offset);
+ block = find_block(dsp, ba);
if (!block) {
list_splice(&tmp, &dsp->free_block_list);
+
+ ba->size = size;
+ ba->offset = offset;
return -ENOMEM;
}
list_move_tail(&block->list, &tmp);
- offset += block->size;
- size -= block->size;
+ ba->offset += block->size;
+ ba->size -= block->size;
}
+ ba->size = size;
+ ba->offset = offset;
+
+ list_for_each_entry(block, &tmp, list) {
+
+ if (block->offset < block_start)
+ block_start = block->offset;
- list_for_each_entry(block, &tmp, list)
- list_add(&block->module_list, &module->block_list);
+ list_add(&block->module_list, block_list);
+
+ dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
+ }
list_splice(&tmp, &dsp->used_block_list);
return 0;
}
-/* allocate free DSP blocks for module data - callers hold locks */
-static int block_alloc(struct sst_module *module,
- struct sst_module_data *data)
+/* allocate first free DSP blocks for data - callers hold locks */
+static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list)
{
- struct sst_dsp *dsp = module->dsp;
struct sst_mem_block *block, *tmp;
int ret = 0;
- if (data->size == 0)
+ if (ba->size == 0)
return 0;
/* find first free whole blocks that can hold module */
list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks with wrong type */
- if (block->type != data->type)
+ if (block->type != ba->type)
continue;
- if (data->size > block->size)
+ if (ba->size > block->size)
continue;
- data->offset = block->offset;
- block->data_type = data->data_type;
- block->bytes_used = data->size % block->size;
- list_add(&block->module_list, &module->block_list);
+ ba->offset = block->offset;
+ block->bytes_used = ba->size % block->size;
+ list_add(&block->module_list, block_list);
list_move(&block->list, &dsp->used_block_list);
- dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
- module->id, block->type, block->index);
+ dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
return 0;
}
@@ -276,15 +645,19 @@ static int block_alloc(struct sst_module *module,
list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
/* ignore blocks with wrong type */
- if (block->type != data->type)
+ if (block->type != ba->type)
continue;
/* do we span > 1 blocks */
- if (data->size > block->size) {
- ret = block_alloc_contiguous(module, data,
- block->offset, data->size);
+ if (ba->size > block->size) {
+
+ /* align ba to block boundary */
+ ba->offset = block->offset;
+
+ ret = block_alloc_contiguous(dsp, ba, block_list);
if (ret == 0)
return ret;
+
}
}
@@ -292,93 +665,74 @@ static int block_alloc(struct sst_module *module,
return -ENOMEM;
}
-/* remove module from memory - callers hold locks */
-static void block_module_remove(struct sst_module *module)
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list)
{
- struct sst_mem_block *block, *tmp;
- struct sst_dsp *dsp = module->dsp;
- int err;
+ int ret;
- /* disable each block */
- list_for_each_entry(block, &module->block_list, module_list) {
+ dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+ ba->size, ba->offset, ba->type);
- if (block->ops && block->ops->disable) {
- err = block->ops->disable(block);
- if (err < 0)
- dev_err(dsp->dev,
- "error: cant disable block %d:%d\n",
- block->type, block->index);
- }
- }
+ mutex_lock(&dsp->mutex);
- /* mark each block as free */
- list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
- list_del(&block->module_list);
- list_move(&block->list, &dsp->free_block_list);
+ ret = block_alloc(dsp, ba, block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
+ goto out;
}
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_module_prepare(struct sst_module *module)
-{
- struct sst_mem_block *block;
- int ret = 0;
- /* enable each block so that's it'e ready for module P/S data */
- list_for_each_entry(block, &module->block_list, module_list) {
+ /* prepare DSP blocks for module usage */
+ ret = block_list_prepare(dsp, block_list);
+ if (ret < 0)
+ dev_err(dsp->dev, "error: prepare failed\n");
- if (block->ops && block->ops->enable) {
- ret = block->ops->enable(block);
- if (ret < 0) {
- dev_err(module->dsp->dev,
- "error: cant disable block %d:%d\n",
- block->type, block->index);
- goto err;
- }
- }
- }
+out:
+ mutex_unlock(&dsp->mutex);
return ret;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_blocks);
-err:
- list_for_each_entry(block, &module->block_list, module_list) {
- if (block->ops && block->ops->disable)
- block->ops->disable(block);
- }
- return ret;
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
+{
+ mutex_lock(&dsp->mutex);
+ block_list_remove(dsp, block_list);
+ mutex_unlock(&dsp->mutex);
+ return 0;
}
+EXPORT_SYMBOL_GPL(sst_free_blocks);
/* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_module *module,
- struct sst_module_data *data)
+static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+ struct list_head *block_list)
{
- struct sst_dsp *dsp = module->dsp;
struct sst_mem_block *block, *tmp;
- u32 end = data->offset + data->size, block_end;
+ u32 end = ba->offset + ba->size, block_end;
int err;
/* only IRAM/DRAM blocks are managed */
- if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
+ if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
return 0;
/* are blocks already attached to this module */
- list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+ list_for_each_entry_safe(block, tmp, block_list, module_list) {
- /* force compacting mem blocks of the same data_type */
- if (block->data_type != data->data_type)
+ /* ignore blocks with wrong type */
+ if (block->type != ba->type)
continue;
block_end = block->offset + block->size;
/* find block that holds section */
- if (data->offset >= block->offset && end < block_end)
+ if (ba->offset >= block->offset && end <= block_end)
return 0;
/* does block span more than 1 section */
- if (data->offset >= block->offset && data->offset < block_end) {
+ if (ba->offset >= block->offset && ba->offset < block_end) {
- err = block_alloc_contiguous(module, data,
- block->offset + block->size,
- data->size - block->size);
+ /* align ba to block boundary */
+ ba->size -= block_end - ba->offset;
+ ba->offset = block_end;
+ err = block_alloc_contiguous(dsp, ba, block_list);
if (err < 0)
return -ENOMEM;
@@ -391,82 +745,270 @@ static int block_alloc_fixed(struct sst_module *module,
list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
block_end = block->offset + block->size;
+ /* ignore blocks with wrong type */
+ if (block->type != ba->type)
+ continue;
+
/* find block that holds section */
- if (data->offset >= block->offset && end < block_end) {
+ if (ba->offset >= block->offset && end <= block_end) {
/* add block */
- block->data_type = data->data_type;
list_move(&block->list, &dsp->used_block_list);
- list_add(&block->module_list, &module->block_list);
+ list_add(&block->module_list, block_list);
+ dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+ block->type, block->index, block->offset);
return 0;
}
/* does block span more than 1 section */
- if (data->offset >= block->offset && data->offset < block_end) {
+ if (ba->offset >= block->offset && ba->offset < block_end) {
- err = block_alloc_contiguous(module, data,
- block->offset, data->size);
+ /* align ba to block boundary */
+ ba->offset = block->offset;
+
+ err = block_alloc_contiguous(dsp, ba, block_list);
if (err < 0)
return -ENOMEM;
return 0;
}
-
}
return -ENOMEM;
}
/* Load fixed module data into DSP memory blocks */
-int sst_module_insert_fixed_block(struct sst_module *module,
- struct sst_module_data *data)
+int sst_module_alloc_blocks(struct sst_module *module)
{
struct sst_dsp *dsp = module->dsp;
+ struct sst_fw *sst_fw = module->sst_fw;
+ struct sst_block_allocator ba;
int ret;
+ ba.size = module->size;
+ ba.type = module->type;
+ ba.offset = module->offset;
+
+ dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+ ba.size, ba.offset, ba.type);
+
mutex_lock(&dsp->mutex);
/* alloc blocks that includes this section */
- ret = block_alloc_fixed(module, data);
+ ret = block_alloc_fixed(dsp, &ba, &module->block_list);
if (ret < 0) {
dev_err(dsp->dev,
"error: no free blocks for section at offset 0x%x size 0x%x\n",
- data->offset, data->size);
+ module->offset, module->size);
mutex_unlock(&dsp->mutex);
return -ENOMEM;
}
/* prepare DSP blocks for module copy */
- ret = block_module_prepare(module);
+ ret = block_list_prepare(dsp, &module->block_list);
if (ret < 0) {
dev_err(dsp->dev, "error: fw module prepare failed\n");
goto err;
}
/* copy partial module data to blocks */
- sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size);
+ if (dsp->fw_use_dma) {
+ ret = sst_dsp_dma_copyto(dsp,
+ dsp->addr.lpe_base + module->offset,
+ sst_fw->dmable_fw_paddr + module->data_offset,
+ module->size);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: module copy failed\n");
+ goto err;
+ }
+ } else
+ sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
+ module->size);
mutex_unlock(&dsp->mutex);
return ret;
err:
- block_module_remove(module);
+ block_list_remove(dsp, &module->block_list);
mutex_unlock(&dsp->mutex);
return ret;
}
-EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
/* Unload entire module from DSP memory */
-int sst_block_module_remove(struct sst_module *module)
+int sst_module_free_blocks(struct sst_module *module)
{
struct sst_dsp *dsp = module->dsp;
mutex_lock(&dsp->mutex);
- block_module_remove(module);
+ block_list_remove(dsp, &module->block_list);
mutex_unlock(&dsp->mutex);
return 0;
}
-EXPORT_SYMBOL_GPL(sst_block_module_remove);
+EXPORT_SYMBOL_GPL(sst_module_free_blocks);
+
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+ int offset)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+ struct sst_module *module = runtime->module;
+ struct sst_block_allocator ba;
+ int ret;
+
+ if (module->persistent_size == 0)
+ return 0;
+
+ ba.size = module->persistent_size;
+ ba.type = SST_MEM_DRAM;
+
+ mutex_lock(&dsp->mutex);
+
+ /* do we need to allocate at a fixed address ? */
+ if (offset != 0) {
+
+ ba.offset = offset;
+
+ dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
+ ba.size, ba.type, ba.offset);
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
+
+ } else {
+ dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
+ ba.size, ba.type);
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc(dsp, &ba, &runtime->block_list);
+ }
+ if (ret < 0) {
+ dev_err(dsp->dev,
+ "error: no free blocks for runtime module size 0x%x\n",
+ module->persistent_size);
+ mutex_unlock(&dsp->mutex);
+ return -ENOMEM;
+ }
+ runtime->persistent_offset = ba.offset;
+
+ /* prepare DSP blocks for module copy */
+ ret = block_list_prepare(dsp, &runtime->block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: runtime block prepare failed\n");
+ goto err;
+ }
+
+ mutex_unlock(&dsp->mutex);
+ return ret;
+
+err:
+ block_list_remove(dsp, &module->block_list);
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
+
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+
+ mutex_lock(&dsp->mutex);
+ block_list_remove(dsp, &runtime->block_list);
+ mutex_unlock(&dsp->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
+
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+ struct sst_module *module = runtime->module;
+ int ret = 0;
+
+ dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
+ runtime->id, runtime->persistent_offset,
+ module->persistent_size);
+
+ context->buffer = dma_alloc_coherent(dsp->dma_dev,
+ module->persistent_size,
+ &context->dma_buffer, GFP_DMA | GFP_KERNEL);
+ if (!context->buffer) {
+ dev_err(dsp->dev, "error: DMA context alloc failed\n");
+ return -ENOMEM;
+ }
+
+ mutex_lock(&dsp->mutex);
+
+ if (dsp->fw_use_dma) {
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0)
+ goto err;
+
+ ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
+ dsp->addr.lpe_base + runtime->persistent_offset,
+ module->persistent_size);
+ sst_dsp_dma_put_channel(dsp);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: context copy failed\n");
+ goto err;
+ }
+ } else
+ sst_memcpy32(context->buffer, dsp->addr.lpe +
+ runtime->persistent_offset,
+ module->persistent_size);
+
+err:
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_save);
+
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+ struct sst_module_runtime_context *context)
+{
+ struct sst_dsp *dsp = runtime->dsp;
+ struct sst_module *module = runtime->module;
+ int ret = 0;
+
+ dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
+ runtime->id, runtime->persistent_offset,
+ module->persistent_size);
+
+ mutex_lock(&dsp->mutex);
+
+ if (!context->buffer) {
+ dev_info(dsp->dev, "no context buffer need to restore!\n");
+ goto err;
+ }
+
+ if (dsp->fw_use_dma) {
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0)
+ goto err;
+
+ ret = sst_dsp_dma_copyto(dsp,
+ dsp->addr.lpe_base + runtime->persistent_offset,
+ context->dma_buffer, module->persistent_size);
+ sst_dsp_dma_put_channel(dsp);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: module copy failed\n");
+ goto err;
+ }
+ } else
+ sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
+ context->buffer, module->persistent_size);
+
+ dma_free_coherent(dsp->dma_dev, module->persistent_size,
+ context->buffer, context->dma_buffer);
+ context->buffer = NULL;
+
+err:
+ mutex_unlock(&dsp->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
/* register a DSP memory block for use with FW based modules */
struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -519,80 +1061,84 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp)
EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
/* allocate scratch buffer blocks */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
+int sst_block_alloc_scratch(struct sst_dsp *dsp)
{
- struct sst_module *sst_module, *scratch;
- struct sst_mem_block *block, *tmp;
- u32 block_size;
- int ret = 0;
-
- scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
- if (scratch == NULL)
- return NULL;
+ struct sst_module *module;
+ struct sst_block_allocator ba;
+ int ret;
mutex_lock(&dsp->mutex);
/* calculate required scratch size */
- list_for_each_entry(sst_module, &dsp->module_list, list) {
- if (scratch->s.size < sst_module->s.size)
- scratch->s.size = sst_module->s.size;
+ dsp->scratch_size = 0;
+ list_for_each_entry(module, &dsp->module_list, list) {
+ dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
+ module->id, module->scratch_size);
+ if (dsp->scratch_size < module->scratch_size)
+ dsp->scratch_size = module->scratch_size;
}
- dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
- scratch->s.size);
-
- /* init scratch module */
- scratch->dsp = dsp;
- scratch->s.type = SST_MEM_DRAM;
- scratch->s.data_type = SST_DATA_S;
- INIT_LIST_HEAD(&scratch->block_list);
+ dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
+ dsp->scratch_size);
- /* check free blocks before looking at used blocks for space */
- if (!list_empty(&dsp->free_block_list))
- block = list_first_entry(&dsp->free_block_list,
- struct sst_mem_block, list);
- else
- block = list_first_entry(&dsp->used_block_list,
- struct sst_mem_block, list);
- block_size = block->size;
+ if (dsp->scratch_size == 0) {
+ dev_info(dsp->dev, "no modules need scratch buffer\n");
+ mutex_unlock(&dsp->mutex);
+ return 0;
+ }
/* allocate blocks for module scratch buffers */
dev_dbg(dsp->dev, "allocating scratch blocks\n");
- ret = block_alloc(scratch, &scratch->s);
+
+ ba.size = dsp->scratch_size;
+ ba.type = SST_MEM_DRAM;
+
+ /* do we need to allocate at fixed offset */
+ if (dsp->scratch_offset != 0) {
+
+ dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
+ ba.size, ba.type, ba.offset);
+
+ ba.offset = dsp->scratch_offset;
+
+ /* alloc blocks that includes this section */
+ ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
+
+ } else {
+ dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
+ ba.size, ba.type);
+
+ ba.offset = 0;
+ ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
+ }
if (ret < 0) {
dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
- goto err;
+ mutex_unlock(&dsp->mutex);
+ return ret;
}
- /* assign the same offset of scratch to each module */
- list_for_each_entry(sst_module, &dsp->module_list, list)
- sst_module->s.offset = scratch->s.offset;
-
- mutex_unlock(&dsp->mutex);
- return scratch;
+ ret = block_list_prepare(dsp, &dsp->scratch_block_list);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: scratch block prepare failed\n");
+ mutex_unlock(&dsp->mutex);
+ return ret;
+ }
-err:
- list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
- list_del(&block->module_list);
+ /* assign the same offset of scratch to each module */
+ dsp->scratch_offset = ba.offset;
mutex_unlock(&dsp->mutex);
- return NULL;
+ return dsp->scratch_size;
}
-EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
/* free all scratch blocks */
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
- struct sst_module *scratch)
+void sst_block_free_scratch(struct sst_dsp *dsp)
{
- struct sst_mem_block *block, *tmp;
-
mutex_lock(&dsp->mutex);
-
- list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
- list_del(&block->module_list);
-
+ block_list_remove(dsp, &dsp->scratch_block_list);
mutex_unlock(&dsp->mutex);
}
-EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+EXPORT_SYMBOL_GPL(sst_block_free_scratch);
/* get a module from it's unique ID */
struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
@@ -612,3 +1158,40 @@ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
return NULL;
}
EXPORT_SYMBOL_GPL(sst_module_get_from_id);
+
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+ struct sst_module *module, u32 id)
+{
+ struct sst_module_runtime *runtime;
+ struct sst_dsp *dsp = module->dsp;
+
+ mutex_lock(&dsp->mutex);
+
+ list_for_each_entry(runtime, &module->runtime_list, list) {
+ if (runtime->id == id) {
+ mutex_unlock(&dsp->mutex);
+ return runtime;
+ }
+ }
+
+ mutex_unlock(&dsp->mutex);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
+
+/* returns block address in DSP address space */
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+ enum sst_mem_type type)
+{
+ switch (type) {
+ case SST_MEM_IRAM:
+ return offset - dsp->addr.iram_offset +
+ dsp->addr.dsp_iram_offset;
+ case SST_MEM_DRAM:
+ return offset - dsp->addr.dram_offset +
+ dsp->addr.dsp_dram_offset;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
diff --git a/sound/soc/intel/sst-haswell-dsp.c b/sound/soc/intel/sst-haswell-dsp.c
index 4b6c163c10ff..57039b00efc2 100644
--- a/sound/soc/intel/sst-haswell-dsp.c
+++ b/sound/soc/intel/sst-haswell-dsp.c
@@ -42,6 +42,10 @@
#define SST_LP_SHIM_OFFSET 0xE7000
#define SST_WPT_IRAM_OFFSET 0xA0000
#define SST_LP_IRAM_OFFSET 0x80000
+#define SST_WPT_DSP_DRAM_OFFSET 0x400000
+#define SST_WPT_DSP_IRAM_OFFSET 0x00000
+#define SST_LPT_DSP_DRAM_OFFSET 0x400000
+#define SST_LPT_DSP_IRAM_OFFSET 0x00000
#define SST_SHIM_PM_REG 0x84
@@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
{
struct dma_block_info *block;
struct sst_module *mod;
- struct sst_module_data block_data;
struct sst_module_template template;
- int count;
+ int count, ret;
void __iomem *ram;
/* TODO: allowed module types need to be configurable */
@@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
memset(&template, 0, sizeof(template));
template.id = module->type;
- template.entry = module->entry_point;
- template.p.size = module->info.persistent_size;
- template.p.type = SST_MEM_DRAM;
- template.p.data_type = SST_DATA_P;
- template.s.size = module->info.scratch_size;
- template.s.type = SST_MEM_DRAM;
- template.s.data_type = SST_DATA_S;
+ template.entry = module->entry_point - 4;
+ template.persistent_size = module->info.persistent_size;
+ template.scratch_size = module->info.scratch_size;
mod = sst_module_new(fw, &template, NULL);
if (mod == NULL)
@@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
switch (block->type) {
case SST_HSW_IRAM:
ram = dsp->addr.lpe;
- block_data.offset =
+ mod->offset =
block->ram_offset + dsp->addr.iram_offset;
- block_data.type = SST_MEM_IRAM;
+ mod->type = SST_MEM_IRAM;
break;
case SST_HSW_DRAM:
ram = dsp->addr.lpe;
- block_data.offset = block->ram_offset;
- block_data.type = SST_MEM_DRAM;
+ mod->offset = block->ram_offset;
+ mod->type = SST_MEM_DRAM;
break;
default:
dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
return -EINVAL;
}
- block_data.size = block->size;
- block_data.data_type = SST_DATA_M;
- block_data.data = (void *)block + sizeof(*block);
- block_data.data_offset = block_data.data - fw->dma_buf;
+ mod->size = block->size;
+ mod->data = (void *)block + sizeof(*block);
+ mod->data_offset = mod->data - fw->dma_buf;
- dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+ dev_dbg(dsp->dev, "module block %d type 0x%x "
"size 0x%x ==> ram %p offset 0x%x\n",
- count, block->type, block->size, ram,
+ count, mod->type, block->size, ram,
block->ram_offset);
- sst_module_insert_fixed_block(mod, &block_data);
+ ret = sst_module_alloc_blocks(mod);
+ if (ret < 0) {
+ dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+ count);
+ sst_module_free(mod);
+ return ret;
+ }
block = (void *)block + sizeof(*block) + block->size;
}
+
return 0;
}
static int hsw_parse_fw_image(struct sst_fw *sst_fw)
{
struct fw_header *header;
- struct sst_module *scratch;
struct fw_module_header *module;
struct sst_dsp *dsp = sst_fw->dsp;
- struct sst_hsw *hsw = sst_fw->private;
int ret, count;
/* Read the header information from the data pointer */
@@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
module = (void *)module + sizeof(*module) + module->mod_size;
}
- /* allocate persistent/scratch mem regions */
- scratch = sst_mem_block_alloc_scratch(dsp);
- if (scratch == NULL)
- return -ENOMEM;
-
- sst_hsw_set_scratch_module(hsw, scratch);
+ /* allocate scratch mem regions */
+ sst_block_alloc_scratch(dsp);
return 0;
}
@@ -248,8 +247,94 @@ static irqreturn_t hsw_irq(int irq, void *context)
return ret;
}
-static void hsw_boot(struct sst_dsp *sst)
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+ u32 val;
+ u32 reg;
+
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* enable power gating and switch off DRAM & IRAM blocks */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ val |= SST_VDRTCL0_DSRAMPGE_MASK |
+ SST_VDRTCL0_ISRAMPGE_MASK;
+ val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ /* switch off audio PLL */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val |= SST_VDRTCL2_APLLSE_MASK;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* disable MCLK(clkctl.smos = 0) */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+ SST_CLKCTL_MASK, 0);
+
+ /* Set D3 state, delay 50 us */
+ val = readl(sst->addr.pci_cfg + SST_PMCS);
+ val |= SST_PMCS_PS_MASK;
+ writel(val, sst->addr.pci_cfg + SST_PMCS);
+ udelay(50);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
{
+ /* put DSP into reset and stall */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+ SST_CSR_RST | SST_CSR_STALL,
+ SST_CSR_RST | SST_CSR_STALL);
+
+ /* keep in reset for 10ms */
+ mdelay(10);
+
+ /* take DSP out of reset and keep stalled for FW loading */
+ sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+ SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+ int tries = 10;
+ u32 reg;
+
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ reg |= SST_VDRTCL0_D3PGD;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+ /* Set D0 state */
+ reg = readl(sst->addr.pci_cfg + SST_PMCS);
+ reg &= ~SST_PMCS_PS_MASK;
+ writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+ /* check that ADSP shim is enabled */
+ while (tries--) {
+ reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+ if (reg == 0)
+ goto finish;
+
+ msleep(1);
+ }
+
+ return -ENODEV;
+
+finish:
/* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
@@ -264,34 +349,96 @@ static void hsw_boot(struct sst_dsp *sst)
SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
+ /* Stall and reset core, set CSR */
+ hsw_reset(sst);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
+ /* switch on audio PLL */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ reg &= ~SST_VDRTCL2_APLLSE_MASK;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ /* set default power gating control, enable power gating control for all blocks. that is,
+ can't be accessed, please enable each block before accessing. */
+ reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+ reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+ writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
/* disable DMA finish function for SSP0 & SSP1 */
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
SST_CSR2_SDFD_SSP1);
- /* enable DMA engine 0,1 all channels to access host memory */
- 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));
+ /* set on-demond mode on engine 0,1 for all channels */
+ sst_dsp_shim_update_bits(sst, SST_HMDC,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+ /* Enable Interrupt from both sides */
+ sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+ 0x0);
+ sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+ SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+ /* clear IPC registers */
+ sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+ sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+ sst_dsp_shim_write(sst, 0x80, 0x6);
+ sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+ return 0;
+}
- /* disable all clock gating */
- writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+static void hsw_boot(struct sst_dsp *sst)
+{
+ /* set oportunistic mode on engine 0,1 for all channels */
+ sst_dsp_shim_update_bits(sst, SST_HMDC,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
/* set DSP to RUN */
sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
}
-static void hsw_reset(struct sst_dsp *sst)
+static void hsw_stall(struct sst_dsp *sst)
+{
+ /* stall DSP */
+ sst_dsp_shim_update_bits(sst, SST_CSR,
+ SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+ SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
{
+ dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
/* put DSP into reset and stall */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+ sst_dsp_shim_update_bits(sst, SST_CSR,
+ SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+ SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
- /* keep in reset for 10ms */
- mdelay(10);
+ hsw_set_dsp_D3(sst);
+ dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
- /* take DSP out of reset and keep stalled for FW loading */
- sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
- SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+static int hsw_wake(struct sst_dsp *sst)
+{
+ int ret;
+
+ dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+ ret = hsw_set_dsp_D0(sst);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+ return 0;
}
struct sst_adsp_memregion {
@@ -396,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block)
dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
block->type, block->index, block->offset);
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val &= ~SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
bit = hsw_block_get_bit(block);
writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
@@ -403,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block)
/* wait 18 DSP clock ticks */
udelay(10);
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val |= SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
/*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;
@@ -420,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block)
dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
block->type, block->index, block->offset);
+ /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val &= ~SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
bit = hsw_block_get_bit(block);
writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
+ /* wait 18 DSP clock ticks */
+ udelay(10);
+
+ /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+ val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+ val |= SST_VDRTCL2_DCLCGE;
+ writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+ udelay(50);
+
return 0;
}
@@ -432,27 +607,6 @@ static struct sst_block_ops sst_hsw_ops = {
.disable = hsw_block_disable,
};
-static int hsw_enable_shim(struct sst_dsp *sst)
-{
- int tries = 10;
- u32 reg;
-
- /* enable shim */
- reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
- writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
-
- /* check that ADSP shim is enabled */
- while (tries--) {
- reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
- if (reg != 0xffffffff)
- return 0;
-
- msleep(1);
- }
-
- return -ENODEV;
-}
-
static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
{
const struct sst_adsp_memregion *region;
@@ -467,12 +621,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
region = lp_region;
region_count = ARRAY_SIZE(lp_region);
sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+ sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+ sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
break;
case SST_DEV_ID_WILDCAT_POINT:
region = wpt_region;
region_count = ARRAY_SIZE(wpt_region);
sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+ sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+ sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
break;
default:
@@ -487,7 +645,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
}
/* enable the DSP SHIM */
- ret = hsw_enable_shim(sst);
+ ret = hsw_set_dsp_D0(sst);
if (ret < 0) {
dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
return ret;
@@ -497,10 +655,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
if (ret)
return ret;
- /* Enable Interrupt from both sides */
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
- sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
- (0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
/* register DSP memory blocks - ideally we should get this from ACPI */
for (i = 0; i < region_count; i++) {
@@ -532,6 +686,9 @@ static void hsw_free(struct sst_dsp *sst)
struct sst_ops haswell_ops = {
.reset = hsw_reset,
.boot = hsw_boot,
+ .stall = hsw_stall,
+ .wake = hsw_wake,
+ .sleep = hsw_sleep,
.write = sst_shim32_write,
.read = sst_shim32_read,
.write64 = sst_shim32_write64,
diff --git a/sound/soc/intel/sst-haswell-ipc.c b/sound/soc/intel/sst-haswell-ipc.c
index b6291516dbbf..3f8c48231364 100644
--- a/sound/soc/intel/sst-haswell-ipc.c
+++ b/sound/soc/intel/sst-haswell-ipc.c
@@ -30,6 +30,7 @@
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
#include "sst-haswell-ipc.h"
#include "sst-dsp.h"
@@ -276,6 +277,7 @@ struct sst_hsw {
struct sst_hsw_ipc_fw_version version;
struct sst_module *scratch;
bool fw_done;
+ struct sst_fw *sst_fw;
/* stream */
struct list_head stream_list;
@@ -289,6 +291,8 @@ struct sst_hsw {
/* DX */
struct sst_hsw_ipc_dx_reply dx;
+ void *dx_context;
+ dma_addr_t dx_context_paddr;
/* boot */
wait_queue_head_t boot_wait;
@@ -1038,14 +1042,9 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
- if (channel > 1)
+ if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
return -EINVAL;
- if (stream->mute[channel]) {
- stream->mute_volume[channel] = volume;
- return 0;
- }
-
header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
@@ -1053,9 +1052,28 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
header |= (stage_id << IPC_STG_ID_SHIFT);
req = &stream->vol_req;
- req->channel = channel;
req->target_volume = volume;
+ /* set both at same time ? */
+ if (channel == SST_HSW_CHANNELS_ALL) {
+ if (hsw->mute[0] && hsw->mute[1]) {
+ hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+ return 0;
+ } else if (hsw->mute[0])
+ req->channel = 1;
+ else if (hsw->mute[1])
+ req->channel = 0;
+ else
+ req->channel = SST_HSW_CHANNELS_ALL;
+ } else {
+ /* set only 1 channel */
+ if (hsw->mute[channel]) {
+ hsw->mute_volume[channel] = volume;
+ return 0;
+ }
+ req->channel = channel;
+ }
+
ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
if (ret < 0) {
dev_err(hsw->dev, "error: set stream volume failed\n");
@@ -1134,8 +1152,11 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
trace_ipc_request("set mixer volume", volume);
+ if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+ return -EINVAL;
+
/* set both at same time ? */
- if (channel == 2) {
+ if (channel == SST_HSW_CHANNELS_ALL) {
if (hsw->mute[0] && hsw->mute[1]) {
hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
return 0;
@@ -1144,7 +1165,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
else if (hsw->mute[1])
req.channel = 0;
else
- req.channel = 0xffffffff;
+ req.channel = SST_HSW_CHANNELS_ALL;
} else {
/* set only 1 channel */
if (hsw->mute[channel]) {
@@ -1256,10 +1277,6 @@ int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
return -EINVAL;
}
- /* stereo is only supported atm */
- if (channels != 2)
- return -EINVAL;
-
stream->request.format.ch_num = channels;
return 0;
}
@@ -1355,10 +1372,11 @@ int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
}
int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
- u32 entry_point)
+ struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
{
struct sst_hsw_module_map *map = &stream->request.map;
+ struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
+ struct sst_module *module = runtime->module;
if (stream->commited) {
dev_err(hsw->dev, "error: stream committed for set module\n");
@@ -1367,36 +1385,25 @@ int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
/* only support initial module atm */
map->module_entries_count = 1;
- map->module_entries[0].module_id = module_id;
- map->module_entries[0].entry_point = entry_point;
-
- return 0;
-}
-
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set pmem\n");
- return -EINVAL;
- }
-
- stream->request.persistent_mem.offset = offset;
- stream->request.persistent_mem.size = size;
-
- return 0;
-}
-
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
- if (stream->commited) {
- dev_err(hsw->dev, "error: stream committed for set smem\n");
- return -EINVAL;
- }
-
- stream->request.scratch_mem.offset = offset;
- stream->request.scratch_mem.size = size;
+ map->module_entries[0].module_id = module->id;
+ map->module_entries[0].entry_point = module->entry;
+
+ stream->request.persistent_mem.offset =
+ sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
+ stream->request.persistent_mem.size = module->persistent_size;
+
+ stream->request.scratch_mem.offset =
+ sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
+ stream->request.scratch_mem.size = dsp->scratch_size;
+
+ dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
+ runtime->id);
+ dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
+ stream->request.persistent_mem.offset,
+ stream->request.persistent_mem.size);
+ dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
+ stream->request.scratch_mem.offset,
+ stream->request.scratch_mem.size);
return 0;
}
@@ -1630,6 +1637,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
config.clock_frequency = mclk;
config.mode = mode;
config.clock_divider = clock_divider;
+ if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+ config.channels = 4;
+ else
+ config.channels = 2;
trace_hsw_device_config_req(&config);
@@ -1673,34 +1684,283 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
dx->entries_no, state);
- memcpy(&hsw->dx, dx, sizeof(*dx));
- return 0;
+ return ret;
}
-/* Used to save state into hsw->dx_reply */
-int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
- u32 *offset, u32 *size, u32 *source)
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+ int mod_id, int offset)
{
- struct sst_hsw_ipc_dx_memory_item *dx_mem;
- struct sst_hsw_ipc_dx_reply *dx_reply;
- int entry_no;
+ struct sst_dsp *dsp = hsw->dsp;
+ struct sst_module *module;
+ struct sst_module_runtime *runtime;
+ int err;
- dx_reply = &hsw->dx;
- entry_no = dx_reply->entries_no;
+ module = sst_module_get_from_id(dsp, mod_id);
+ if (module == NULL) {
+ dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
+ mod_id);
+ return NULL;
+ }
+
+ runtime = sst_module_runtime_new(module, mod_id, NULL);
+ if (runtime == NULL) {
+ dev_err(dsp->dev, "error: failed to create module %d runtime\n",
+ mod_id);
+ return NULL;
+ }
+
+ err = sst_module_runtime_alloc_blocks(runtime, offset);
+ if (err < 0) {
+ dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
+ mod_id);
+ sst_module_runtime_free(runtime);
+ return NULL;
+ }
+
+ dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
+ mod_id);
+ return runtime;
+}
+
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
+{
+ sst_module_runtime_free_blocks(runtime);
+ sst_module_runtime_free(runtime);
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+ struct sst_dsp *sst = hsw->dsp;
+ u32 item, offset, size;
+ int ret = 0;
- trace_ipc_request("PM get Dx state", entry_no);
+ trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
- if (item >= entry_no)
+ if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+ dev_err(hsw->dev,
+ "error: number of FW context regions greater than %d\n",
+ SST_HSW_MAX_DX_REGIONS);
+ memset(&hsw->dx, 0, sizeof(hsw->dx));
return -EINVAL;
+ }
+
+ ret = sst_dsp_dma_get_channel(sst, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+ return ret;
+ }
+
+ /* set on-demond mode on engine 0 channel 3 */
+ sst_dsp_shim_update_bits(sst, SST_HMDC,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+ SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+ for (item = 0; item < hsw->dx.entries_no; item++) {
+ if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+ && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+ && hsw->dx.mem_info[item].offset <
+ DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+ offset = hsw->dx.mem_info[item].offset
+ - DSP_DRAM_ADDR_OFFSET;
+ size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+ ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+ sst->addr.lpe_base + offset, size);
+ if (ret < 0) {
+ dev_err(hsw->dev,
+ "error: FW context dump failed\n");
+ memset(&hsw->dx, 0, sizeof(hsw->dx));
+ goto out;
+ }
+ }
+ }
+
+out:
+ sst_dsp_dma_put_channel(sst);
+ return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+ struct sst_dsp *sst = hsw->dsp;
+ u32 item, offset, size;
+ int ret;
+
+ for (item = 0; item < hsw->dx.entries_no; item++) {
+ if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+ && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+ && hsw->dx.mem_info[item].offset <
+ DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+ offset = hsw->dx.mem_info[item].offset
+ - DSP_DRAM_ADDR_OFFSET;
+ size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+ ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+ hsw->dx_context_paddr + offset, size);
+ if (ret < 0) {
+ dev_err(hsw->dev,
+ "error: FW context restore failed\n");
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void sst_hsw_drop_all(struct sst_hsw *hsw)
+{
+ struct ipc_message *msg, *tmp;
+ unsigned long flags;
+ int tx_drop_cnt = 0, rx_drop_cnt = 0;
- dx_mem = &dx_reply->mem_info[item];
- *offset = dx_mem->offset;
- *size = dx_mem->size;
- *source = dx_mem->source;
+ /* drop all TX and Rx messages before we stall + reset DSP */
+ spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+ list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
+ list_move(&msg->list, &hsw->empty_list);
+ tx_drop_cnt++;
+ }
+
+ list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
+ list_move(&msg->list, &hsw->empty_list);
+ rx_drop_cnt++;
+ }
+
+ spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+
+ if (tx_drop_cnt || rx_drop_cnt)
+ dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
+ tx_drop_cnt, rx_drop_cnt);
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+ struct sst_dsp *dsp = hsw->dsp;
+ int ret;
+
+ dev_dbg(hsw->dev, "loading audio DSP....");
+
+ ret = sst_dsp_wake(dsp);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+ return -ENODEV;
+ }
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+ return ret;
+ }
+
+ ret = sst_fw_reload(hsw->sst_fw);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: SST FW reload failed\n");
+ sst_dsp_dma_put_channel(dsp);
+ return -ENOMEM;
+ }
+ sst_dsp_dma_put_channel(dsp);
return 0;
}
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+ struct sst_dsp *dsp = hsw->dsp;
+ int ret;
+
+ dev_dbg(hsw->dev, "restoring audio DSP....");
+
+ ret = sst_dsp_dma_get_channel(dsp, 0);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+ return ret;
+ }
+
+ ret = sst_hsw_dx_state_restore(hsw);
+ if (ret < 0) {
+ dev_err(hsw->dev, "error: SST FW context restore failed\n");
+ sst_dsp_dma_put_channel(dsp);
+ return -ENOMEM;
+ }
+ sst_dsp_dma_put_channel(dsp);
+
+ /* wait for DSP boot completion */
+ sst_dsp_boot(dsp);
+
+ return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+ int ret;
+
+ dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+ ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+ if (ret < 0)
+ return ret;
+
+ sst_dsp_stall(hsw->dsp);
+
+ ret = sst_hsw_dx_state_dump(hsw);
+ if (ret < 0)
+ return ret;
+
+ sst_hsw_drop_all(hsw);
+
+ return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+ sst_fw_unload(hsw->sst_fw);
+ sst_block_free_scratch(hsw->dsp);
+
+ hsw->boot_complete = false;
+
+ sst_dsp_sleep(hsw->dsp);
+
+ return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+ struct device *dev = hsw->dev;
+ int ret;
+
+ dev_dbg(dev, "audio dsp runtime resume\n");
+
+ if (hsw->boot_complete)
+ return 1; /* tell caller no action is required */
+
+ ret = sst_hsw_dsp_restore(hsw);
+ if (ret < 0)
+ dev_err(dev, "error: audio DSP boot failure\n");
+
+ ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+ msecs_to_jiffies(IPC_BOOT_MSECS));
+ if (ret == 0) {
+ dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+ return -EIO;
+ }
+
+ /* Set ADSP SSP port settings */
+ ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
+ SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+ SST_HSW_DEVICE_CLOCK_MASTER, 9);
+ if (ret < 0)
+ dev_err(dev, "error: SSP re-initialization failed\n");
+
+ return ret;
+}
+#endif
+
static int msg_empty_list_init(struct sst_hsw *hsw)
{
int i;
@@ -1718,12 +1978,6 @@ static int msg_empty_list_init(struct sst_hsw *hsw)
return 0;
}
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
- struct sst_module *scratch)
-{
- hsw->scratch = scratch;
-}
-
struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
{
return hsw->dsp;
@@ -1738,7 +1992,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
{
struct sst_hsw_ipc_fw_version version;
struct sst_hsw *hsw;
- struct sst_fw *hsw_sst_fw;
int ret;
dev_dbg(dev, "initialising Audio DSP IPC\n");
@@ -1780,12 +2033,19 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
goto dsp_err;
}
+ /* allocate DMA buffer for context storage */
+ hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+ SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+ if (hsw->dx_context == NULL) {
+ ret = -ENOMEM;
+ goto dma_err;
+ }
+
/* keep the DSP in reset state for base FW loading */
sst_dsp_reset(hsw->dsp);
- hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
-
- if (hsw_sst_fw == NULL) {
+ hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
+ if (hsw->sst_fw == NULL) {
ret = -ENODEV;
dev_err(dev, "error: failed to load firmware\n");
goto fw_err;
@@ -1797,7 +2057,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
msecs_to_jiffies(IPC_BOOT_MSECS));
if (ret == 0) {
ret = -EIO;
- dev_err(hsw->dev, "error: ADSP boot timeout\n");
+ dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+ sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
goto boot_err;
}
@@ -1816,8 +2078,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
boot_err:
sst_dsp_reset(hsw->dsp);
- sst_fw_free(hsw_sst_fw);
+ sst_fw_free(hsw->sst_fw);
fw_err:
+ dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+ hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
sst_dsp_free(hsw->dsp);
dsp_err:
kthread_stop(hsw->tx_thread);
@@ -1834,6 +2099,8 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
sst_dsp_reset(hsw->dsp);
sst_fw_free_all(hsw->dsp);
+ dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+ hsw->dx_context, hsw->dx_context_paddr);
sst_dsp_free(hsw->dsp);
kfree(hsw->scratch);
kthread_stop(hsw->tx_thread);
diff --git a/sound/soc/intel/sst-haswell-ipc.h b/sound/soc/intel/sst-haswell-ipc.h
index 2ac194a6d04b..138e894ab413 100644
--- a/sound/soc/intel/sst-haswell-ipc.h
+++ b/sound/soc/intel/sst-haswell-ipc.h
@@ -21,8 +21,10 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
-#define SST_HSW_NO_CHANNELS 2
+#define SST_HSW_NO_CHANNELS 4
#define SST_HSW_MAX_DX_REGIONS 14
+#define SST_HSW_DX_CONTEXT_SIZE (640 * 1024)
+#define SST_HSW_CHANNELS_ALL 0xffffffff
#define SST_HSW_FW_LOG_CONFIG_DWORDS 12
#define SST_HSW_GLOBAL_LOG 15
@@ -40,6 +42,7 @@ struct sst_hsw_stream;
struct sst_hsw_log_stream;
struct sst_pdata;
struct sst_module;
+struct sst_module_runtime;
extern struct sst_ops haswell_ops;
/* Stream Allocate Path ID */
@@ -84,6 +87,7 @@ enum sst_hsw_device_mclk {
enum sst_hsw_device_mode {
SST_HSW_DEVICE_CLOCK_SLAVE = 0,
SST_HSW_DEVICE_CLOCK_MASTER = 1,
+ SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
};
/* DX Power State */
@@ -295,7 +299,8 @@ struct sst_hsw_ipc_device_config_req {
u32 clock_frequency;
u32 mode;
u16 clock_divider;
- u16 reserved;
+ u8 channels;
+ u8 reserved;
} __attribute__((packed));
/* Audio Data formats */
@@ -430,8 +435,7 @@ int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
enum sst_hsw_interleaving style);
int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
- struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
- u32 entry_point);
+ struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
struct sst_hsw_stream *stream, u32 offset, u32 size);
int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
@@ -484,7 +488,16 @@ int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
- struct sst_module *scratch);
+
+/* runtime module management */
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+ int mod_id, int offset);
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
+
+/* PM */
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
+int sst_hsw_dsp_load(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
#endif
diff --git a/sound/soc/intel/sst-haswell-pcm.c b/sound/soc/intel/sst-haswell-pcm.c
index 33fc5c3abf55..0180b386c421 100644
--- a/sound/soc/intel/sst-haswell-pcm.c
+++ b/sound/soc/intel/sst-haswell-pcm.c
@@ -18,6 +18,7 @@
#include <linux/dma-mapping.h>
#include <linux/slab.h>
#include <linux/delay.h>
+#include <linux/pm_runtime.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <sound/core.h>
@@ -73,6 +74,13 @@ static const u32 volume_map[] = {
#define HSW_PCM_PERIODS_MAX 64
#define HSW_PCM_PERIODS_MIN 2
+#define HSW_PCM_DAI_ID_SYSTEM 0
+#define HSW_PCM_DAI_ID_OFFLOAD0 1
+#define HSW_PCM_DAI_ID_OFFLOAD1 2
+#define HSW_PCM_DAI_ID_LOOPBACK 3
+#define HSW_PCM_DAI_ID_CAPTURE 4
+
+
static const struct snd_pcm_hardware hsw_pcm_hardware = {
.info = SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
@@ -89,22 +97,39 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
.buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
};
+struct hsw_pcm_module_map {
+ int dai_id;
+ enum sst_hsw_module_id mod_id;
+};
+
/* private data for each PCM DSP stream */
struct hsw_pcm_data {
int dai_id;
struct sst_hsw_stream *stream;
+ struct sst_module_runtime *runtime;
+ struct sst_module_runtime_context context;
+ struct snd_pcm *hsw_pcm;
u32 volume[2];
struct snd_pcm_substream *substream;
struct snd_compr_stream *cstream;
unsigned int wpos;
struct mutex mutex;
bool allocated;
+ int persistent_offset;
+};
+
+enum hsw_pm_state {
+ HSW_PM_STATE_D3 = 0,
+ HSW_PM_STATE_D0 = 1,
};
/* private data for the driver */
struct hsw_priv_data {
/* runtime DSP */
struct sst_hsw *hsw;
+ struct device *dev;
+ enum hsw_pm_state pm_state;
+ struct snd_soc_card *soc_card;
/* page tables */
struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@ -138,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(platform);
struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
struct sst_hsw *hsw = pdata->hsw;
u32 volume;
mutex_lock(&pcm_data->mutex);
+ pm_runtime_get_sync(pdata->dev);
if (!pcm_data->stream) {
pcm_data->volume[0] =
hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
pcm_data->volume[1] =
hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
mutex_unlock(&pcm_data->mutex);
return 0;
}
@@ -160,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
if (ucontrol->value.integer.value[0] ==
ucontrol->value.integer.value[1]) {
volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
+ /* apply volume value to all channels */
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
} else {
volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
@@ -168,6 +198,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
}
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
mutex_unlock(&pcm_data->mutex);
return 0;
}
@@ -175,21 +207,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
+ struct hsw_priv_data *pdata =
+ snd_soc_platform_get_drvdata(platform);
struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
struct sst_hsw *hsw = pdata->hsw;
u32 volume;
mutex_lock(&pcm_data->mutex);
+ pm_runtime_get_sync(pdata->dev);
if (!pcm_data->stream) {
ucontrol->value.integer.value[0] =
hsw_ipc_to_mixer(pcm_data->volume[0]);
ucontrol->value.integer.value[1] =
hsw_ipc_to_mixer(pcm_data->volume[1]);
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
mutex_unlock(&pcm_data->mutex);
return 0;
}
@@ -198,6 +234,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
mutex_unlock(&pcm_data->mutex);
return 0;
@@ -206,16 +245,18 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
static int hsw_volume_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
u32 volume;
+ pm_runtime_get_sync(pdata->dev);
+
if (ucontrol->value.integer.value[0] ==
ucontrol->value.integer.value[1]) {
volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
- sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
+ sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
} else {
volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
@@ -225,23 +266,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
}
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
return 0;
}
static int hsw_volume_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
- struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
- struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+ struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+ struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
struct sst_hsw *hsw = pdata->hsw;
unsigned int volume = 0;
+ pm_runtime_get_sync(pdata->dev);
sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
return 0;
}
@@ -252,23 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
static const struct snd_kcontrol_new hsw_volume_controls[] = {
/* Global DSP volume */
SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
- ARRAY_SIZE(volume_map) -1, 0,
+ ARRAY_SIZE(volume_map) - 1, 0,
hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
/* Offload 0 volume */
SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
- ARRAY_SIZE(volume_map), 0,
+ ARRAY_SIZE(volume_map) - 1, 0,
hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
/* Offload 1 volume */
SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
- ARRAY_SIZE(volume_map), 0,
- hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
- /* Loopback volume */
- SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
- ARRAY_SIZE(volume_map), 0,
+ ARRAY_SIZE(volume_map) - 1, 0,
hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
/* Mic Capture volume */
- SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
- ARRAY_SIZE(volume_map), 0,
+ SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
+ ARRAY_SIZE(volume_map) - 1, 0,
hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
};
@@ -354,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
/* DSP stream type depends on DAI ID */
switch (rtd->cpu_dai->id) {
case 0:
- stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
- module_id = SST_HSW_MODULE_PCM_SYSTEM;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+ module_id = SST_HSW_MODULE_PCM_SYSTEM;
+ }
+ else {
+ stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+ module_id = SST_HSW_MODULE_PCM_CAPTURE;
+ }
break;
case 1:
case 2:
@@ -368,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
module_id = SST_HSW_MODULE_PCM_REFERENCE;
break;
- case 4:
- stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
- module_id = SST_HSW_MODULE_PCM_CAPTURE;
- break;
default:
dev_err(rtd->dev, "error: invalid DAI ID %d\n",
rtd->cpu_dai->id);
@@ -421,13 +465,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- /* we only support stereo atm */
channels = params_channels(params);
- if (channels != 2) {
- dev_err(rtd->dev, "error: invalid channels %d\n", channels);
- return -EINVAL;
- }
-
map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
map, SST_HSW_CHANNEL_CONFIG_STEREO);
@@ -478,35 +516,23 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- /* we use hardcoded memory offsets atm, will be updated for new FW */
- if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
- sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
- SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
- sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
- 0x449400, 0x4000);
- sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
- 0x400000, 0);
- } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
- sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
- SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
-
- sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
- module_data->offset, module_data->size);
- sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
- 0x44d400, 0x3800);
-
- sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
- module_data->offset, module_data->size);
- sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
- 0x400000, 0);
- }
+ sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+ pcm_data->runtime);
ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
if (ret < 0) {
dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
return ret;
}
- pcm_data->allocated = true;
+
+ if (!pcm_data->allocated) {
+ /* Set previous saved volume */
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+ 0, pcm_data->volume[0]);
+ sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+ 1, pcm_data->volume[1]);
+ pcm_data->allocated = true;
+ }
ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
if (ret < 0)
@@ -558,7 +584,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
pos = frames_to_bytes(runtime,
(runtime->control->appl_ptr % runtime->buffer_size));
- dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+ dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
/* let alsa know we have play a period */
snd_pcm_period_elapsed(substream);
@@ -580,7 +606,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
offset = bytes_to_frames(runtime, position);
ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
- dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+ dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
position, ppos);
return offset;
}
@@ -596,6 +622,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
pcm_data = &pdata->pcm[rtd->cpu_dai->id];
mutex_lock(&pcm_data->mutex);
+ pm_runtime_get_sync(pdata->dev);
snd_soc_pcm_set_drvdata(rtd, pcm_data);
pcm_data->substream = substream;
@@ -606,16 +633,12 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
hsw_notify_pointer, pcm_data);
if (pcm_data->stream == NULL) {
dev_err(rtd->dev, "error: failed to create stream\n");
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
mutex_unlock(&pcm_data->mutex);
return -EINVAL;
}
- /* Set previous saved volume */
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
- 0, pcm_data->volume[0]);
- sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
- 1, pcm_data->volume[1]);
-
mutex_unlock(&pcm_data->mutex);
return 0;
}
@@ -645,6 +668,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
pcm_data->stream = NULL;
out:
+ pm_runtime_mark_last_busy(pdata->dev);
+ pm_runtime_put_autosuspend(pdata->dev);
mutex_unlock(&pcm_data->mutex);
return ret;
}
@@ -660,6 +685,56 @@ static struct snd_pcm_ops hsw_pcm_ops = {
.page = snd_pcm_sgbuf_ops_page,
};
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+ {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
+ {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
+ {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
+ {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
+ {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[i];
+
+ /* create new runtime module, use same offset if recreated */
+ pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+ mod_map[i].mod_id, pcm_data->persistent_offset);
+ if (pcm_data->runtime == NULL)
+ goto err;
+ pcm_data->persistent_offset =
+ pcm_data->runtime->persistent_offset;
+ }
+
+ return 0;
+
+err:
+ for (--i; i >= 0; i--) {
+ pcm_data = &pdata->pcm[i];
+ sst_hsw_runtime_module_free(pcm_data->runtime);
+ }
+
+ return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+ struct hsw_pcm_data *pcm_data;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+ pcm_data = &pdata->pcm[i];
+
+ sst_hsw_runtime_module_free(pcm_data->runtime);
+ }
+}
+
static void hsw_pcm_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -670,6 +745,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
struct snd_pcm *pcm = rtd->pcm;
struct snd_soc_platform *platform = rtd->platform;
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+ struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
struct device *dev = pdata->dma_dev;
int ret = 0;
@@ -686,18 +762,18 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
return ret;
}
}
+ priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
return ret;
}
#define HSW_FORMATS \
- (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE |\
- SNDRV_PCM_FMTBIT_S8)
+ (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
static struct snd_soc_dai_driver hsw_dais[] = {
{
.name = "System Pin",
+ .id = HSW_PCM_DAI_ID_SYSTEM,
.playback = {
.stream_name = "System Playback",
.channels_min = 2,
@@ -705,10 +781,18 @@ static struct snd_soc_dai_driver hsw_dais[] = {
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
},
+ .capture = {
+ .stream_name = "Analog Capture",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+ },
},
{
/* PCM */
.name = "Offload0 Pin",
+ .id = HSW_PCM_DAI_ID_OFFLOAD0,
.playback = {
.stream_name = "Offload0 Playback",
.channels_min = 2,
@@ -720,6 +804,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
{
/* PCM */
.name = "Offload1 Pin",
+ .id = HSW_PCM_DAI_ID_OFFLOAD1,
.playback = {
.stream_name = "Offload1 Playback",
.channels_min = 2,
@@ -730,6 +815,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
},
{
.name = "Loopback Pin",
+ .id = HSW_PCM_DAI_ID_LOOPBACK,
.capture = {
.stream_name = "Loopback Capture",
.channels_min = 2,
@@ -738,16 +824,6 @@ static struct snd_soc_dai_driver hsw_dais[] = {
.formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
},
},
- {
- .name = "Capture Pin",
- .capture = {
- .stream_name = "Analog Capture",
- .channels_min = 2,
- .channels_max = 2,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
- },
- },
};
static const struct snd_soc_dapm_widget widgets[] = {
@@ -778,9 +854,20 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
{
struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
- struct device *dma_dev = pdata->dma_dev;
+ struct device *dma_dev, *dev;
int i, ret = 0;
+ if (!pdata)
+ return -ENODEV;
+
+ dev = platform->dev;
+ dma_dev = pdata->dma_dev;
+
+ priv_data->hsw = pdata->dsp;
+ priv_data->dev = platform->dev;
+ priv_data->pm_state = HSW_PM_STATE_D0;
+ priv_data->soc_card = platform->component.card;
+
/* allocate DSP buffer page tables */
for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
@@ -803,6 +890,16 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
}
}
+ /* allocate runtime modules */
+ hsw_pcm_create_modules(priv_data);
+
+ /* enable runtime PM with auto suspend */
+ pm_runtime_set_autosuspend_delay(platform->dev,
+ SST_RUNTIME_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(platform->dev);
+ pm_runtime_enable(platform->dev);
+ pm_runtime_idle(platform->dev);
+
return 0;
err:
@@ -821,6 +918,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
snd_soc_platform_get_drvdata(platform);
int i;
+ pm_runtime_disable(platform->dev);
+ hsw_pcm_free_modules(priv_data);
+
for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
if (hsw_dais[i].playback.channels_min)
snd_dma_free_pages(&priv_data->dmab[i][0]);
@@ -898,10 +998,181 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+ return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+
+ if (pdata->pm_state == HSW_PM_STATE_D3)
+ return 0;
+
+ sst_hsw_dsp_runtime_suspend(hsw);
+ sst_hsw_dsp_runtime_sleep(hsw);
+ pdata->pm_state = HSW_PM_STATE_D3;
+
+ return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ int ret;
+
+ if (pdata->pm_state == HSW_PM_STATE_D0)
+ return 0;
+
+ ret = sst_hsw_dsp_load(hsw);
+ if (ret < 0) {
+ dev_err(dev, "failed to reload %d\n", ret);
+ return ret;
+ }
+
+ ret = hsw_pcm_create_modules(pdata);
+ if (ret < 0) {
+ dev_err(dev, "failed to create modules %d\n", ret);
+ return ret;
+ }
+
+ ret = sst_hsw_dsp_runtime_resume(hsw);
+ if (ret < 0)
+ return ret;
+ else if (ret == 1) /* no action required */
+ return 0;
+
+ pdata->pm_state = HSW_PM_STATE_D0;
+ return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle NULL
+#define hsw_pcm_runtime_suspend NULL
+#define hsw_pcm_runtime_resume NULL
+#endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+
+static void hsw_pcm_complete(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i, err;
+
+ if (pdata->pm_state == HSW_PM_STATE_D0)
+ return;
+
+ err = sst_hsw_dsp_load(hsw);
+ if (err < 0) {
+ dev_err(dev, "failed to reload %d\n", err);
+ return;
+ }
+
+ err = hsw_pcm_create_modules(pdata);
+ if (err < 0) {
+ dev_err(dev, "failed to create modules %d\n", err);
+ return;
+ }
+
+ for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+ pcm_data = &pdata->pcm[i];
+
+ if (!pcm_data->substream)
+ continue;
+
+ err = sst_module_runtime_restore(pcm_data->runtime,
+ &pcm_data->context);
+ if (err < 0)
+ dev_err(dev, "failed to restore context for PCM %d\n", i);
+ }
+
+ snd_soc_resume(pdata->soc_card->dev);
+
+ err = sst_hsw_dsp_runtime_resume(hsw);
+ if (err < 0)
+ return;
+ else if (err == 1) /* no action required */
+ return;
+
+ pdata->pm_state = HSW_PM_STATE_D0;
+ return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+ struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+ struct sst_hsw *hsw = pdata->hsw;
+ struct hsw_pcm_data *pcm_data;
+ int i, err;
+
+ if (pdata->pm_state == HSW_PM_STATE_D3)
+ return 0;
+ /* suspend all active streams */
+ for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+ pcm_data = &pdata->pcm[i];
+
+ if (!pcm_data->substream)
+ continue;
+ dev_dbg(dev, "suspending pcm %d\n", i);
+ snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+ /* We need to wait until the DSP FW stops the streams */
+ msleep(2);
+ }
+
+ snd_soc_suspend(pdata->soc_card->dev);
+ snd_soc_poweroff(pdata->soc_card->dev);
+
+ /* enter D3 state and stall */
+ sst_hsw_dsp_runtime_suspend(hsw);
+
+ /* preserve persistent memory */
+ for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+ pcm_data = &pdata->pcm[i];
+
+ if (!pcm_data->substream)
+ continue;
+
+ dev_dbg(dev, "saving context pcm %d\n", i);
+ err = sst_module_runtime_save(pcm_data->runtime,
+ &pcm_data->context);
+ if (err < 0)
+ dev_err(dev, "failed to save context for PCM %d\n", i);
+ }
+
+ /* put the DSP to sleep */
+ sst_hsw_dsp_runtime_sleep(hsw);
+ pdata->pm_state = HSW_PM_STATE_D3;
+
+ return 0;
+}
+
+#else
+#define hsw_pcm_prepare NULL
+#define hsw_pcm_complete NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+ .runtime_idle = hsw_pcm_runtime_idle,
+ .runtime_suspend = hsw_pcm_runtime_suspend,
+ .runtime_resume = hsw_pcm_runtime_resume,
+ .prepare = hsw_pcm_prepare,
+ .complete = hsw_pcm_complete,
+};
+
static struct platform_driver hsw_pcm_driver = {
.driver = {
.name = "haswell-pcm-audio",
.owner = THIS_MODULE,
+ .pm = &hsw_pcm_pm,
+
},
.probe = hsw_pcm_dev_probe,
diff --git a/sound/soc/intel/sst-mfld-platform-compress.c b/sound/soc/intel/sst-mfld-platform-compress.c
index 59467775c9b8..395168986462 100644
--- a/sound/soc/intel/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/sst-mfld-platform-compress.c
@@ -67,8 +67,11 @@ static int sst_platform_compr_open(struct snd_compr_stream *cstream)
goto out_ops;
}
stream->compr_ops = sst->compr_ops;
-
stream->id = 0;
+
+ /* Turn on LPE */
+ sst->compr_ops->power(sst->dev, true);
+
sst_set_stream_status(stream, SST_PLATFORM_INIT);
runtime->private_data = stream;
return 0;
@@ -83,6 +86,9 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
int ret_val = 0, str_id;
stream = cstream->runtime->private_data;
+ /* Turn off LPE */
+ sst->compr_ops->power(sst->dev, false);
+
/*need to check*/
str_id = stream->id;
if (str_id)
diff --git a/sound/soc/intel/sst-mfld-platform-pcm.c b/sound/soc/intel/sst-mfld-platform-pcm.c
index aa9b600dfc9b..6032f18693be 100644
--- a/sound/soc/intel/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/sst-mfld-platform-pcm.c
@@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
{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[] = {
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
{
- .name = "Headset-cpu-dai",
- .id = 0,
- .playback = {
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- },
- .capture = {
- .channels_min = 1,
- .channels_max = 5,
- .rates = SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S24_LE,
- },
-},
-{
- .name = "Compress-cpu-dai",
- .compress_dai = 1,
- .playback = {
- .channels_min = SST_STEREO,
- .channels_max = SST_STEREO,
- .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
-},
-};
+
+ return sst_send_pipe_gains(dai, stream, mute);
+}
/* helper functions */
void sst_set_stream_status(struct sst_runtime_stream *stream,
@@ -451,12 +427,133 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream,
return snd_pcm_lib_free_pages(substream);
}
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ int ret = 0;
+
+ if (!dai->active) {
+ ret = sst_handle_vb_timer(dai, true);
+ if (ret)
+ return ret;
+ ret = send_ssp_cmd(dai, dai->name, 1);
+ }
+ return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ if (!dai->active) {
+ send_ssp_cmd(dai, dai->name, 0);
+ sst_handle_vb_timer(dai, false);
+ }
+}
+
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,
+ .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_compr_dai_ops = {
+ .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_be_dai_ops = {
+ .startup = sst_enable_ssp,
+ .shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+ .name = "media-cpu-dai",
+ .ops = &sst_media_dai_ops,
+ .playback = {
+ .stream_name = "Headset Playback",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "Headset Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "compress-cpu-dai",
+ .compress_dai = 1,
+ .ops = &sst_compr_dai_ops,
+ .playback = {
+ .stream_name = "Compress Playback",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+/* BE CPU Dais */
+{
+ .name = "ssp0-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp0 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp0 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "ssp1-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp1 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp1 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
+{
+ .name = "ssp2-port",
+ .ops = &sst_be_dai_ops,
+ .playback = {
+ .stream_name = "ssp2 Tx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "ssp2 Rx",
+ .channels_min = SST_STEREO,
+ .channels_max = SST_STEREO,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+},
};
static int sst_platform_open(struct snd_pcm_substream *substream)
@@ -609,6 +706,7 @@ static int sst_platform_probe(struct platform_device *pdev)
pdata->pdev_strm_map = dpcm_strm_map;
pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
drv->pdata = pdata;
+ drv->pdev = pdev;
mutex_init(&drv->lock);
dev_set_drvdata(&pdev->dev, drv);
diff --git a/sound/soc/intel/sst-mfld-platform.h b/sound/soc/intel/sst-mfld-platform.h
index 19f83ec51613..79c8d1246a8f 100644
--- a/sound/soc/intel/sst-mfld-platform.h
+++ b/sound/soc/intel/sst-mfld-platform.h
@@ -117,6 +117,7 @@ struct compress_sst_ops {
int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
int (*set_metadata)(struct device *dev, unsigned int str_id,
struct snd_compr_metadata *mdata);
+ int (*power)(struct device *dev, bool state);
};
struct sst_ops {
@@ -153,6 +154,10 @@ struct sst_device {
struct sst_data;
int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
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);
diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile
new file mode 100644
index 000000000000..fd21726361b5
--- /dev/null
+++ b/sound/soc/intel/sst/Makefile
@@ -0,0 +1,7 @@
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c
new file mode 100644
index 000000000000..8a8d56a146e7
--- /dev/null
+++ b/sound/soc/intel/sst/sst.c
@@ -0,0 +1,437 @@
+/*
+ * sst.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+ return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+ return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+ union interrupt_reg_mrfld isr;
+ union ipc_header_mrfld header;
+ union sst_imr_reg_mrfld imr;
+ struct ipc_post *msg = NULL;
+ unsigned int size = 0;
+ struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+ irqreturn_t retval = IRQ_HANDLED;
+
+ /* Interrupt arrived, check src */
+ isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+ if (isr.part.done_interrupt) {
+ /* Clear done bit */
+ spin_lock(&drv->ipc_spin_lock);
+ header.full = sst_shim_read64(drv->shim,
+ drv->ipc_reg.ipcx);
+ header.p.header_high.part.done = 0;
+ sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+ /* write 1 to clear status register */;
+ isr.part.done_interrupt = 1;
+ sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+ spin_unlock(&drv->ipc_spin_lock);
+
+ /* we can send more messages to DSP so trigger work */
+ queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+ retval = IRQ_HANDLED;
+ }
+
+ if (isr.part.busy_interrupt) {
+ /* message from dsp so copy that */
+ spin_lock(&drv->ipc_spin_lock);
+ imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+ imr.part.busy_interrupt = 1;
+ sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+ spin_unlock(&drv->ipc_spin_lock);
+ header.full = sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+ if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+ drv->ops->clear_interrupt(drv);
+ return IRQ_HANDLED;
+ }
+
+ if (header.p.header_high.part.large) {
+ size = header.p.header_low_payload;
+ if (sst_validate_mailbox_size(size)) {
+ memcpy_fromio(msg->mailbox_data,
+ drv->mailbox + drv->mailbox_recv_offset, size);
+ } else {
+ dev_err(drv->dev,
+ "Mailbox not copied, payload size is: %u\n", size);
+ header.p.header_low_payload = 0;
+ }
+ }
+
+ msg->mrfld_header = header;
+ msg->is_process_reply =
+ sst_is_process_reply(header.p.header_high.part.msg_id);
+ spin_lock(&drv->rx_msg_lock);
+ list_add_tail(&msg->node, &drv->rx_list);
+ spin_unlock(&drv->rx_msg_lock);
+ drv->ops->clear_interrupt(drv);
+ retval = IRQ_WAKE_THREAD;
+ }
+ return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+ struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+ struct ipc_post *__msg, *msg = NULL;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+ if (list_empty(&drv->rx_list)) {
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ return IRQ_HANDLED;
+ }
+
+ list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+ list_del(&msg->node);
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ if (msg->is_process_reply)
+ drv->ops->process_message(msg);
+ else
+ drv->ops->process_reply(drv, msg);
+
+ if (msg->is_large)
+ kfree(msg->mailbox_data);
+ kfree(msg);
+ spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+ }
+ spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+ return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+ int ret = 0;
+
+ ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+ IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+ true, true, false, true);
+
+ if (ret < 0) {
+ dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+ .interrupt = intel_sst_interrupt_mrfld,
+ .irq_thread = intel_sst_irq_thread_mrfld,
+ .clear_interrupt = intel_sst_clear_intr_mrfld,
+ .start = sst_start_mrfld,
+ .reset = intel_sst_reset_dsp_mrfld,
+ .post_message = sst_post_message_mrfld,
+ .process_reply = sst_process_reply_mrfld,
+ .save_dsp_context = sst_save_dsp_context_v2,
+ .alloc_stream = sst_alloc_stream_mrfld,
+ .post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+ switch (sst->dev_id) {
+ case SST_MRFLD_PCI_ID:
+ case SST_BYT_ACPI_ID:
+ case SST_CHV_ACPI_ID:
+ sst->tstamp = SST_TIME_STAMP_MRFLD;
+ sst->ops = &mrfld_ops;
+ return 0;
+
+ default:
+ dev_err(sst->dev,
+ "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+ return -EINVAL;
+ };
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+ struct intel_sst_drv *ctx = container_of(work,
+ struct intel_sst_drv, ipc_post_msg_wq);
+
+ ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+ INIT_LIST_HEAD(&ctx->memcpy_list);
+ INIT_LIST_HEAD(&ctx->rx_list);
+ INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+ INIT_LIST_HEAD(&ctx->block_list);
+ INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+ init_waitqueue_head(&ctx->wait_queue);
+
+ ctx->post_msg_wq =
+ create_singlethread_workqueue("sst_post_msg_wq");
+ if (!ctx->post_msg_wq)
+ return -EBUSY;
+ return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+ mutex_init(&ctx->sst_lock);
+ spin_lock_init(&ctx->rx_msg_lock);
+ spin_lock_init(&ctx->ipc_spin_lock);
+ spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+ struct device *dev, unsigned int dev_id)
+{
+ *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+ if (!(*ctx))
+ return -ENOMEM;
+
+ (*ctx)->dev = dev;
+ (*ctx)->dev_id = dev_id;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+ int ret = 0, i;
+
+ if (!ctx->pdata)
+ return -EINVAL;
+
+ if (!ctx->pdata->probe_data)
+ return -EINVAL;
+
+ memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+ ret = sst_driver_ops(ctx);
+ if (ret != 0)
+ return -EINVAL;
+
+ sst_init_locks(ctx);
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ /* pvt_id 0 reserved for async messages */
+ ctx->pvt_id = 1;
+ ctx->stream_cnt = 0;
+ ctx->fw_in_mem = NULL;
+ /* we use memcpy, so set to 0 */
+ ctx->use_dma = 0;
+ ctx->use_lli = 0;
+
+ if (sst_workqueue_init(ctx))
+ return -EINVAL;
+
+ ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+ ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+ ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+ dev_info(ctx->dev, "Got drv data max stream %d\n",
+ ctx->info.max_streams);
+
+ for (i = 1; i <= ctx->info.max_streams; i++) {
+ struct stream_info *stream = &ctx->streams[i];
+
+ memset(stream, 0, sizeof(*stream));
+ stream->pipe_id = PIPE_RSVD;
+ mutex_init(&stream->lock);
+ }
+
+ /* Register the ISR */
+ ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+ ctx->ops->irq_thread, 0, SST_DRV_NAME,
+ ctx);
+ if (ret)
+ goto do_free_mem;
+
+ dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+ /* default intr are unmasked so set this as masked */
+ sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+ ctx->qos = devm_kzalloc(ctx->dev,
+ sizeof(struct pm_qos_request), GFP_KERNEL);
+ if (!ctx->qos) {
+ ret = -ENOMEM;
+ goto do_free_mem;
+ }
+ pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
+ PM_QOS_DEFAULT_VALUE);
+
+ dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+ ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+ ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+ if (ret) {
+ dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+ goto do_free_mem;
+ }
+ sst_register(ctx->dev);
+ return 0;
+
+do_free_mem:
+ destroy_workqueue(ctx->post_msg_wq);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+ pm_runtime_get_noresume(ctx->dev);
+ pm_runtime_disable(ctx->dev);
+ sst_unregister(ctx->dev);
+ sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+ flush_scheduled_work();
+ destroy_workqueue(ctx->post_msg_wq);
+ pm_qos_remove_request(ctx->qos);
+ kfree(ctx->fw_sg_list.src);
+ kfree(ctx->fw_sg_list.dst);
+ ctx->fw_sg_list.list_len = 0;
+ kfree(ctx->fw_in_mem);
+ ctx->fw_in_mem = NULL;
+ sst_memcpy_free_resources(ctx);
+ ctx = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+static inline void sst_save_shim64(struct intel_sst_drv *ctx,
+ void __iomem *shim,
+ struct sst_shim_regs64 *shim_regs)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+
+ shim_regs->imrx = sst_shim_read64(shim, SST_IMRX),
+
+ spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
+ void __iomem *shim,
+ struct sst_shim_regs64 *shim_regs)
+{
+ unsigned long irq_flags;
+
+ /*
+ * we only need to restore IMRX for this case, rest will be
+ * initialize by FW or driver when firmware is loaded
+ */
+ spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+ sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
+ spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+ pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+ pm_runtime_use_autosuspend(ctx->dev);
+ /*
+ * For acpi devices, the actual physical device state is
+ * initially active. So change the state to active before
+ * enabling the pm
+ */
+ pm_runtime_enable(ctx->dev);
+
+ if (acpi_disabled)
+ pm_runtime_set_active(ctx->dev);
+ else
+ pm_runtime_put_noidle(ctx->dev);
+
+ sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+ int ret = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state == SST_RESET) {
+ dev_dbg(dev, "LPE is already in RESET state, No action\n");
+ return 0;
+ }
+ /* save fw context */
+ if (ctx->ops->save_dsp_context(ctx))
+ return -EBUSY;
+
+ /* Move the SST state to Reset */
+ sst_set_fw_state_locked(ctx, SST_RESET);
+
+ synchronize_irq(ctx->irq_num);
+ flush_workqueue(ctx->post_msg_wq);
+
+ /* save the shim registers because PMC doesn't save state */
+ sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+
+ return ret;
+}
+
+static int intel_sst_runtime_resume(struct device *dev)
+{
+ int ret = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state == SST_RESET) {
+ ret = sst_load_fw(ctx);
+ if (ret) {
+ dev_err(dev, "FW download fail %d\n", ret);
+ sst_set_fw_state_locked(ctx, SST_RESET);
+ }
+ }
+ return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+ .runtime_suspend = intel_sst_runtime_suspend,
+ .runtime_resume = intel_sst_runtime_resume,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h
new file mode 100644
index 000000000000..7f4bbfcbc6f5
--- /dev/null
+++ b/sound/soc/intel/sst/sst.h
@@ -0,0 +1,546 @@
+/*
+ * sst.h - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID 0x80860F28
+#define SST_CHV_ACPI_ID 0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+ SST_FW_LOADING = 1,
+ SST_FW_RUNNING,
+ SST_RESET,
+ SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+ SST_SET_ALGO = 0,
+ SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT 1000
+
+#define FW_SIGNATURE_SIZE 4
+
+/* stream states */
+enum sst_stream_states {
+ STREAM_UN_INIT = 0, /* Freed/Not used stream */
+ STREAM_RUNNING = 1, /* Running */
+ STREAM_PAUSED = 2, /* Paused stream */
+ STREAM_DECODE = 3, /* stream is in decoding only state */
+ STREAM_INIT = 4, /* stream init, waiting for data */
+ STREAM_RESET = 5, /* force reset on recovery */
+};
+
+enum sst_ram_type {
+ SST_IRAM = 1,
+ SST_DRAM = 2,
+ SST_DDR = 5,
+ SST_CUSTOM_INFO = 7, /* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+union sst_pisr_reg {
+ struct {
+ u32 pssp0:1;
+ u32 pssp1:1;
+ u32 rsvd0:3;
+ u32 dmac:1;
+ u32 rsvd1:26;
+ } part;
+ u32 full;
+};
+
+union sst_pimr_reg {
+ struct {
+ u32 ssp0:1;
+ u32 ssp1:1;
+ u32 rsvd0:3;
+ u32 dmac:1;
+ u32 rsvd1:10;
+ u32 ssp0_sc:1;
+ u32 ssp1_sc:1;
+ u32 rsvd2:3;
+ u32 dmac_sc:1;
+ u32 rsvd3:10;
+ } part;
+ u32 full;
+};
+
+union config_status_reg_mrfld {
+ struct {
+ u64 lpe_reset:1;
+ u64 lpe_reset_vector:1;
+ u64 runstall:1;
+ u64 pwaitmode:1;
+ u64 clk_sel:3;
+ u64 rsvd2:1;
+ u64 sst_clk:3;
+ u64 xt_snoop:1;
+ u64 rsvd3:4;
+ u64 clk_sel1:6;
+ u64 clk_enable:3;
+ u64 rsvd4:6;
+ u64 slim0baseclk:1;
+ u64 rsvd:32;
+ } part;
+ u64 full;
+};
+
+union interrupt_reg_mrfld {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+union sst_imr_reg_mrfld {
+ struct {
+ u64 done_interrupt:1;
+ u64 busy_interrupt:1;
+ u64 rsvd:62;
+ } part;
+ u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+ bool condition;
+ int ret_code;
+ void *data;
+ u32 size;
+ bool on;
+ u32 msg_id;
+ u32 drv_id;
+ struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @ops : stream operation pb/cp/drm...
+ * @bufs: stream buffer list
+ * @lock : stream mutex for protecting state
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @str_type : stream type
+ * @cumm_bytes : cummulative bytes decoded
+ * @str_type : stream type
+ * @src : stream source
+ */
+struct stream_info {
+ unsigned int status;
+ unsigned int prev;
+ unsigned int ops;
+ struct mutex lock;
+
+ void *pcm_substream;
+ void (*period_elapsed)(void *pcm_substream);
+
+ unsigned int sfreq;
+ u32 cumm_bytes;
+
+ void *compr_cb_param;
+ void (*compr_cb)(void *compr_cb_param);
+
+ void *drain_cb_param;
+ void (*drain_notify)(void *drain_cb_param);
+
+ unsigned int num_ch;
+ unsigned int pipe_id;
+ unsigned int str_id;
+ unsigned int task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+ unsigned char signature[FW_SIGNATURE_SIZE];
+ u32 file_size;
+ u32 modules;
+ u32 file_format;
+ u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+ unsigned char signature[FW_SIGNATURE_SIZE];
+ u32 mod_size;
+ u32 blocks;
+ u32 type;
+ u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+ enum sst_ram_type type;
+ u32 size;
+ u32 ram_offset;
+ u32 rsvd;
+};
+
+struct sst_runtime_param {
+ struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+ struct scatterlist *src;
+ struct scatterlist *dst;
+ int list_len;
+ unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+ struct list_head memcpylist;
+ void *dstn;
+ const void *src;
+ u32 size;
+ bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+ SST_LIB_NOT_FOUND = 0,
+ SST_LIB_FOUND,
+ SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+ const char *name; /*Library name*/
+ u32 id; /*Module ID*/
+ u32 entry_pt; /*Module entry point*/
+ u8 status; /*module status*/
+ u8 rsvd1;
+ u16 rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+ phys_addr_t current_base;
+ int avail;
+ unsigned int count;
+};
+
+struct sst_ipc_reg {
+ int ipcx;
+ int ipcd;
+};
+
+struct sst_shim_regs64 {
+ u64 csr;
+ u64 pisr;
+ u64 pimr;
+ u64 isrx;
+ u64 isrd;
+ u64 imrx;
+ u64 imrd;
+ u64 ipcx;
+ u64 ipcd;
+ u64 isrsc;
+ u64 isrlpesc;
+ u64 imrsc;
+ u64 imrlpesc;
+ u64 ipcsc;
+ u64 ipclpesc;
+ u64 clkctl;
+ u64 csr2;
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ * devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @shim_regs64: Struct to save shim registers
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos : PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+ int sst_state;
+ int irq_num;
+ unsigned int dev_id;
+ void __iomem *ddr;
+ void __iomem *shim;
+ void __iomem *mailbox;
+ void __iomem *iram;
+ void __iomem *dram;
+ unsigned int mailbox_add;
+ unsigned int iram_base;
+ unsigned int dram_base;
+ unsigned int shim_phy_add;
+ unsigned int iram_end;
+ unsigned int dram_end;
+ unsigned int ddr_end;
+ unsigned int ddr_base;
+ unsigned int mailbox_recv_offset;
+ struct sst_shim_regs64 *shim_regs64;
+ struct list_head block_list;
+ struct list_head ipc_dispatch_list;
+ struct sst_platform_info *pdata;
+ struct list_head rx_list;
+ struct work_struct ipc_post_msg_wq;
+ wait_queue_head_t wait_queue;
+ struct workqueue_struct *post_msg_wq;
+ unsigned int tstamp;
+ /* str_id 0 is not used */
+ struct stream_info streams[MAX_NUM_STREAMS+1];
+ spinlock_t ipc_spin_lock;
+ spinlock_t block_lock;
+ spinlock_t rx_msg_lock;
+ struct pci_dev *pci;
+ struct device *dev;
+ volatile long unsigned pvt_id;
+ struct mutex sst_lock;
+ unsigned int stream_cnt;
+ unsigned int csr_value;
+ void *fw_in_mem;
+ struct sst_sg_list fw_sg_list, library_list;
+ struct intel_sst_ops *ops;
+ struct sst_info info;
+ struct pm_qos_request *qos;
+ unsigned int use_dma;
+ unsigned int use_lli;
+ atomic_t fw_clear_context;
+ bool lib_dwnld_reqd;
+ struct list_head memcpy_list;
+ struct sst_ipc_reg ipc_reg;
+ struct sst_mem_mgr lib_mem_mgr;
+ /*
+ * Holder for firmware name. Due to async call it needs to be
+ * persistent till worker thread gets called
+ */
+ char firmware_name[20];
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+ irqreturn_t (*interrupt)(int, void *);
+ irqreturn_t (*irq_thread)(int, void *);
+ void (*clear_interrupt)(struct intel_sst_drv *ctx);
+ int (*start)(struct intel_sst_drv *ctx);
+ int (*reset)(struct intel_sst_drv *ctx);
+ void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+ int (*post_message)(struct intel_sst_drv *ctx,
+ struct ipc_post *msg, bool sync);
+ void (*process_message)(struct ipc_post *msg);
+ void (*set_bypass)(bool set);
+ int (*save_dsp_context)(struct intel_sst_drv *sst);
+ void (*restore_dsp_context)(void);
+ int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+ void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
+ struct snd_sst_bytes_v2 *sbytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param,
+ struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+ int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *ctx,
+ struct ipc_post *msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
+
+int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+ u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+ u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+ u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+ int task_id, int ipc_msg, int cmd_id, int pipe_id,
+ size_t mbox_data_len, const void *mbox_data, void **data,
+ bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
+void sst_init_stream(struct stream_info *stream,
+ int codec, int sst_id, int ops, u8 slot);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+ int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+ struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+ struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+ int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+ int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+ struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+#endif
diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c
new file mode 100644
index 000000000000..31124aa4434e
--- /dev/null
+++ b/sound/soc/intel/sst/sst_acpi.c
@@ -0,0 +1,383 @@
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ * Authors: Ramesh Babu K V <Ramesh.Babu@intel.com>
+ * Authors: Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include "../sst-mfld-platform.h"
+#include "../sst-dsp.h"
+#include "sst.h"
+
+struct sst_machines {
+ char codec_id[32];
+ char board[32];
+ char machine[32];
+ void (*machine_quirk)(void);
+ char firmware[32];
+ struct sst_platform_info *pdata;
+
+};
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START 0xff2c0000
+#define SST_BYT_IRAM_PHY_END 0xff2d4000
+#define SST_BYT_DRAM_PHY_START 0xff300000
+#define SST_BYT_DRAM_PHY_END 0xff320000
+#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END 0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR 0xff340000
+#define SST_BYT_MBOX_PHY_ADDR 0xff344000
+#define SST_BYT_DMA0_PHY_ADDR 0xff298000
+#define SST_BYT_DMA1_PHY_ADDR 0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR 0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR 0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET 0x80000
+#define BYT_FW_MOD_TABLE_SIZE 0x100
+#define BYT_FW_MOD_OFFSET (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+ .use_elf = false,
+ .max_streams = 25,
+ .iram_start = SST_BYT_IRAM_PHY_START,
+ .iram_end = SST_BYT_IRAM_PHY_END,
+ .iram_use = true,
+ .dram_start = SST_BYT_DRAM_PHY_START,
+ .dram_end = SST_BYT_DRAM_PHY_END,
+ .dram_use = true,
+ .imr_start = SST_BYT_IMR_VIRT_START,
+ .imr_end = SST_BYT_IMR_VIRT_END,
+ .imr_use = true,
+ .mailbox_start = SST_BYT_MBOX_PHY_ADDR,
+ .num_probes = 0,
+ .lpe_viewpt_rqd = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+ .ipc_offset = 0,
+ .mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info byt_lib_dnld_info = {
+ .mod_base = SST_BYT_IMR_VIRT_START,
+ .mod_end = SST_BYT_IMR_VIRT_END,
+ .mod_table_offset = BYT_FW_MOD_TABLE_OFFSET,
+ .mod_table_size = BYT_FW_MOD_TABLE_SIZE,
+ .mod_ddr_dnld = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+ .shim_offset = 0x140000,
+ .shim_size = 0x000100,
+ .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+ .ssp0_offset = 0xa0000,
+ .ssp0_size = 0x1000,
+ .dma0_offset = 0x98000,
+ .dma0_size = 0x4000,
+ .dma1_offset = 0x9c000,
+ .dma1_size = 0x4000,
+ .iram_offset = 0x0c0000,
+ .iram_size = 0x14000,
+ .dram_offset = 0x100000,
+ .dram_size = 0x28000,
+ .mbox_offset = 0x144000,
+ .mbox_size = 0x1000,
+ .acpi_lpe_res_index = 0,
+ .acpi_ddr_index = 2,
+ .acpi_ipc_irq_index = 5,
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+ .probe_data = &byt_fwparse_info,
+ .ipc_info = &byt_ipc_info,
+ .lib_info = &byt_lib_dnld_info,
+ .res_info = &byt_rvp_res_info,
+ .platform = "sst-mfld-platform",
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail.
+ */
+static struct sst_platform_info chv_platform_data = {
+ .probe_data = &byt_fwparse_info,
+ .ipc_info = &byt_ipc_info,
+ .lib_info = &byt_lib_dnld_info,
+ .res_info = &byt_rvp_res_info,
+ .platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+ struct resource *rsrc;
+ struct platform_device *pdev = to_platform_device(ctx->dev);
+
+ /* All ACPI resource request here */
+ /* Get Shim addr */
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ ctx->pdata->res_info->acpi_lpe_res_index);
+ if (!rsrc) {
+ dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+ return -EIO;
+ }
+ dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+ (unsigned int)resource_size(rsrc));
+
+ ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+ ctx->iram_end = ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+ dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+ ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+ ctx->pdata->res_info->iram_size);
+ if (!ctx->iram) {
+ dev_err(ctx->dev, "unable to map IRAM");
+ return -EIO;
+ }
+
+ ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+ ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+ dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+ ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+ ctx->pdata->res_info->dram_size);
+ if (!ctx->dram) {
+ dev_err(ctx->dev, "unable to map DRAM");
+ return -EIO;
+ }
+
+ ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+ dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+ ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+ ctx->pdata->res_info->shim_size);
+ if (!ctx->shim) {
+ dev_err(ctx->dev, "unable to map SHIM");
+ return -EIO;
+ }
+
+ /* reassign physical address to LPE viewpoint address */
+ ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+ /* Get mailbox addr */
+ ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+ dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+ ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+ ctx->pdata->res_info->mbox_size);
+ if (!ctx->mailbox) {
+ dev_err(ctx->dev, "unable to map mailbox");
+ return -EIO;
+ }
+
+ /* reassign physical address to LPE viewpoint address */
+ ctx->mailbox_add = ctx->info.mailbox_start;
+
+ rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+ ctx->pdata->res_info->acpi_ddr_index);
+ if (!rsrc) {
+ dev_err(ctx->dev, "Invalid DDR base from IFWI");
+ return -EIO;
+ }
+ ctx->ddr_base = rsrc->start;
+ ctx->ddr_end = rsrc->end;
+ dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+ ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+ resource_size(rsrc));
+ if (!ctx->ddr) {
+ dev_err(ctx->dev, "unable to map DDR");
+ return -EIO;
+ }
+
+ /* Find the IRQ */
+ ctx->irq_num = platform_get_irq(pdev,
+ ctx->pdata->res_info->acpi_ipc_irq_index);
+ return 0;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+ void *context, void **ret)
+{
+ *(bool *)context = true;
+ return AE_OK;
+}
+
+static struct sst_machines *sst_acpi_find_machine(
+ struct sst_machines *machines)
+{
+ struct sst_machines *mach;
+ bool found = false;
+
+ for (mach = machines; mach->codec_id; mach++)
+ if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
+ sst_acpi_mach_match,
+ &found, NULL)) && found)
+ return mach;
+
+ return NULL;
+}
+
+int sst_acpi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int ret = 0;
+ struct intel_sst_drv *ctx;
+ const struct acpi_device_id *id;
+ struct sst_machines *mach;
+ struct platform_device *mdev;
+ struct platform_device *plat_dev;
+ unsigned int dev_id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return -ENODEV;
+ dev_dbg(dev, "for %s", id->id);
+
+ mach = (struct sst_machines *)id->driver_data;
+ mach = sst_acpi_find_machine(mach);
+ if (mach == NULL) {
+ dev_err(dev, "No matching machine driver found\n");
+ return -ENODEV;
+ }
+
+ ret = kstrtouint(id->id, 16, &dev_id);
+ if (ret < 0) {
+ dev_err(dev, "Unique device id conversion error: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+ plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
+ if (plat_dev == NULL) {
+ dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
+ return -ENODEV;
+ }
+
+ /* Create platform device for sst machine driver */
+ mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
+ if (mdev == NULL) {
+ dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
+ return -ENODEV;
+ }
+
+ ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+ if (ret < 0)
+ return ret;
+
+ /* Fill sst platform data */
+ ctx->pdata = mach->pdata;
+ strcpy(ctx->firmware_name, mach->firmware);
+
+ ret = sst_platform_get_resources(ctx);
+ if (ret)
+ return ret;
+
+ ret = sst_context_init(ctx);
+ if (ret < 0)
+ return ret;
+
+ /* need to save shim registers in BYT */
+ ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
+ GFP_KERNEL);
+ if (!ctx->shim_regs64) {
+ return -ENOMEM;
+ goto do_sst_cleanup;
+ }
+
+ sst_configure_runtime_pm(ctx);
+ platform_set_drvdata(pdev, ctx);
+ return ret;
+
+do_sst_cleanup:
+ sst_context_cleanup(ctx);
+ platform_set_drvdata(pdev, NULL);
+ dev_err(ctx->dev, "failed with %d\n", ret);
+ return ret;
+}
+
+/**
+* intel_sst_remove - remove function
+*
+* @pdev: platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+int sst_acpi_remove(struct platform_device *pdev)
+{
+ struct intel_sst_drv *ctx;
+
+ ctx = platform_get_drvdata(pdev);
+ sst_context_cleanup(ctx);
+ platform_set_drvdata(pdev, NULL);
+ return 0;
+}
+
+static struct sst_machines sst_acpi_bytcr[] = {
+ {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
+ &byt_rvp_platform_data },
+ {},
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+static struct sst_machines sst_acpi_chv[] = {
+ {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin",
+ &chv_platform_data },
+ {},
+};
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+ { "80860F28", (unsigned long)&sst_acpi_bytcr},
+ { "808622A8", (unsigned long) &sst_acpi_chv},
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+ .driver = {
+ .name = "intel_sst_acpi",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(sst_acpi_ids),
+ .pm = &intel_sst_pm,
+ },
+ .probe = sst_acpi_probe,
+ .remove = sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c
new file mode 100644
index 000000000000..5f75ef3cdd22
--- /dev/null
+++ b/sound/soc/intel/sst/sst_drv_interface.c
@@ -0,0 +1,686 @@
+/*
+ * sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz) (((pcm_wd_sz + 15) >> 4) << 1)
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+ struct stream_info *stream;
+ int ret = 0;
+
+ stream = get_stream_info(ctx, str_id);
+ if (stream) {
+ /* str_id is valid, so stream is alloacted */
+ ret = sst_free_stream(ctx, str_id);
+ if (ret)
+ sst_clean_stream(&ctx->streams[str_id]);
+ return ret;
+ } else {
+ dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+ }
+ return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param,
+ struct snd_sst_lib_download **lib_dnld)
+{
+ int retval;
+
+ retval = ctx->ops->alloc_stream(ctx, str_param);
+ if (retval > 0)
+ dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+ return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.sfreq;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.externalsr;
+ case SST_CODEC_TYPE_MP3:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+ switch (str_param->codec) {
+ case SST_CODEC_TYPE_PCM:
+ return str_param->sparams.uc.pcm_params.num_chan;
+ case SST_CODEC_TYPE_MP3:
+ return str_param->sparams.uc.mp3_params.num_chan;
+ case SST_CODEC_TYPE_AAC:
+ return str_param->sparams.uc.aac_params.num_chan;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+ struct snd_sst_params *str_param)
+{
+ int retval;
+ struct stream_info *str_info;
+
+ /* stream is not allocated, we are allocating */
+ retval = ctx->ops->alloc_stream(ctx, str_param);
+ if (retval <= 0) {
+ return -EIO;
+ }
+ /* store sampling freq */
+ str_info = &ctx->streams[retval];
+ str_info->sfreq = sst_get_sfreq(str_param);
+
+ return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ dev_dbg(ctx->dev, "state:%d", state);
+ if (state == true)
+ return pm_runtime_get_sync(dev);
+ else
+ return sst_pm_runtime_put(ctx);
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+ struct snd_sst_params *str_param)
+{
+ int retval;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (!str_param)
+ return -EINVAL;
+
+ retval = sst_get_stream(ctx, str_param);
+ if (retval > 0)
+ ctx->stream_cnt++;
+ else
+ dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+ return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+ struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+ int str_id, retval;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ retval = pm_runtime_get_sync(ctx->dev);
+ if (retval < 0)
+ return retval;
+
+ str_id = sst_get_stream(ctx, str_params);
+ if (str_id > 0) {
+ dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+ stream = &ctx->streams[str_id];
+ stream->compr_cb = cb->compr_cb;
+ stream->compr_cb_param = cb->param;
+ stream->drain_notify = cb->drain_notify;
+ stream->drain_cb_param = cb->drain_cb_param;
+ } else {
+ dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+ str_id = -EINVAL;
+ sst_pm_runtime_put(ctx);
+ }
+ return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+ int retval;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream) {
+ dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+ return -EINVAL;
+ }
+
+ if (stream->status == STREAM_RESET) {
+ dev_dbg(dev, "stream in reset state...\n");
+ stream->status = STREAM_UN_INIT;
+
+ retval = 0;
+ goto put;
+ }
+
+ retval = sst_free_stream(ctx, str_id);
+put:
+ stream->compr_cb_param = NULL;
+ stream->compr_cb = NULL;
+
+ if (retval)
+ dev_err(dev, "free stream returned err %d\n", retval);
+
+ dev_dbg(dev, "End\n");
+ return retval;
+
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+ unsigned long bytes)
+{
+ struct stream_info *stream;
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ int offset;
+ void __iomem *addr;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ /* update bytes sent */
+ stream->cumm_bytes += bytes;
+ dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(ctx->mailbox + ctx->tstamp)
+ +(str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+
+ fw_tstamp.bytes_copied = stream->cumm_bytes;
+ dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+ fw_tstamp.bytes_copied, bytes);
+
+ addr = ((void *)(ctx->mailbox + ctx->tstamp)) +
+ (str_id * sizeof(fw_tstamp));
+ offset = offsetof(struct snd_sst_tstamp, bytes_copied);
+ sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+ return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+ unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+ retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+ sizeof(*metadata), metadata, NULL,
+ true, true, true, false);
+
+ return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+ unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_RUNNING;
+ return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+ unsigned int str_id)
+{
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+ struct snd_compr_tstamp *tstamp)
+{
+ struct snd_sst_tstamp fw_tstamp = {0,};
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(ctx->mailbox + ctx->tstamp)
+ +(str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+ dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+ tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+ tstamp->pcm_frames = fw_tstamp.frames_decoded;
+ tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+ (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24)));
+ tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+ dev_dbg(dev, "PCM = %u\n", tstamp->pcm_io_frames);
+ dev_dbg(dev, "Ptr Query on strid = %d copied_total %d, decodec %d\n",
+ str_id, tstamp->copied_total, tstamp->pcm_frames);
+ dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+ return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+ caps->num_codecs = NUM_CODEC;
+ caps->min_fragment_size = MIN_FRAGMENT_SIZE; /* 50KB */
+ caps->max_fragment_size = MAX_FRAGMENT_SIZE; /* 1024KB */
+ caps->min_fragments = MIN_FRAGMENT;
+ caps->max_fragments = MAX_FRAGMENT;
+ caps->codecs[0] = SND_AUDIOCODEC_MP3;
+ caps->codecs[1] = SND_AUDIOCODEC_AAC;
+ return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+ .num_descriptors = 1,
+ .descriptor[0].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[0].bit_rate[0] = 320,
+ .descriptor[0].bit_rate[1] = 192,
+ .descriptor[0].num_bitrates = 2,
+ .descriptor[0].profiles = 0,
+ .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+ .descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+ .num_descriptors = 2,
+ .descriptor[1].max_ch = 2,
+ .descriptor[0].sample_rates[0] = 48000,
+ .descriptor[0].sample_rates[1] = 44100,
+ .descriptor[0].sample_rates[2] = 32000,
+ .descriptor[0].sample_rates[3] = 16000,
+ .descriptor[0].sample_rates[4] = 8000,
+ .descriptor[0].num_sample_rates = 5,
+ .descriptor[1].bit_rate[0] = 320,
+ .descriptor[1].bit_rate[1] = 192,
+ .descriptor[1].num_bitrates = 2,
+ .descriptor[1].profiles = 0,
+ .descriptor[1].modes = 0,
+ .descriptor[1].formats =
+ (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+ SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+ if (codec->codec == SND_AUDIOCODEC_MP3)
+ *codec = caps_mp3;
+ else if (codec->codec == SND_AUDIOCODEC_AAC)
+ *codec = caps_aac;
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+ struct stream_info *stream;
+
+ dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+ str_id);
+ stream = &ctx->streams[str_id];
+ if (stream->compr_cb)
+ stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+ struct stream_info *stream;
+ int retval = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream) {
+ dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+ return -EINVAL;
+ }
+
+ if (stream->status == STREAM_RESET) {
+ /* silently fail here as we have cleaned the stream earlier */
+ dev_dbg(ctx->dev, "stream in reset state...\n");
+
+ retval = 0;
+ goto put;
+ }
+
+ retval = free_stream_context(ctx, str_id);
+put:
+ stream->pcm_substream = NULL;
+ stream->status = STREAM_UN_INIT;
+ stream->period_elapsed = NULL;
+ ctx->stream_cnt--;
+
+ if (retval)
+ dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+ dev_dbg(ctx->dev, "Exit\n");
+ return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+ struct pcm_stream_info *info,
+ struct snd_pcm_substream *substream,
+ struct snd_sst_tstamp *fw_tstamp)
+{
+ size_t delay_bytes, delay_frames;
+ size_t buffer_sz;
+ u32 pointer_bytes, pointer_samples;
+
+ dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+ fw_tstamp->ring_buffer_counter);
+ dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+ fw_tstamp->hardware_counter);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+ fw_tstamp->hardware_counter);
+ else
+ delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+ fw_tstamp->ring_buffer_counter);
+ delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+ buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+ div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+ pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+ dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+ info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+ info->pcm_delay = delay_frames / substream->runtime->channels;
+ dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+ info->buffer_ptr, info->pcm_delay);
+ return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+ struct stream_info *stream;
+ struct snd_pcm_substream *substream;
+ struct snd_sst_tstamp fw_tstamp;
+ unsigned int str_id;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_id = info->str_id;
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ if (!stream->pcm_substream)
+ return -EINVAL;
+ substream = stream->pcm_substream;
+
+ memcpy_fromio(&fw_tstamp,
+ ((void *)(ctx->mailbox + ctx->tstamp)
+ + (str_id * sizeof(fw_tstamp))),
+ sizeof(fw_tstamp));
+ return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_RUNNING;
+ sst_start_stream(ctx, str_id);
+
+ return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+ struct stream_info *str_info;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ str_info = get_stream_info(ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ str_info->prev = STREAM_UN_INIT;
+ str_info->status = STREAM_INIT;
+ return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+ int str_id = 0;
+ struct stream_info *stream;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ str_id = str_info->str_id;
+
+ if (ctx->sst_state != SST_FW_RUNNING)
+ return 0;
+
+ stream = get_stream_info(ctx, str_id);
+ if (!stream)
+ return -EINVAL;
+
+ dev_dbg(ctx->dev, "setting the period ptrs\n");
+ stream->pcm_substream = str_info->arg;
+ stream->period_elapsed = str_info->period_elapsed;
+ stream->sfreq = str_info->sfreq;
+ stream->prev = stream->status;
+ stream->status = STREAM_INIT;
+ dev_dbg(ctx->dev,
+ "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+ stream->pcm_substream, stream->period_elapsed,
+ stream->sfreq, stream->status);
+
+ return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+ struct snd_sst_bytes_v2 *bytes)
+{
+ int ret_val = 0;
+ struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+ if (NULL == bytes)
+ return -EINVAL;
+ ret_val = pm_runtime_get_sync(ctx->dev);
+ if (ret_val < 0)
+ return ret_val;
+
+ ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+ sst_pm_runtime_put(ctx);
+
+ return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+ .open = sst_open_pcm_stream,
+ .stream_init = sst_stream_init,
+ .stream_start = sst_stream_start,
+ .stream_drop = sst_stream_drop,
+ .stream_read_tstamp = sst_read_timestamp,
+ .send_byte_stream = sst_send_byte_stream,
+ .close = sst_close_pcm_stream,
+ .power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+ .open = sst_cdev_open,
+ .close = sst_cdev_close,
+ .stream_pause = sst_cdev_stream_pause,
+ .stream_pause_release = sst_cdev_stream_pause_release,
+ .stream_start = sst_cdev_stream_start,
+ .stream_drop = sst_cdev_stream_drop,
+ .stream_drain = sst_cdev_stream_drain,
+ .stream_partial_drain = sst_cdev_stream_partial_drain,
+ .tstamp = sst_cdev_tstamp,
+ .ack = sst_cdev_ack,
+ .get_caps = sst_cdev_caps,
+ .get_codec_caps = sst_cdev_codec_caps,
+ .set_metadata = sst_cdev_set_metadata,
+ .power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+ .name = "Intel(R) SST LPE",
+ .dev = NULL,
+ .ops = &pcm_ops,
+ .compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+ int ret_val;
+
+ sst_dsp_device.dev = dev;
+ ret_val = sst_register_dsp(&sst_dsp_device);
+ if (ret_val)
+ dev_err(dev, "Unable to register DSP with platform driver\n");
+
+ return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+ return sst_unregister_dsp(&sst_dsp_device);
+}
diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c
new file mode 100644
index 000000000000..484e60978477
--- /dev/null
+++ b/sound/soc/intel/sst/sst_ipc.c
@@ -0,0 +1,373 @@
+/*
+ * sst_ipc.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corporation
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+ u32 msg_id, u32 drv_id)
+{
+ struct sst_block *msg = NULL;
+
+ dev_dbg(ctx->dev, "Enter\n");
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return NULL;
+ msg->condition = false;
+ msg->on = true;
+ msg->msg_id = msg_id;
+ msg->drv_id = drv_id;
+ spin_lock_bh(&ctx->block_lock);
+ list_add_tail(&msg->node, &ctx->block_list);
+ spin_unlock_bh(&ctx->block_lock);
+
+ return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ * a) when its small message and block in not there, so silently ignore
+ * them
+ * b) when we are actually not able to find the block (bug perhaps)
+ *
+ * Since we have bit of small messages we can spam kernel log with err
+ * print on above so need to keep as debug prints which should be enabled
+ * via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+ u32 drv_id, u32 ipc, void *data, u32 size)
+{
+ struct sst_block *block = NULL;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ spin_lock_bh(&ctx->block_lock);
+ list_for_each_entry(block, &ctx->block_list, node) {
+ dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+ block->drv_id);
+ if (block->msg_id == ipc && block->drv_id == drv_id) {
+ dev_dbg(ctx->dev, "free up the block\n");
+ block->ret_code = result;
+ block->data = data;
+ block->size = size;
+ block->condition = true;
+ spin_unlock_bh(&ctx->block_lock);
+ wake_up(&ctx->wait_queue);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&ctx->block_lock);
+ dev_dbg(ctx->dev,
+ "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+ ipc, drv_id);
+ return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+ struct sst_block *block = NULL, *__block;
+
+ dev_dbg(ctx->dev, "Enter\n");
+ spin_lock_bh(&ctx->block_lock);
+ list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+ if (block == freed) {
+ pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+ /* toggle the index position of pvt_id */
+ list_del(&freed->node);
+ spin_unlock_bh(&ctx->block_lock);
+ kfree(freed->data);
+ freed->data = NULL;
+ kfree(freed);
+ return 0;
+ }
+ }
+ spin_unlock_bh(&ctx->block_lock);
+ dev_err(ctx->dev, "block is already freed!!!\n");
+ return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *ipc_msg, bool sync)
+{
+ struct ipc_post *msg = ipc_msg;
+ union ipc_header_mrfld header;
+ unsigned int loop_count = 0;
+ int retval = 0;
+ unsigned long irq_flags;
+
+ dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+ if (sync) {
+ while (header.p.header_high.part.busy) {
+ if (loop_count > 25) {
+ dev_err(sst_drv_ctx->dev,
+ "sst: Busy wait failed, cant send this msg\n");
+ retval = -EBUSY;
+ goto out;
+ }
+ cpu_relax();
+ loop_count++;
+ header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+ }
+ } else {
+ if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+ /* queue is empty, nothing to send */
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ dev_dbg(sst_drv_ctx->dev,
+ "Empty msg queue... NO Action\n");
+ return 0;
+ }
+
+ if (header.p.header_high.part.busy) {
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+ return 0;
+ }
+
+ /* copy msg from list */
+ msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+ struct ipc_post, node);
+ list_del(&msg->node);
+ }
+ dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+ msg->mrfld_header.p.header_high.full);
+ dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+ msg->mrfld_header.p.header_low_payload);
+
+ if (msg->mrfld_header.p.header_high.part.large)
+ memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+ msg->mailbox_data,
+ msg->mrfld_header.p.header_low_payload);
+
+ sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ kfree(msg->mailbox_data);
+ kfree(msg);
+ return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union interrupt_reg_mrfld isr;
+ union interrupt_reg_mrfld imr;
+ union ipc_header_mrfld clear_ipc;
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+ imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+ isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+ /* write 1 to clear*/
+ isr.part.busy_interrupt = 1;
+ sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+ /* Set IA done bit */
+ clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+ clear_ipc.p.header_high.part.busy = 0;
+ clear_ipc.p.header_high.part.done = 1;
+ clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+ sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+ /* un mask busy interrupt */
+ imr.part.busy_interrupt = 0;
+ sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+ spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+ void *msg)
+{
+ struct ipc_header_fw_init *init =
+ (struct ipc_header_fw_init *)msg;
+ int retval = 0;
+
+ dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+ if (init->result) {
+ sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+ dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+ init->result);
+ retval = init->result;
+ goto ret;
+ }
+
+ret:
+ sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *msg)
+{
+ u32 msg_id;
+ int str_id;
+ u32 data_size, i;
+ void *data_offset;
+ struct stream_info *stream;
+ union ipc_header_high msg_high;
+ u32 msg_low, pipe_id;
+
+ msg_high = msg->mrfld_header.p.header_high;
+ msg_low = msg->mrfld_header.p.header_low_payload;
+ msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+ data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+ data_size = msg_low - (sizeof(struct ipc_dsp_hdr));
+
+ switch (msg_id) {
+ case IPC_SST_PERIOD_ELAPSED_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0) {
+ dev_dbg(sst_drv_ctx->dev,
+ "Period elapsed rcvd for pipe id 0x%x\n",
+ pipe_id);
+ stream = &sst_drv_ctx->streams[str_id];
+ if (stream->period_elapsed)
+ stream->period_elapsed(stream->pcm_substream);
+ if (stream->compr_cb)
+ stream->compr_cb(stream->compr_cb_param);
+ }
+ break;
+
+ case IPC_IA_DRAIN_STREAM_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0) {
+ stream = &sst_drv_ctx->streams[str_id];
+ if (stream->drain_notify)
+ stream->drain_notify(stream->drain_cb_param);
+ }
+ break;
+
+ case IPC_IA_FW_ASYNC_ERR_MRFLD:
+ dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+ for (i = 0; i < (data_size/4); i++)
+ print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+ 16, 4, data_offset, data_size, false);
+ break;
+
+ case IPC_IA_FW_INIT_CMPLT_MRFLD:
+ process_fw_init(sst_drv_ctx, data_offset);
+ break;
+
+ case IPC_IA_BUF_UNDER_RUN_MRFLD:
+ pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+ str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+ if (str_id > 0)
+ dev_err(sst_drv_ctx->dev,
+ "Buffer under-run for pipe:%#x str_id:%d\n",
+ pipe_id, str_id);
+ break;
+
+ default:
+ dev_err(sst_drv_ctx->dev,
+ "Unrecognized async msg from FW msg_id %#x\n", msg_id);
+ }
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct ipc_post *msg)
+{
+ unsigned int drv_id;
+ void *data;
+ union ipc_header_high msg_high;
+ u32 msg_low;
+ struct ipc_dsp_hdr *dsp_hdr;
+ unsigned int cmd_id;
+
+ msg_high = msg->mrfld_header.p.header_high;
+ msg_low = msg->mrfld_header.p.header_low_payload;
+
+ dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+ msg->mrfld_header.p.header_high.full,
+ msg->mrfld_header.p.header_low_payload);
+
+ drv_id = msg_high.part.drv_id;
+
+ /* Check for async messages first */
+ if (drv_id == SST_ASYNC_DRV_ID) {
+ /*FW sent async large message*/
+ process_fw_async_msg(sst_drv_ctx, msg);
+ return;
+ }
+
+ /* FW sent short error response for an IPC */
+ if (msg_high.part.result && drv_id && !msg_high.part.large) {
+ /* 32-bit FW error code in msg_low */
+ dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+ sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, NULL, 0);
+ return;
+ }
+
+ /*
+ * Process all valid responses
+ * if it is a large message, the payload contains the size to
+ * copy from mailbox
+ **/
+ if (msg_high.part.large) {
+ data = kzalloc(msg_low, GFP_KERNEL);
+ if (!data)
+ return;
+ memcpy(data, (void *) msg->mailbox_data, msg_low);
+ /* Copy command id so that we can use to put sst to reset */
+ dsp_hdr = (struct ipc_dsp_hdr *)data;
+ cmd_id = dsp_hdr->cmd_id;
+ dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
+ if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, data, msg_low))
+ kfree(data);
+ } else {
+ sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+ msg_high.part.drv_id,
+ msg_high.part.msg_id, NULL, 0);
+ }
+
+}
diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c
new file mode 100644
index 000000000000..b580f96e25e5
--- /dev/null
+++ b/sound/soc/intel/sst/sst_loader.c
@@ -0,0 +1,456 @@
+/*
+ * sst_dsp.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+ /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+ * right count in words
+ */
+ __iowrite32_copy(dst, src, count/4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union config_status_reg_mrfld csr;
+
+ dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full |= 0x7;
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full &= ~(0x1);
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+ return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+ union config_status_reg_mrfld csr;
+
+ dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.full |= 0x7;
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+ csr.part.xt_snoop = 1;
+ csr.full &= ~(0x5);
+ sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+ csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+ dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+ csr.full);
+ return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+ struct fw_module_header **module, u32 *num_modules)
+{
+ struct sst_fw_header *header;
+ const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ /* Read the header information from the data pointer */
+ header = (struct sst_fw_header *)sst_fw_in_mem;
+ dev_dbg(ctx->dev,
+ "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+ header->signature, header->file_size, header->modules,
+ header->file_format, sizeof(*header));
+
+ /* verify FW */
+ if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+ (size != header->file_size + sizeof(*header))) {
+ /* Invalid FW signature */
+ dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+ return -EINVAL;
+ }
+ *num_modules = header->modules;
+ *module = (void *)sst_fw_in_mem + sizeof(*header);
+
+ return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+ void *destn, const void *src, u32 size, bool is_io)
+{
+ struct sst_memcpy_list *listnode;
+
+ listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+ if (listnode == NULL)
+ return -ENOMEM;
+ listnode->dstn = destn;
+ listnode->src = src;
+ listnode->size = size;
+ listnode->is_io = is_io;
+ list_add_tail(&listnode->memcpylist, memcpy_list);
+
+ return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx : driver context
+ * @module : FW module header
+ * @memcpy_list : Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+ struct fw_module_header *module, struct list_head *memcpy_list)
+{
+ struct fw_block_info *block;
+ u32 count;
+ int ret_val = 0;
+ void __iomem *ram_iomem;
+
+ dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+ module->signature, module->mod_size,
+ module->blocks, module->type);
+ dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+ block = (void *)module + sizeof(*module);
+
+ for (count = 0; count < module->blocks; count++) {
+ if (block->size <= 0) {
+ dev_err(sst_drv_ctx->dev, "block size invalid\n");
+ return -EINVAL;
+ }
+ switch (block->type) {
+ case SST_IRAM:
+ ram_iomem = sst_drv_ctx->iram;
+ break;
+ case SST_DRAM:
+ ram_iomem = sst_drv_ctx->dram;
+ break;
+ case SST_DDR:
+ ram_iomem = sst_drv_ctx->ddr;
+ break;
+ case SST_CUSTOM_INFO:
+ block = (void *)block + sizeof(*block) + block->size;
+ continue;
+ default:
+ dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+ block->type, count);
+ return -EINVAL;
+ }
+
+ ret_val = sst_fill_memcpy_list(memcpy_list,
+ ram_iomem + block->ram_offset,
+ (void *)block + sizeof(*block), block->size, 1);
+ if (ret_val)
+ return ret_val;
+
+ block = (void *)block + sizeof(*block) + block->size;
+ }
+ return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx : pointer to drv context
+ * @size : size of the firmware
+ * @fw_list : pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+ struct list_head *fw_list)
+{
+ struct fw_module_header *module;
+ u32 count, num_modules;
+ int ret_val;
+
+ ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+ if (ret_val)
+ return ret_val;
+
+ for (count = 0; count < num_modules; count++) {
+ ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+ if (ret_val)
+ return ret_val;
+ module = (void *)module + sizeof(*module) + module->mod_size;
+ }
+
+ return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+ struct sst_memcpy_list *listnode;
+
+ list_for_each_entry(listnode, memcpy_list, memcpylist) {
+ if (listnode->is_io == true)
+ memcpy32_toio((void __iomem *)listnode->dstn,
+ listnode->src, listnode->size);
+ else
+ memcpy(listnode->dstn, listnode->src, listnode->size);
+ }
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+ struct sst_memcpy_list *listnode, *tmplistnode;
+
+ /* Free the list */
+ if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+ list_for_each_entry_safe(listnode, tmplistnode,
+ &sst_drv_ctx->memcpy_list, memcpylist) {
+ list_del(&listnode->memcpylist);
+ kfree(listnode);
+ }
+ }
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+ const struct firmware *fw)
+{
+ int retval = 0;
+
+ sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+ if (!sst->fw_in_mem) {
+ retval = -ENOMEM;
+ goto end_release;
+ }
+ dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+ dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+ memcpy(sst->fw_in_mem, fw->data, fw->size);
+ retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+ if (retval) {
+ dev_err(sst->dev, "Failed to parse fw\n");
+ kfree(sst->fw_in_mem);
+ sst->fw_in_mem = NULL;
+ }
+
+end_release:
+ release_firmware(fw);
+ return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+ struct intel_sst_drv *ctx = context;
+
+ dev_dbg(ctx->dev, "Enter\n");
+
+ if (fw == NULL) {
+ dev_err(ctx->dev, "request fw failed\n");
+ return;
+ }
+
+ mutex_lock(&ctx->sst_lock);
+
+ if (ctx->sst_state != SST_RESET ||
+ ctx->fw_in_mem != NULL) {
+ if (fw != NULL)
+ release_firmware(fw);
+ mutex_unlock(&ctx->sst_lock);
+ return;
+ }
+
+ dev_dbg(ctx->dev, "Request Fw completed\n");
+ sst_cache_and_parse_fw(ctx, fw);
+ mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+ int retval = 0;
+ const struct firmware *fw;
+
+ retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+ if (fw == NULL) {
+ dev_err(sst->dev, "fw is returning as null\n");
+ return -EINVAL;
+ }
+ if (retval) {
+ dev_err(sst->dev, "request fw failed %d\n", retval);
+ return retval;
+ }
+ mutex_lock(&sst->sst_lock);
+ retval = sst_cache_and_parse_fw(sst, fw);
+ mutex_unlock(&sst->sst_lock);
+
+ return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+ unsigned int ddr_base)
+{
+ void __iomem *addr;
+ u32 bss_reset = 0;
+
+ addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+ memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+ bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+ addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+ memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+ sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+ dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+ int ret_val = 0;
+ struct sst_block *block;
+
+ dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+ if (sst_drv_ctx->sst_state != SST_RESET ||
+ sst_drv_ctx->sst_state == SST_SHUTDOWN)
+ return -EAGAIN;
+
+ if (!sst_drv_ctx->fw_in_mem) {
+ dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+ ret_val = sst_request_fw(sst_drv_ctx);
+ if (ret_val)
+ return ret_val;
+ }
+
+ BUG_ON(!sst_drv_ctx->fw_in_mem);
+ block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+ if (block == NULL)
+ return -ENOMEM;
+
+ /* Prevent C-states beyond C6 */
+ pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+ sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+ ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+ if (ret_val)
+ goto restore;
+
+ sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+ /* Write the DRAM/DCCM config before enabling FW */
+ if (sst_drv_ctx->ops->post_download)
+ sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+ /* bring sst out of reset */
+ ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+ if (ret_val)
+ goto restore;
+
+ ret_val = sst_wait_timeout(sst_drv_ctx, block);
+ if (ret_val) {
+ dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+ /* FW download failed due to timeout */
+ ret_val = -EBUSY;
+
+ }
+
+
+restore:
+ /* Re-enable Deeper C-states beyond C6 */
+ pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+ sst_free_block(sst_drv_ctx, block);
+ dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+ if (sst_drv_ctx->ops->restore_dsp_context)
+ sst_drv_ctx->ops->restore_dsp_context();
+ sst_drv_ctx->sst_state = SST_FW_RUNNING;
+ return ret_val;
+}
+
diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c
new file mode 100644
index 000000000000..3a0b3bf0af97
--- /dev/null
+++ b/sound/soc/intel/sst/sst_pci.c
@@ -0,0 +1,209 @@
+/*
+ * sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+ int ddr_base, ret = 0;
+ struct pci_dev *pci = ctx->pci;
+
+ ret = pci_request_regions(pci, SST_DRV_NAME);
+ if (ret)
+ return ret;
+
+ /* map registers */
+ /* DDR base */
+ if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+ ctx->ddr_base = pci_resource_start(pci, 0);
+ /* check that the relocated IMR base matches with FW Binary */
+ ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+ if (!ctx->pdata->lib_info) {
+ dev_err(ctx->dev, "lib_info pointer NULL\n");
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ if (ddr_base != ctx->pdata->lib_info->mod_base) {
+ dev_err(ctx->dev,
+ "FW LSP DDR BASE does not match with IFWI\n");
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ ctx->ddr_end = pci_resource_end(pci, 0);
+
+ ctx->ddr = pcim_iomap(pci, 0,
+ pci_resource_len(pci, 0));
+ if (!ctx->ddr) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+ } else {
+ ctx->ddr = NULL;
+ }
+ /* SHIM */
+ ctx->shim_phy_add = pci_resource_start(pci, 1);
+ ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+ if (!ctx->shim) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+ /* Shared SRAM */
+ ctx->mailbox_add = pci_resource_start(pci, 2);
+ ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+ if (!ctx->mailbox) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+ /* IRAM */
+ ctx->iram_end = pci_resource_end(pci, 3);
+ ctx->iram_base = pci_resource_start(pci, 3);
+ ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+ if (!ctx->iram) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+ /* DRAM */
+ ctx->dram_end = pci_resource_end(pci, 4);
+ ctx->dram_base = pci_resource_start(pci, 4);
+ ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+ if (!ctx->dram) {
+ ret = -EINVAL;
+ goto do_release_regions;
+ }
+ dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+ pci_release_regions(pci);
+ return 0;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci: PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+ const struct pci_device_id *pci_id)
+{
+ int ret = 0;
+ struct intel_sst_drv *sst_drv_ctx;
+ struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+ dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+ ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+ if (ret < 0)
+ return ret;
+
+ sst_drv_ctx->pdata = sst_pdata;
+ sst_drv_ctx->irq_num = pci->irq;
+ snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+ "%s%04x%s", "fw_sst_",
+ sst_drv_ctx->dev_id, ".bin");
+
+ ret = sst_context_init(sst_drv_ctx);
+ if (ret < 0)
+ return ret;
+
+ /* Init the device */
+ ret = pcim_enable_device(pci);
+ if (ret) {
+ dev_err(sst_drv_ctx->dev,
+ "device can't be enabled. Returned err: %d\n", ret);
+ goto do_free_drv_ctx;
+ }
+ sst_drv_ctx->pci = pci_dev_get(pci);
+ ret = sst_platform_get_resources(sst_drv_ctx);
+ if (ret < 0)
+ goto do_free_drv_ctx;
+
+ pci_set_drvdata(pci, sst_drv_ctx);
+ sst_configure_runtime_pm(sst_drv_ctx);
+
+ return ret;
+
+do_free_drv_ctx:
+ sst_context_cleanup(sst_drv_ctx);
+ dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+ return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci: PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+ struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+ sst_context_cleanup(sst_drv_ctx);
+ pci_dev_put(sst_drv_ctx->pci);
+ pci_release_regions(pci);
+ pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static struct pci_device_id intel_sst_ids[] = {
+ { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+ { 0, }
+};
+
+static struct pci_driver sst_driver = {
+ .name = SST_DRV_NAME,
+ .id_table = intel_sst_ids,
+ .probe = intel_sst_probe,
+ .remove = intel_sst_remove,
+#ifdef CONFIG_PM
+ .driver = {
+ .pm = &intel_sst_pm,
+ },
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c
new file mode 100644
index 000000000000..4b7720864492
--- /dev/null
+++ b/sound/soc/intel/sst/sst_pvt.c
@@ -0,0 +1,449 @@
+/*
+ * sst_pvt.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+ writel(value, addr + offset);
+ return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+ return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+ u64 val = 0;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+
+ return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+ memcpy_toio(addr + offset, &value, sizeof(value));
+ return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+ u64 val = 0;
+
+ memcpy_fromio(&val, addr + offset, sizeof(val));
+ return val;
+}
+
+void sst_set_fw_state_locked(
+ struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_drv_ctx->sst_state = sst_state;
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+ struct sst_block *block)
+{
+ int retval = 0;
+
+ if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+ block->condition)) {
+ /* event wake */
+ if (block->ret_code < 0) {
+ dev_err(sst_drv_ctx->dev,
+ "stream failed %d\n", block->ret_code);
+ retval = -EBUSY;
+ } else {
+ dev_dbg(sst_drv_ctx->dev, "event up\n");
+ retval = 0;
+ }
+ } else {
+ dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+ retval = -EINTR;
+ }
+ return retval;
+
+}
+
+unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
+{
+ unsigned long long val = 0;
+
+ switch (sst->dev_id) {
+ case SST_MRFLD_PCI_ID:
+ case SST_BYT_ACPI_ID:
+ val = sst_shim_read64(sst->shim, addr);
+ break;
+ }
+ return val;
+}
+
+void write_shim_data(struct intel_sst_drv *sst, int addr,
+ unsigned long long data)
+{
+ switch (sst->dev_id) {
+ case SST_MRFLD_PCI_ID:
+ case SST_BYT_ACPI_ID:
+ sst_shim_write64(sst->shim, addr, (u64) data);
+ break;
+ }
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+ int retval = 0;
+
+ /*
+ * NOTE:
+ * Observed that FW processes the alloc msg and replies even
+ * before the alloc thread has finished execution
+ */
+ dev_dbg(sst_drv_ctx->dev,
+ "waiting for condition %x ipc %d drv_id %d\n",
+ block->condition, block->msg_id, block->drv_id);
+ if (wait_event_timeout(sst_drv_ctx->wait_queue,
+ block->condition,
+ msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+ /* event wake */
+ dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+ block->condition);
+ dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+ block->ret_code);
+ retval = -block->ret_code;
+ } else {
+ block->on = false;
+ dev_err(sst_drv_ctx->dev,
+ "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+ block->condition, block->msg_id, sst_drv_ctx->sst_state);
+ sst_drv_ctx->sst_state = SST_RESET;
+
+ retval = -EBUSY;
+ }
+ return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+ struct ipc_post *msg;
+
+ msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+ if (!msg)
+ return -ENOMEM;
+ if (large) {
+ msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+ if (!msg->mailbox_data) {
+ kfree(msg);
+ return -ENOMEM;
+ }
+ } else {
+ msg->mailbox_data = NULL;
+ }
+ msg->is_large = large;
+ *arg = msg;
+ return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+ struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+ u32 msg_id, u32 drv_id)
+{
+ int retval = 0;
+
+ retval = sst_create_ipc_msg(arg, large);
+ if (retval)
+ return retval;
+ *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+ if (*block == NULL) {
+ kfree(*arg);
+ return -ENOMEM;
+ }
+ return retval;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+ stream->status = STREAM_UN_INIT;
+ stream->prev = STREAM_UN_INIT;
+ mutex_lock(&stream->lock);
+ stream->cumm_bytes = 0;
+ mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+ int task_id, int ipc_msg, int cmd_id, int pipe_id,
+ size_t mbox_data_len, const void *mbox_data, void **data,
+ bool large, bool fill_dsp, bool sync, bool response)
+{
+ struct ipc_post *msg = NULL;
+ struct ipc_dsp_hdr dsp_hdr;
+ struct sst_block *block;
+ int ret = 0, pvt_id;
+
+ pvt_id = sst_assign_pvt_id(sst);
+ if (pvt_id < 0)
+ return pvt_id;
+
+ if (response)
+ ret = sst_create_block_and_ipc_msg(
+ &msg, large, sst, &block, ipc_msg, pvt_id);
+ else
+ ret = sst_create_ipc_msg(&msg, large);
+
+ if (ret < 0) {
+ test_and_clear_bit(pvt_id, &sst->pvt_id);
+ return -ENOMEM;
+ }
+
+ dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+ pvt_id, pipe_id, task_id, ipc_msg);
+ sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+ task_id, large, pvt_id);
+ msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+ msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+ dev_dbg(sst->dev, "header:%x\n",
+ msg->mrfld_header.p.header_high.full);
+ dev_dbg(sst->dev, "response rqd: %x",
+ msg->mrfld_header.p.header_high.part.res_rqd);
+ dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+ msg->mrfld_header.p.header_low_payload);
+ if (fill_dsp) {
+ sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+ memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+ if (mbox_data_len) {
+ memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+ mbox_data, mbox_data_len);
+ }
+ }
+
+ if (sync)
+ sst->ops->post_message(sst, msg, true);
+ else
+ sst_add_to_dispatch_list_and_post(sst, msg);
+
+ if (response) {
+ ret = sst_wait_timeout(sst, block);
+ if (ret < 0) {
+ goto out;
+ } else if(block->data) {
+ if (!data)
+ goto out;
+ *data = kzalloc(block->size, GFP_KERNEL);
+ if (!(*data)) {
+ ret = -ENOMEM;
+ goto out;
+ } else
+ memcpy(data, (void *) block->data, block->size);
+ }
+ }
+out:
+ if (response)
+ sst_free_block(sst, block);
+ test_and_clear_bit(pvt_id, &sst->pvt_id);
+ return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+ int ret;
+
+ pm_runtime_mark_last_busy(sst_drv->dev);
+ ret = pm_runtime_put_autosuspend(sst_drv->dev);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+ int msg, int task_id, int large, int drv_id)
+{
+ header->full = 0;
+ header->p.header_high.part.msg_id = msg;
+ header->p.header_high.part.task_id = task_id;
+ header->p.header_high.part.large = large;
+ header->p.header_high.part.drv_id = drv_id;
+ header->p.header_high.part.done = 0;
+ header->p.header_high.part.busy = 1;
+ header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+ int pipe_id, int len)
+{
+ dsp->cmd_id = msg;
+ dsp->mod_index_id = 0xff;
+ dsp->pipe_id = pipe_id;
+ dsp->length = len;
+ dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+ int local;
+
+ spin_lock(&drv->block_lock);
+ /* find first zero index from lsb */
+ local = ffz(drv->pvt_id);
+ dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+ if (local >= SST_MAX_BLOCKS){
+ spin_unlock(&drv->block_lock);
+ dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+ return -EINVAL;
+ }
+ /* toggle the index */
+ change_bit(local, &drv->pvt_id);
+ spin_unlock(&drv->block_lock);
+ return local;
+}
+
+void sst_init_stream(struct stream_info *stream,
+ int codec, int sst_id, int ops, u8 slot)
+{
+ stream->status = STREAM_INIT;
+ stream->prev = STREAM_UN_INIT;
+ stream->ops = ops;
+}
+
+int sst_validate_strid(
+ struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+ dev_err(sst_drv_ctx->dev,
+ "SST ERR: invalid stream id : %d, max %d\n",
+ str_id, sst_drv_ctx->info.max_streams);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct stream_info *get_stream_info(
+ struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ if (sst_validate_strid(sst_drv_ctx, str_id))
+ return NULL;
+ return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ u32 pipe_id)
+{
+ int i;
+
+ for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+ if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+ return i;
+
+ dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+ return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+ /* Get the difference from 512MB aligned base addr */
+ /* relocate the base */
+ base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+ return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+ struct ipc_post *msg)
+{
+ unsigned long irq_flags;
+
+ spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+ list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+ spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+ sst->ops->post_message(sst, NULL, false);
+}
diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c
new file mode 100644
index 000000000000..dae2a41997aa
--- /dev/null
+++ b/sound/soc/intel/sst/sst_stream.c
@@ -0,0 +1,437 @@
+/*
+ * sst_stream.c - Intel SST Driver for audio engine
+ *
+ * Copyright (C) 2008-14 Intel Corp
+ * Authors: Vinod Koul <vinod.koul@intel.com>
+ * Harsha Priya <priya.harsha@intel.com>
+ * Dharageswari R <dharageswari.r@intel.com>
+ * KP Jeeja <jeeja.kp@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.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+ struct snd_sst_alloc_mrfld alloc_param;
+ struct snd_sst_params *str_params;
+ struct snd_sst_tstamp fw_tstamp;
+ struct stream_info *str_info;
+ struct snd_sst_alloc_response *response;
+ unsigned int str_id, pipe_id, task_id;
+ int i, num_ch, ret = 0;
+ void *data = NULL;
+
+ dev_dbg(sst_drv_ctx->dev, "Enter\n");
+ BUG_ON(!params);
+
+ str_params = (struct snd_sst_params *)params;
+ memset(&alloc_param, 0, sizeof(alloc_param));
+ alloc_param.operation = str_params->ops;
+ alloc_param.codec_type = str_params->codec;
+ alloc_param.sg_count = str_params->aparams.sg_count;
+ alloc_param.ring_buf_info[0].addr =
+ str_params->aparams.ring_buf_info[0].addr;
+ alloc_param.ring_buf_info[0].size =
+ str_params->aparams.ring_buf_info[0].size;
+ alloc_param.frag_size = str_params->aparams.frag_size;
+
+ memcpy(&alloc_param.codec_params, &str_params->sparams,
+ sizeof(struct snd_sst_stream_params));
+
+ /*
+ * fill channel map params for multichannel support.
+ * Ideally channel map should be received from upper layers
+ * for multichannel support.
+ * Currently hardcoding as per FW reqm.
+ */
+ num_ch = sst_get_num_channel(str_params);
+ for (i = 0; i < 8; i++) {
+ if (i < num_ch)
+ alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+ else
+ alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
+ }
+
+ str_id = str_params->stream_id;
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (str_info == NULL) {
+ dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
+ return -EINVAL;
+ }
+
+ pipe_id = str_params->device_type;
+ task_id = str_params->task;
+ sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
+ sst_drv_ctx->streams[str_id].task_id = task_id;
+ sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+ if (sst_drv_ctx->info.lpe_viewpt_rqd)
+ alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+ else
+ alloc_param.ts = sst_drv_ctx->mailbox_add +
+ sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+ dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+ alloc_param.ts);
+ dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+ pipe_id, task_id);
+
+ /* allocate device type context */
+ sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
+ str_id, alloc_param.operation, 0);
+
+ dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+ str_id, pipe_id);
+ ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
+ IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
+ &alloc_param, data, true, true, false, true);
+
+ if (ret < 0) {
+ dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+ /* alloc failed, so reset the state to uninit */
+ str_info->status = STREAM_UN_INIT;
+ str_id = ret;
+ } else if (data) {
+ response = (struct snd_sst_alloc_response *)data;
+ ret = response->str_type.result;
+ if (!ret)
+ goto out;
+ dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+ if (ret == SST_ERR_STREAM_IN_USE) {
+ dev_err(sst_drv_ctx->dev,
+ "FW not in clean state, send free for:%d\n", str_id);
+ sst_free_stream(sst_drv_ctx, str_id);
+ }
+ str_id = -ret;
+ }
+out:
+ kfree(data);
+ return str_id;
+}
+
+/**
+* sst_start_stream - Send msg for a starting stream
+* @str_id: stream ID
+*
+* This function is called by any function which wants to start
+* a stream.
+*/
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ u16 data = 0;
+
+ dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status != STREAM_RUNNING)
+ return -EBADRQC;
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(u16), &data, NULL, true, true, true, false);
+
+ return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+ struct snd_sst_bytes_v2 *bytes)
+{ struct ipc_post *msg = NULL;
+ u32 length;
+ int pvt_id, ret = 0;
+ struct sst_block *block = NULL;
+
+ dev_dbg(sst_drv_ctx->dev,
+ "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+ bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+ bytes->pipe_id, bytes->len);
+
+ if (sst_create_ipc_msg(&msg, true))
+ return -ENOMEM;
+
+ pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+ sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+ bytes->task_id, 1, pvt_id);
+ msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+ length = bytes->len;
+ msg->mrfld_header.p.header_low_payload = length;
+ dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+ memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+ if (bytes->block) {
+ block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+ if (block == NULL) {
+ kfree(msg);
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+ dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+ msg->mrfld_header.p.header_low_payload);
+
+ if (bytes->block) {
+ ret = sst_wait_timeout(sst_drv_ctx, block);
+ if (ret) {
+ dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+ sst_free_block(sst_drv_ctx, block);
+ goto out;
+ }
+ }
+ if (bytes->type == SND_SST_BYTES_GET) {
+ /*
+ * copy the reply and send back
+ * we need to update only sz and payload
+ */
+ if (bytes->block) {
+ unsigned char *r = block->data;
+
+ dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+ bytes->len);
+ memcpy(bytes->bytes, r, bytes->len);
+ }
+ }
+ if (bytes->block)
+ sst_free_block(sst_drv_ctx, block);
+out:
+ test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+ return 0;
+}
+
+/*
+ * sst_pause_stream - Send msg for a pausing stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status == STREAM_PAUSED)
+ return 0;
+ if (str_info->status == STREAM_RUNNING ||
+ str_info->status == STREAM_INIT) {
+ if (str_info->prev == STREAM_UN_INIT)
+ return -EBADRQC;
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+ 0, NULL, NULL, true, true, false, true);
+
+ if (retval == 0) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_PAUSED;
+ } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ }
+ } else {
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+ }
+
+ return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status == STREAM_RUNNING)
+ return 0;
+ if (str_info->status == STREAM_PAUSED) {
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+ str_info->pipe_id, 0, NULL, NULL,
+ true, true, false, true);
+
+ if (!retval) {
+ if (str_info->prev == STREAM_RUNNING)
+ str_info->status = STREAM_RUNNING;
+ else
+ str_info->status = STREAM_INIT;
+ str_info->prev = STREAM_PAUSED;
+ } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+ retval = -EINVAL;
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ }
+ } else {
+ retval = -EBADRQC;
+ dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+ }
+
+ return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+
+ if (str_info->status != STREAM_UN_INIT) {
+ str_info->prev = STREAM_UN_INIT;
+ str_info->status = STREAM_INIT;
+ str_info->cumm_bytes = 0;
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+ IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+ str_info->pipe_id, 0, NULL, NULL,
+ true, true, true, false);
+ } else {
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+ str_info->status);
+ }
+ return retval;
+}
+
+/**
+* sst_drain_stream - Send msg for draining stream
+* @str_id: stream ID
+*
+* This function is called by any function which wants to drain
+* a stream.
+*/
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+ int str_id, bool partial_drain)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ if (str_info->status != STREAM_RUNNING &&
+ str_info->status != STREAM_INIT &&
+ str_info->status != STREAM_PAUSED) {
+ dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+ str_info->status);
+ return -EBADRQC;
+ }
+
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+ sizeof(u8), &partial_drain, NULL, true, true, false, false);
+ /*
+ * with new non blocked drain implementation in core we dont need to
+ * wait for respsonse, and need to only invoke callback for drain
+ * complete
+ */
+
+ return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @str_id: stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+ int retval = 0;
+ struct stream_info *str_info;
+ struct intel_sst_ops *ops;
+
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ if (sst_drv_ctx->sst_state == SST_RESET) {
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ return -ENODEV;
+ }
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ str_info = get_stream_info(sst_drv_ctx, str_id);
+ if (!str_info)
+ return -EINVAL;
+ ops = sst_drv_ctx->ops;
+
+ mutex_lock(&str_info->lock);
+ if (str_info->status != STREAM_UN_INIT) {
+ str_info->prev = str_info->status;
+ str_info->status = STREAM_UN_INIT;
+ mutex_unlock(&str_info->lock);
+
+ dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+ str_id, str_info->pipe_id);
+ retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+ IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+ NULL, NULL, true, true, false, true);
+
+ dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+ retval);
+ mutex_lock(&sst_drv_ctx->sst_lock);
+ sst_clean_stream(str_info);
+ mutex_unlock(&sst_drv_ctx->sst_lock);
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+ } else {
+ mutex_unlock(&str_info->lock);
+ retval = -EBADRQC;
+ dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+ }
+
+ return retval;
+}
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
index 5cb91f9e8626..0fb7d2a91c3a 100644
--- a/sound/soc/jz4740/qi_lb60.c
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -77,25 +77,18 @@ static int qi_lb60_probe(struct platform_device *pdev)
{
struct qi_lb60 *qi_lb60;
struct snd_soc_card *card = &qi_lb60_card;
- int ret;
qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
if (!qi_lb60)
return -ENOMEM;
- qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
+ qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW);
if (IS_ERR(qi_lb60->snd_gpio))
return PTR_ERR(qi_lb60->snd_gpio);
- ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
- if (ret)
- return ret;
- qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
+ qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW);
if (IS_ERR(qi_lb60->amp_gpio))
return PTR_ERR(qi_lb60->amp_gpio);
- ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
- if (ret)
- return ret;
card->dev = &pdev->dev;
diff --git a/sound/soc/nuc900/nuc900-ac97.c b/sound/soc/nuc900/nuc900-ac97.c
index f2f67942b229..dff443e4b657 100644
--- a/sound/soc/nuc900/nuc900-ac97.c
+++ b/sound/soc/nuc900/nuc900-ac97.c
@@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
static struct snd_soc_dai_driver nuc900_ac97_dai = {
.probe = nuc900_ac97_probe,
.remove = nuc900_ac97_remove,
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index ae956e3f4b9d..73ca2820c08c 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
{
.name = "pxa2xx-ac97",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
},
{
.name = "pxa2xx-ac97-aux",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Aux Playback",
.channels_min = 1,
@@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
},
{
.name = "pxa2xx-ac97-mic",
- .ac97_control = 1,
+ .bus_control = true,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index f373e37f8305..c74ba37f862c 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -154,8 +154,10 @@ static void rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
while (val) {
regmap_read(i2s->regmap, I2S_CLR, &val);
retry--;
- if (!retry)
+ if (!retry) {
dev_warn(i2s->dev, "fail to clear\n");
+ break;
+ }
}
}
}
diff --git a/sound/soc/s6000/Kconfig b/sound/soc/s6000/Kconfig
deleted file mode 100644
index f244a2566f20..000000000000
--- a/sound/soc/s6000/Kconfig
+++ /dev/null
@@ -1,26 +0,0 @@
-config SND_S6000_SOC
- tristate "SoC Audio for the Stretch s6000 family"
- depends on XTENSA_VARIANT_S6000 || COMPILE_TEST
- depends on HAS_IOMEM
- select SND_S6000_SOC_PCM if XTENSA_VARIANT_S6000
- help
- Say Y or M if you want to add support for codecs attached to
- s6000 family chips. You will also need to select the platform
- to support below.
-
-config SND_S6000_SOC_PCM
- tristate
-
-config SND_S6000_SOC_I2S
- tristate
-
-config SND_S6000_SOC_S6IPCAM
- bool "SoC Audio support for Stretch 6105 IP Camera"
- depends on SND_S6000_SOC=y
- depends on I2C=y
- depends on XTENSA_PLATFORM_S6105 || COMPILE_TEST
- select SND_S6000_SOC_I2S
- select SND_SOC_TLV320AIC3X
- help
- Say Y if you want to add support for SoC audio on the
- Stretch s6105 IP Camera Reference Design.
diff --git a/sound/soc/s6000/Makefile b/sound/soc/s6000/Makefile
deleted file mode 100644
index 0f0ae2a012aa..000000000000
--- a/sound/soc/s6000/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-# s6000 Platform Support
-snd-soc-s6000-objs := s6000-pcm.o
-snd-soc-s6000-i2s-objs := s6000-i2s.o
-
-obj-$(CONFIG_SND_S6000_SOC_PCM) += snd-soc-s6000.o
-obj-$(CONFIG_SND_S6000_SOC_I2S) += snd-soc-s6000-i2s.o
-
-# s6105 Machine Support
-snd-soc-s6ipcam-objs := s6105-ipcam.o
-
-obj-$(CONFIG_SND_S6000_SOC_S6IPCAM) += snd-soc-s6ipcam.o
diff --git a/sound/soc/s6000/s6000-i2s.c b/sound/soc/s6000/s6000-i2s.c
deleted file mode 100644
index 1c8d01166e5b..000000000000
--- a/sound/soc/s6000/s6000-i2s.c
+++ /dev/null
@@ -1,617 +0,0 @@
-/*
- * ALSA SoC I2S Audio Layer for the Stretch S6000 family
- *
- * Author: Daniel Gloeckner, <dg@emlix.com>
- * Copyright: (C) 2009 emlix GmbH <info@emlix.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/init.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/delay.h>
-#include <linux/clk.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/initval.h>
-#include <sound/soc.h>
-
-#include "s6000-i2s.h"
-#include "s6000-pcm.h"
-
-struct s6000_i2s_dev {
- dma_addr_t sifbase;
- u8 __iomem *scbbase;
- unsigned int wide;
- unsigned int channel_in;
- unsigned int channel_out;
- unsigned int lines_in;
- unsigned int lines_out;
- struct s6000_pcm_dma_params dma_params;
-};
-
-#define S6_I2S_INTERRUPT_STATUS 0x00
-#define S6_I2S_INT_OVERRUN 1
-#define S6_I2S_INT_UNDERRUN 2
-#define S6_I2S_INT_ALIGNMENT 4
-#define S6_I2S_INTERRUPT_ENABLE 0x04
-#define S6_I2S_INTERRUPT_RAW 0x08
-#define S6_I2S_INTERRUPT_CLEAR 0x0C
-#define S6_I2S_INTERRUPT_SET 0x10
-#define S6_I2S_MODE 0x20
-#define S6_I2S_DUAL 0
-#define S6_I2S_WIDE 1
-#define S6_I2S_TX_DEFAULT 0x24
-#define S6_I2S_DATA_CFG(c) (0x40 + 0x10 * (c))
-#define S6_I2S_IN 0
-#define S6_I2S_OUT 1
-#define S6_I2S_UNUSED 2
-#define S6_I2S_INTERFACE_CFG(c) (0x44 + 0x10 * (c))
-#define S6_I2S_DIV_MASK 0x001fff
-#define S6_I2S_16BIT 0x000000
-#define S6_I2S_20BIT 0x002000
-#define S6_I2S_24BIT 0x004000
-#define S6_I2S_32BIT 0x006000
-#define S6_I2S_BITS_MASK 0x006000
-#define S6_I2S_MEM_16BIT 0x000000
-#define S6_I2S_MEM_32BIT 0x008000
-#define S6_I2S_MEM_MASK 0x008000
-#define S6_I2S_CHANNELS_SHIFT 16
-#define S6_I2S_CHANNELS_MASK 0x030000
-#define S6_I2S_SCK_IN 0x000000
-#define S6_I2S_SCK_OUT 0x040000
-#define S6_I2S_SCK_DIR 0x040000
-#define S6_I2S_WS_IN 0x000000
-#define S6_I2S_WS_OUT 0x080000
-#define S6_I2S_WS_DIR 0x080000
-#define S6_I2S_LEFT_FIRST 0x000000
-#define S6_I2S_RIGHT_FIRST 0x100000
-#define S6_I2S_FIRST 0x100000
-#define S6_I2S_CUR_SCK 0x200000
-#define S6_I2S_CUR_WS 0x400000
-#define S6_I2S_ENABLE(c) (0x48 + 0x10 * (c))
-#define S6_I2S_DISABLE_IF 0x02
-#define S6_I2S_ENABLE_IF 0x03
-#define S6_I2S_IS_BUSY 0x04
-#define S6_I2S_DMA_ACTIVE 0x08
-#define S6_I2S_IS_ENABLED 0x10
-
-#define S6_I2S_NUM_LINES 4
-
-#define S6_I2S_SIF_PORT0 0x0000000
-#define S6_I2S_SIF_PORT1 0x0000080 /* docs say 0x0000010 */
-
-static inline void s6_i2s_write_reg(struct s6000_i2s_dev *dev, int reg, u32 val)
-{
- writel(val, dev->scbbase + reg);
-}
-
-static inline u32 s6_i2s_read_reg(struct s6000_i2s_dev *dev, int reg)
-{
- return readl(dev->scbbase + reg);
-}
-
-static inline void s6_i2s_mod_reg(struct s6000_i2s_dev *dev, int reg,
- u32 mask, u32 val)
-{
- val ^= s6_i2s_read_reg(dev, reg) & ~mask;
- s6_i2s_write_reg(dev, reg, val);
-}
-
-static void s6000_i2s_start_channel(struct s6000_i2s_dev *dev, int channel)
-{
- int i, j, cur, prev;
-
- /*
- * Wait for WCLK to toggle 5 times before enabling the channel
- * s6000 Family Datasheet 3.6.4:
- * "At least two cycles of WS must occur between commands
- * to disable or enable the interface"
- */
- j = 0;
- prev = ~S6_I2S_CUR_WS;
- for (i = 1000000; --i && j < 6; ) {
- cur = s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(channel))
- & S6_I2S_CUR_WS;
- if (prev != cur) {
- prev = cur;
- j++;
- }
- }
- if (j < 6)
- printk(KERN_WARNING "s6000-i2s: timeout waiting for WCLK\n");
-
- s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_ENABLE_IF);
-}
-
-static void s6000_i2s_stop_channel(struct s6000_i2s_dev *dev, int channel)
-{
- s6_i2s_write_reg(dev, S6_I2S_ENABLE(channel), S6_I2S_DISABLE_IF);
-}
-
-static void s6000_i2s_start(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
- int channel;
-
- channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- dev->channel_out : dev->channel_in;
-
- s6000_i2s_start_channel(dev, channel);
-}
-
-static void s6000_i2s_stop(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = substream->private_data;
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
- int channel;
-
- channel = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- dev->channel_out : dev->channel_in;
-
- s6000_i2s_stop_channel(dev, channel);
-}
-
-static int s6000_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
- int after)
-{
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- if ((substream->stream == SNDRV_PCM_STREAM_CAPTURE) ^ !after)
- s6000_i2s_start(substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- if (!after)
- s6000_i2s_stop(substream);
- }
- return 0;
-}
-
-static unsigned int s6000_i2s_int_sources(struct s6000_i2s_dev *dev)
-{
- unsigned int pending;
- pending = s6_i2s_read_reg(dev, S6_I2S_INTERRUPT_RAW);
- pending &= S6_I2S_INT_ALIGNMENT |
- S6_I2S_INT_UNDERRUN |
- S6_I2S_INT_OVERRUN;
- s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR, pending);
-
- return pending;
-}
-
-static unsigned int s6000_i2s_check_xrun(struct snd_soc_dai *cpu_dai)
-{
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
- unsigned int errors;
- unsigned int ret;
-
- errors = s6000_i2s_int_sources(dev);
- if (likely(!errors))
- return 0;
-
- ret = 0;
- if (errors & S6_I2S_INT_ALIGNMENT)
- printk(KERN_ERR "s6000-i2s: WCLK misaligned\n");
- if (errors & S6_I2S_INT_UNDERRUN)
- ret |= 1 << SNDRV_PCM_STREAM_PLAYBACK;
- if (errors & S6_I2S_INT_OVERRUN)
- ret |= 1 << SNDRV_PCM_STREAM_CAPTURE;
- return ret;
-}
-
-static void s6000_i2s_wait_disabled(struct s6000_i2s_dev *dev)
-{
- int channel;
- int n = 50;
- for (channel = 0; channel < 2; channel++) {
- while (--n >= 0) {
- int v = s6_i2s_read_reg(dev, S6_I2S_ENABLE(channel));
- if ((v & S6_I2S_IS_ENABLED)
- || !(v & (S6_I2S_DMA_ACTIVE | S6_I2S_IS_BUSY)))
- break;
- udelay(20);
- }
- }
- if (n < 0)
- printk(KERN_WARNING "s6000-i2s: timeout disabling interfaces");
-}
-
-static int s6000_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
- unsigned int fmt)
-{
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
- u32 w;
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- w = S6_I2S_SCK_IN | S6_I2S_WS_IN;
- break;
- case SND_SOC_DAIFMT_CBS_CFM:
- w = S6_I2S_SCK_OUT | S6_I2S_WS_IN;
- break;
- case SND_SOC_DAIFMT_CBM_CFS:
- w = S6_I2S_SCK_IN | S6_I2S_WS_OUT;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- w = S6_I2S_SCK_OUT | S6_I2S_WS_OUT;
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- w |= S6_I2S_LEFT_FIRST;
- break;
- case SND_SOC_DAIFMT_NB_IF:
- w |= S6_I2S_RIGHT_FIRST;
- break;
- default:
- return -EINVAL;
- }
-
- s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(0),
- S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
- s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(1),
- S6_I2S_FIRST | S6_I2S_WS_DIR | S6_I2S_SCK_DIR, w);
-
- return 0;
-}
-
-static int s6000_i2s_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div)
-{
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
- if (!div || (div & 1) || div > (S6_I2S_DIV_MASK + 1) * 2)
- return -EINVAL;
-
- s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(div_id),
- S6_I2S_DIV_MASK, div / 2 - 1);
- return 0;
-}
-
-static int s6000_i2s_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params,
- struct snd_soc_dai *dai)
-{
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- int interf;
- u32 w = 0;
-
- if (dev->wide)
- interf = 0;
- else {
- w |= (((params_channels(params) - 2) / 2)
- << S6_I2S_CHANNELS_SHIFT) & S6_I2S_CHANNELS_MASK;
- interf = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- ? dev->channel_out : dev->channel_in;
- }
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S16_LE:
- w |= S6_I2S_16BIT | S6_I2S_MEM_16BIT;
- break;
- case SNDRV_PCM_FORMAT_S32_LE:
- w |= S6_I2S_32BIT | S6_I2S_MEM_32BIT;
- break;
- default:
- printk(KERN_WARNING "s6000-i2s: unsupported PCM format %x\n",
- params_format(params));
- return -EINVAL;
- }
-
- if (s6_i2s_read_reg(dev, S6_I2S_INTERFACE_CFG(interf))
- & S6_I2S_IS_ENABLED) {
- printk(KERN_ERR "s6000-i2s: interface already enabled\n");
- return -EBUSY;
- }
-
- s6_i2s_mod_reg(dev, S6_I2S_INTERFACE_CFG(interf),
- S6_I2S_CHANNELS_MASK|S6_I2S_MEM_MASK|S6_I2S_BITS_MASK,
- w);
-
- return 0;
-}
-
-static int s6000_i2s_dai_probe(struct snd_soc_dai *dai)
-{
- struct s6000_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
- struct s6000_snd_platform_data *pdata = dai->dev->platform_data;
-
- if (!pdata)
- return -EINVAL;
-
- dai->capture_dma_data = &dev->dma_params;
- dai->playback_dma_data = &dev->dma_params;
-
- dev->wide = pdata->wide;
- dev->channel_in = pdata->channel_in;
- dev->channel_out = pdata->channel_out;
- dev->lines_in = pdata->lines_in;
- dev->lines_out = pdata->lines_out;
-
- s6_i2s_write_reg(dev, S6_I2S_MODE,
- dev->wide ? S6_I2S_WIDE : S6_I2S_DUAL);
-
- if (dev->wide) {
- int i;
-
- if (dev->lines_in + dev->lines_out > S6_I2S_NUM_LINES)
- return -EINVAL;
-
- dev->channel_in = 0;
- dev->channel_out = 1;
- dai->driver->capture.channels_min = 2 * dev->lines_in;
- dai->driver->capture.channels_max = dai->driver->capture.channels_min;
- dai->driver->playback.channels_min = 2 * dev->lines_out;
- dai->driver->playback.channels_max = dai->driver->playback.channels_min;
-
- for (i = 0; i < dev->lines_out; i++)
- s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_OUT);
-
- for (; i < S6_I2S_NUM_LINES - dev->lines_in; i++)
- s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i),
- S6_I2S_UNUSED);
-
- for (; i < S6_I2S_NUM_LINES; i++)
- s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(i), S6_I2S_IN);
- } else {
- unsigned int cfg[2] = {S6_I2S_UNUSED, S6_I2S_UNUSED};
-
- if (dev->lines_in > 1 || dev->lines_out > 1)
- return -EINVAL;
-
- dai->driver->capture.channels_min = 2 * dev->lines_in;
- dai->driver->capture.channels_max = 8 * dev->lines_in;
- dai->driver->playback.channels_min = 2 * dev->lines_out;
- dai->driver->playback.channels_max = 8 * dev->lines_out;
-
- if (dev->lines_in)
- cfg[dev->channel_in] = S6_I2S_IN;
- if (dev->lines_out)
- cfg[dev->channel_out] = S6_I2S_OUT;
-
- s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(0), cfg[0]);
- s6_i2s_write_reg(dev, S6_I2S_DATA_CFG(1), cfg[1]);
- }
-
- if (dev->lines_out) {
- if (dev->lines_in) {
- if (!dev->dma_params.dma_out)
- return -ENODEV;
- } else {
- dev->dma_params.dma_out = dev->dma_params.dma_in;
- dev->dma_params.dma_in = 0;
- }
- }
- dev->dma_params.sif_in = dev->sifbase + (dev->channel_in ?
- S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
- dev->dma_params.sif_out = dev->sifbase + (dev->channel_out ?
- S6_I2S_SIF_PORT1 : S6_I2S_SIF_PORT0);
- dev->dma_params.same_rate = pdata->same_rate | pdata->wide;
- return 0;
-}
-
-#define S6000_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
-#define S6000_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
-
-static const struct snd_soc_dai_ops s6000_i2s_dai_ops = {
- .set_fmt = s6000_i2s_set_dai_fmt,
- .set_clkdiv = s6000_i2s_set_clkdiv,
- .hw_params = s6000_i2s_hw_params,
-};
-
-static struct snd_soc_dai_driver s6000_i2s_dai = {
- .probe = s6000_i2s_dai_probe,
- .playback = {
- .channels_min = 2,
- .channels_max = 8,
- .formats = S6000_I2S_FORMATS,
- .rates = S6000_I2S_RATES,
- .rate_min = 0,
- .rate_max = 1562500,
- },
- .capture = {
- .channels_min = 2,
- .channels_max = 8,
- .formats = S6000_I2S_FORMATS,
- .rates = S6000_I2S_RATES,
- .rate_min = 0,
- .rate_max = 1562500,
- },
- .ops = &s6000_i2s_dai_ops,
-};
-
-static const struct snd_soc_component_driver s6000_i2s_component = {
- .name = "s6000-i2s",
-};
-
-static int s6000_i2s_probe(struct platform_device *pdev)
-{
- struct s6000_i2s_dev *dev;
- struct resource *scbmem, *sifmem, *region, *dma1, *dma2;
- u8 __iomem *mmio;
- int ret;
-
- scbmem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!scbmem) {
- dev_err(&pdev->dev, "no mem resource?\n");
- ret = -ENODEV;
- goto err_release_none;
- }
-
- region = request_mem_region(scbmem->start, resource_size(scbmem),
- pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "I2S SCB region already claimed\n");
- ret = -EBUSY;
- goto err_release_none;
- }
-
- mmio = ioremap(scbmem->start, resource_size(scbmem));
- if (!mmio) {
- dev_err(&pdev->dev, "can't ioremap SCB region\n");
- ret = -ENOMEM;
- goto err_release_scb;
- }
-
- sifmem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
- if (!sifmem) {
- dev_err(&pdev->dev, "no second mem resource?\n");
- ret = -ENODEV;
- goto err_release_map;
- }
-
- region = request_mem_region(sifmem->start, resource_size(sifmem),
- pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "I2S SIF region already claimed\n");
- ret = -EBUSY;
- goto err_release_map;
- }
-
- dma1 = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (!dma1) {
- dev_err(&pdev->dev, "no dma resource?\n");
- ret = -ENODEV;
- goto err_release_sif;
- }
-
- region = request_mem_region(dma1->start, resource_size(dma1),
- pdev->name);
- if (!region) {
- dev_err(&pdev->dev, "I2S DMA region already claimed\n");
- ret = -EBUSY;
- goto err_release_sif;
- }
-
- dma2 = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (dma2) {
- region = request_mem_region(dma2->start, resource_size(dma2),
- pdev->name);
- if (!region) {
- dev_err(&pdev->dev,
- "I2S DMA region already claimed\n");
- ret = -EBUSY;
- goto err_release_dma1;
- }
- }
-
- dev = kzalloc(sizeof(struct s6000_i2s_dev), GFP_KERNEL);
- if (!dev) {
- ret = -ENOMEM;
- goto err_release_dma2;
- }
- dev_set_drvdata(&pdev->dev, dev);
-
- dev->sifbase = sifmem->start;
- dev->scbbase = mmio;
-
- s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
- s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_CLEAR,
- S6_I2S_INT_ALIGNMENT |
- S6_I2S_INT_UNDERRUN |
- S6_I2S_INT_OVERRUN);
-
- s6000_i2s_stop_channel(dev, 0);
- s6000_i2s_stop_channel(dev, 1);
- s6000_i2s_wait_disabled(dev);
-
- dev->dma_params.check_xrun = s6000_i2s_check_xrun;
- dev->dma_params.trigger = s6000_i2s_trigger;
- dev->dma_params.dma_in = dma1->start;
- dev->dma_params.dma_out = dma2 ? dma2->start : 0;
- dev->dma_params.irq = platform_get_irq(pdev, 0);
- if (dev->dma_params.irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
- ret = -ENODEV;
- goto err_release_dev;
- }
-
- s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE,
- S6_I2S_INT_ALIGNMENT |
- S6_I2S_INT_UNDERRUN |
- S6_I2S_INT_OVERRUN);
-
- ret = snd_soc_register_component(&pdev->dev, &s6000_i2s_component,
- &s6000_i2s_dai, 1);
- if (ret)
- goto err_release_dev;
-
- return 0;
-
-err_release_dev:
- kfree(dev);
-err_release_dma2:
- if (dma2)
- release_mem_region(dma2->start, resource_size(dma2));
-err_release_dma1:
- release_mem_region(dma1->start, resource_size(dma1));
-err_release_sif:
- release_mem_region(sifmem->start, resource_size(sifmem));
-err_release_map:
- iounmap(mmio);
-err_release_scb:
- release_mem_region(scbmem->start, resource_size(scbmem));
-err_release_none:
- return ret;
-}
-
-static int s6000_i2s_remove(struct platform_device *pdev)
-{
- struct s6000_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
- struct resource *region;
- void __iomem *mmio = dev->scbbase;
-
- snd_soc_unregister_component(&pdev->dev);
-
- s6000_i2s_stop_channel(dev, 0);
- s6000_i2s_stop_channel(dev, 1);
-
- s6_i2s_write_reg(dev, S6_I2S_INTERRUPT_ENABLE, 0);
- kfree(dev);
-
- region = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- release_mem_region(region->start, resource_size(region));
-
- region = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (region)
- release_mem_region(region->start, resource_size(region));
-
- region = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- release_mem_region(region->start, resource_size(region));
-
- 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 = {
- .probe = s6000_i2s_probe,
- .remove = s6000_i2s_remove,
- .driver = {
- .name = "s6000-i2s",
- .owner = THIS_MODULE,
- },
-};
-
-module_platform_driver(s6000_i2s_driver);
-
-MODULE_AUTHOR("Daniel Gloeckner");
-MODULE_DESCRIPTION("Stretch s6000 family I2S SoC Interface");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-i2s.h b/sound/soc/s6000/s6000-i2s.h
deleted file mode 100644
index 86aa1921c89e..000000000000
--- a/sound/soc/s6000/s6000-i2s.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * ALSA SoC I2S Audio Layer for the Stretch s6000 family
- *
- * Author: Daniel Gloeckner, <dg@emlix.com>
- * Copyright: (C) 2009 emlix GmbH <info@emlix.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 _S6000_I2S_H
-#define _S6000_I2S_H
-
-struct s6000_snd_platform_data {
- int lines_in;
- int lines_out;
- int channel_in;
- int channel_out;
- int wide;
- int same_rate;
-};
-#endif
diff --git a/sound/soc/s6000/s6000-pcm.c b/sound/soc/s6000/s6000-pcm.c
deleted file mode 100644
index fb8461e1b1f6..000000000000
--- a/sound/soc/s6000/s6000-pcm.c
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * ALSA PCM interface for the Stetch s6000 family
- *
- * Author: Daniel Gloeckner, <dg@emlix.com>
- * Copyright: (C) 2009 emlix GmbH <info@emlix.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/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/dma-mapping.h>
-#include <linux/interrupt.h>
-
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/dma.h>
-#include <variant/dmac.h>
-
-#include "s6000-pcm.h"
-
-#define S6_PCM_PREALLOCATE_SIZE (96 * 1024)
-#define S6_PCM_PREALLOCATE_MAX (2048 * 1024)
-
-static struct snd_pcm_hardware s6000_pcm_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_JOINT_DUPLEX),
- .buffer_bytes_max = 0x7ffffff0,
- .period_bytes_min = 16,
- .period_bytes_max = 0xfffff0,
- .periods_min = 2,
- .periods_max = 1024, /* no limit */
- .fifo_size = 0,
-};
-
-struct s6000_runtime_data {
- spinlock_t lock;
- int period; /* current DMA period */
-};
-
-static void s6000_pcm_enqueue_dma(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct s6000_runtime_data *prtd = runtime->private_data;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- int channel;
- unsigned int period_size;
- unsigned int dma_offset;
- dma_addr_t dma_pos;
- dma_addr_t src, dst;
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- period_size = snd_pcm_lib_period_bytes(substream);
- dma_offset = prtd->period * period_size;
- dma_pos = runtime->dma_addr + dma_offset;
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- src = dma_pos;
- dst = par->sif_out;
- channel = par->dma_out;
- } else {
- src = par->sif_in;
- dst = dma_pos;
- channel = par->dma_in;
- }
-
- if (!s6dmac_channel_enabled(DMA_MASK_DMAC(channel),
- DMA_INDEX_CHNL(channel)))
- return;
-
- if (s6dmac_fifo_full(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel))) {
- printk(KERN_ERR "s6000-pcm: fifo full\n");
- return;
- }
-
- if (WARN_ON(period_size & 15))
- return;
- s6dmac_put_fifo(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel),
- src, dst, period_size);
-
- prtd->period++;
- if (unlikely(prtd->period >= runtime->periods))
- prtd->period = 0;
-}
-
-static irqreturn_t s6000_pcm_irq(int irq, void *data)
-{
- struct snd_pcm *pcm = data;
- struct snd_soc_pcm_runtime *runtime = pcm->private_data;
- struct s6000_runtime_data *prtd;
- unsigned int has_xrun;
- int i, ret = IRQ_NONE;
-
- for (i = 0; i < 2; ++i) {
- struct snd_pcm_substream *substream = pcm->streams[i].substream;
- struct s6000_pcm_dma_params *params =
- snd_soc_dai_get_dma_data(runtime->cpu_dai, substream);
- u32 channel;
- unsigned int pending;
-
- if (substream == SNDRV_PCM_STREAM_PLAYBACK)
- channel = params->dma_out;
- else
- channel = params->dma_in;
-
- has_xrun = params->check_xrun(runtime->cpu_dai);
-
- if (!channel)
- continue;
-
- if (unlikely(has_xrun & (1 << i)) &&
- substream->runtime &&
- snd_pcm_running(substream)) {
- dev_dbg(pcm->dev, "xrun\n");
- snd_pcm_stream_lock(substream);
- snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(substream);
- ret = IRQ_HANDLED;
- }
-
- pending = s6dmac_int_sources(DMA_MASK_DMAC(channel),
- DMA_INDEX_CHNL(channel));
-
- if (pending & 1) {
- ret = IRQ_HANDLED;
- if (likely(substream->runtime &&
- snd_pcm_running(substream))) {
- snd_pcm_period_elapsed(substream);
- dev_dbg(pcm->dev, "period elapsed %x %x\n",
- s6dmac_cur_src(DMA_MASK_DMAC(channel),
- DMA_INDEX_CHNL(channel)),
- s6dmac_cur_dst(DMA_MASK_DMAC(channel),
- DMA_INDEX_CHNL(channel)));
- prtd = substream->runtime->private_data;
- spin_lock(&prtd->lock);
- s6000_pcm_enqueue_dma(substream);
- spin_unlock(&prtd->lock);
- }
- }
-
- if (unlikely(pending & ~7)) {
- if (pending & (1 << 3))
- printk(KERN_WARNING
- "s6000-pcm: DMA %x Underflow\n",
- channel);
- if (pending & (1 << 4))
- printk(KERN_WARNING
- "s6000-pcm: DMA %x Overflow\n",
- channel);
- if (pending & 0x1e0)
- printk(KERN_WARNING
- "s6000-pcm: DMA %x Master Error "
- "(mask %x)\n",
- channel, pending >> 5);
-
- }
- }
-
- return ret;
-}
-
-static int s6000_pcm_start(struct snd_pcm_substream *substream)
-{
- struct s6000_runtime_data *prtd = substream->runtime->private_data;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- unsigned long flags;
- int srcinc;
- u32 dma;
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- spin_lock_irqsave(&prtd->lock, flags);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- srcinc = 1;
- dma = par->dma_out;
- } else {
- srcinc = 0;
- dma = par->dma_in;
- }
- s6dmac_enable_chan(DMA_MASK_DMAC(dma), DMA_INDEX_CHNL(dma),
- 1 /* priority 1 (0 is max) */,
- 0 /* peripheral requests w/o xfer length mode */,
- srcinc /* source address increment */,
- srcinc^1 /* destination address increment */,
- 0 /* chunksize 0 (skip impossible on this dma) */,
- 0 /* source skip after chunk (impossible) */,
- 0 /* destination skip after chunk (impossible) */,
- 4 /* 16 byte burst size */,
- -1 /* don't conserve bandwidth */,
- 0 /* low watermark irq descriptor threshold */,
- 0 /* disable hardware timestamps */,
- 1 /* enable channel */);
-
- s6000_pcm_enqueue_dma(substream);
- s6000_pcm_enqueue_dma(substream);
-
- spin_unlock_irqrestore(&prtd->lock, flags);
-
- return 0;
-}
-
-static int s6000_pcm_stop(struct snd_pcm_substream *substream)
-{
- struct s6000_runtime_data *prtd = substream->runtime->private_data;
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- unsigned long flags;
- u32 channel;
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- channel = par->dma_out;
- else
- channel = par->dma_in;
-
- s6dmac_set_terminal_count(DMA_MASK_DMAC(channel),
- DMA_INDEX_CHNL(channel), 0);
-
- spin_lock_irqsave(&prtd->lock, flags);
-
- s6dmac_disable_chan(DMA_MASK_DMAC(channel), DMA_INDEX_CHNL(channel));
-
- spin_unlock_irqrestore(&prtd->lock, flags);
-
- return 0;
-}
-
-static int s6000_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- int ret;
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- ret = par->trigger(substream, cmd, 0);
- if (ret < 0)
- return ret;
-
- switch (cmd) {
- case SNDRV_PCM_TRIGGER_START:
- case SNDRV_PCM_TRIGGER_RESUME:
- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
- ret = s6000_pcm_start(substream);
- break;
- case SNDRV_PCM_TRIGGER_STOP:
- case SNDRV_PCM_TRIGGER_SUSPEND:
- case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
- ret = s6000_pcm_stop(substream);
- break;
- default:
- ret = -EINVAL;
- }
- if (ret < 0)
- return ret;
-
- return par->trigger(substream, cmd, 1);
-}
-
-static int s6000_pcm_prepare(struct snd_pcm_substream *substream)
-{
- struct s6000_runtime_data *prtd = substream->runtime->private_data;
-
- prtd->period = 0;
-
- return 0;
-}
-
-static snd_pcm_uframes_t s6000_pcm_pointer(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct s6000_runtime_data *prtd = runtime->private_data;
- unsigned long flags;
- unsigned int offset;
- dma_addr_t count;
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- spin_lock_irqsave(&prtd->lock, flags);
-
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- count = s6dmac_cur_src(DMA_MASK_DMAC(par->dma_out),
- DMA_INDEX_CHNL(par->dma_out));
- else
- count = s6dmac_cur_dst(DMA_MASK_DMAC(par->dma_in),
- DMA_INDEX_CHNL(par->dma_in));
-
- count -= runtime->dma_addr;
-
- spin_unlock_irqrestore(&prtd->lock, flags);
-
- offset = bytes_to_frames(runtime, count);
- if (unlikely(offset >= runtime->buffer_size))
- offset = 0;
-
- return offset;
-}
-
-static int s6000_pcm_open(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct s6000_runtime_data *prtd;
- int ret;
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
- snd_soc_set_runtime_hwparams(substream, &s6000_pcm_hardware);
-
- ret = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
- if (ret < 0)
- return ret;
- ret = snd_pcm_hw_constraint_step(runtime, 0,
- SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
- if (ret < 0)
- return ret;
- ret = snd_pcm_hw_constraint_integer(runtime,
- SNDRV_PCM_HW_PARAM_PERIODS);
- if (ret < 0)
- return ret;
-
- if (par->same_rate) {
- int rate;
- spin_lock(&par->lock); /* needed? */
- rate = par->rate;
- spin_unlock(&par->lock);
- if (rate != -1) {
- ret = snd_pcm_hw_constraint_minmax(runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- rate, rate);
- if (ret < 0)
- return ret;
- }
- }
-
- prtd = kzalloc(sizeof(struct s6000_runtime_data), GFP_KERNEL);
- if (prtd == NULL)
- return -ENOMEM;
-
- spin_lock_init(&prtd->lock);
-
- runtime->private_data = prtd;
-
- return 0;
-}
-
-static int s6000_pcm_close(struct snd_pcm_substream *substream)
-{
- struct snd_pcm_runtime *runtime = substream->runtime;
- struct s6000_runtime_data *prtd = runtime->private_data;
-
- kfree(prtd);
-
- return 0;
-}
-
-static int s6000_pcm_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *hw_params)
-{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par;
- int ret;
- ret = snd_pcm_lib_malloc_pages(substream,
- params_buffer_bytes(hw_params));
- if (ret < 0) {
- printk(KERN_WARNING "s6000-pcm: allocation of memory failed\n");
- return ret;
- }
-
- par = snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- if (par->same_rate) {
- spin_lock(&par->lock);
- if (par->rate == -1 ||
- !(par->in_use & ~(1 << substream->stream))) {
- par->rate = params_rate(hw_params);
- par->in_use |= 1 << substream->stream;
- } else if (params_rate(hw_params) != par->rate) {
- snd_pcm_lib_free_pages(substream);
- par->in_use &= ~(1 << substream->stream);
- ret = -EBUSY;
- }
- spin_unlock(&par->lock);
- }
- return ret;
-}
-
-static int s6000_pcm_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
- struct s6000_pcm_dma_params *par =
- snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, substream);
-
- spin_lock(&par->lock);
- par->in_use &= ~(1 << substream->stream);
- if (!par->in_use)
- par->rate = -1;
- spin_unlock(&par->lock);
-
- return snd_pcm_lib_free_pages(substream);
-}
-
-static struct snd_pcm_ops s6000_pcm_ops = {
- .open = s6000_pcm_open,
- .close = s6000_pcm_close,
- .ioctl = snd_pcm_lib_ioctl,
- .hw_params = s6000_pcm_hw_params,
- .hw_free = s6000_pcm_hw_free,
- .trigger = s6000_pcm_trigger,
- .prepare = s6000_pcm_prepare,
- .pointer = s6000_pcm_pointer,
-};
-
-static void s6000_pcm_free(struct snd_pcm *pcm)
-{
- struct snd_soc_pcm_runtime *runtime = pcm->private_data;
- struct s6000_pcm_dma_params *params =
- snd_soc_dai_get_dma_data(runtime->cpu_dai,
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
-
- free_irq(params->irq, pcm);
- snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static int s6000_pcm_new(struct snd_soc_pcm_runtime *runtime)
-{
- struct snd_card *card = runtime->card->snd_card;
- struct snd_pcm *pcm = runtime->pcm;
- struct s6000_pcm_dma_params *params;
- int res;
-
- params = snd_soc_dai_get_dma_data(runtime->cpu_dai,
- pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream);
-
- res = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
- if (res)
- return res;
-
- if (params->dma_in) {
- s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_in),
- DMA_INDEX_CHNL(params->dma_in));
- s6dmac_int_sources(DMA_MASK_DMAC(params->dma_in),
- DMA_INDEX_CHNL(params->dma_in));
- }
-
- if (params->dma_out) {
- s6dmac_disable_chan(DMA_MASK_DMAC(params->dma_out),
- DMA_INDEX_CHNL(params->dma_out));
- s6dmac_int_sources(DMA_MASK_DMAC(params->dma_out),
- DMA_INDEX_CHNL(params->dma_out));
- }
-
- res = request_irq(params->irq, s6000_pcm_irq, IRQF_SHARED,
- "s6000-audio", pcm);
- if (res) {
- printk(KERN_ERR "s6000-pcm couldn't get IRQ\n");
- return res;
- }
-
- res = snd_pcm_lib_preallocate_pages_for_all(pcm,
- SNDRV_DMA_TYPE_DEV,
- card->dev,
- S6_PCM_PREALLOCATE_SIZE,
- S6_PCM_PREALLOCATE_MAX);
- if (res)
- printk(KERN_WARNING "s6000-pcm: preallocation failed\n");
-
- spin_lock_init(&params->lock);
- params->in_use = 0;
- params->rate = -1;
- return 0;
-}
-
-static struct snd_soc_platform_driver s6000_soc_platform = {
- .ops = &s6000_pcm_ops,
- .pcm_new = s6000_pcm_new,
- .pcm_free = s6000_pcm_free,
-};
-
-static int s6000_soc_platform_probe(struct platform_device *pdev)
-{
- return snd_soc_register_platform(&pdev->dev, &s6000_soc_platform);
-}
-
-static int s6000_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver s6000_pcm_driver = {
- .driver = {
- .name = "s6000-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = s6000_soc_platform_probe,
- .remove = s6000_soc_platform_remove,
-};
-
-module_platform_driver(s6000_pcm_driver);
-
-MODULE_AUTHOR("Daniel Gloeckner");
-MODULE_DESCRIPTION("Stretch s6000 family PCM DMA module");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/s6000/s6000-pcm.h b/sound/soc/s6000/s6000-pcm.h
deleted file mode 100644
index 09d9b883e58b..000000000000
--- a/sound/soc/s6000/s6000-pcm.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * ALSA PCM interface for the Stretch s6000 family
- *
- * Author: Daniel Gloeckner, <dg@emlix.com>
- * Copyright: (C) 2009 emlix GmbH <info@emlix.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 _S6000_PCM_H
-#define _S6000_PCM_H
-
-struct snd_soc_dai;
-struct snd_pcm_substream;
-
-struct s6000_pcm_dma_params {
- unsigned int (*check_xrun)(struct snd_soc_dai *cpu_dai);
- int (*trigger)(struct snd_pcm_substream *substream, int cmd, int after);
- dma_addr_t sif_in;
- dma_addr_t sif_out;
- u32 dma_in;
- u32 dma_out;
- int irq;
- int same_rate;
-
- spinlock_t lock;
- int in_use;
- int rate;
-};
-
-#endif
diff --git a/sound/soc/s6000/s6105-ipcam.c b/sound/soc/s6000/s6105-ipcam.c
deleted file mode 100644
index 3510c01f8a6a..000000000000
--- a/sound/soc/s6000/s6105-ipcam.c
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * ASoC driver for Stretch s6105 IP camera platform
- *
- * Author: Daniel Gloeckner, <dg@emlix.com>
- * Copyright: (C) 2009 emlix GmbH <info@emlix.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/timer.h>
-#include <linux/interrupt.h>
-#include <linux/platform_device.h>
-#include <linux/i2c.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/soc.h>
-
-#include "s6000-pcm.h"
-#include "s6000-i2s.h"
-
-#define S6105_CAM_CODEC_CLOCK 12288000
-
-static int s6105_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;
- struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret = 0;
-
- /* set codec DAI configuration */
- ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_CBM_CFM);
- if (ret < 0)
- return ret;
-
- /* set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBM_CFM |
- SND_SOC_DAIFMT_NB_NF);
- if (ret < 0)
- return ret;
-
- /* set the codec system clock */
- ret = snd_soc_dai_set_sysclk(codec_dai, 0, S6105_CAM_CODEC_CLOCK,
- SND_SOC_CLOCK_OUT);
- if (ret < 0)
- return ret;
-
- return 0;
-}
-
-static struct snd_soc_ops s6105_ops = {
- .hw_params = s6105_hw_params,
-};
-
-/* s6105 machine dapm widgets */
-static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
- SND_SOC_DAPM_LINE("Audio Out Differential", NULL),
- SND_SOC_DAPM_LINE("Audio Out Stereo", NULL),
- SND_SOC_DAPM_LINE("Audio In", NULL),
-};
-
-/* s6105 machine audio_mapnections to the codec pins */
-static const struct snd_soc_dapm_route audio_map[] = {
- /* Audio Out connected to HPLOUT, HPLCOM, HPROUT */
- {"Audio Out Differential", NULL, "HPLOUT"},
- {"Audio Out Differential", NULL, "HPLCOM"},
- {"Audio Out Stereo", NULL, "HPLOUT"},
- {"Audio Out Stereo", NULL, "HPROUT"},
-
- /* Audio In connected to LINE1L, LINE1R */
- {"LINE1L", NULL, "Audio In"},
- {"LINE1R", NULL, "Audio In"},
-};
-
-static int output_type_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = 1;
- uinfo->value.enumerated.items = 2;
- if (uinfo->value.enumerated.item) {
- uinfo->value.enumerated.item = 1;
- strcpy(uinfo->value.enumerated.name, "HPLOUT/HPROUT");
- } else {
- strcpy(uinfo->value.enumerated.name, "HPLOUT/HPLCOM");
- }
- return 0;
-}
-
-static int output_type_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- ucontrol->value.enumerated.item[0] = kcontrol->private_value;
- return 0;
-}
-
-static int output_type_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = kcontrol->private_data;
- struct snd_soc_dapm_context *dapm = &card->dapm;
- unsigned int val = (ucontrol->value.enumerated.item[0] != 0);
- char *differential = "Audio Out Differential";
- char *stereo = "Audio Out Stereo";
-
- if (kcontrol->private_value == val)
- return 0;
- kcontrol->private_value = val;
- snd_soc_dapm_disable_pin(dapm, val ? differential : stereo);
- snd_soc_dapm_sync(dapm);
- snd_soc_dapm_enable_pin(dapm, val ? stereo : differential);
- snd_soc_dapm_sync(dapm);
-
- return 1;
-}
-
-static const struct snd_kcontrol_new audio_out_mux = {
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "Master Output Mux",
- .index = 0,
- .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
- .info = output_type_info,
- .get = output_type_get,
- .put = output_type_put,
- .private_value = 1 /* default to stereo */
-};
-
-/* Logic for a aic3x as connected on the s6105 ip camera ref design */
-static int s6105_aic3x_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_card *card = rtd->card;
-
- /* must correspond to audio_out_mux.private_value initializer */
- snd_soc_dapm_disable_pin(&card->dapm, "Audio Out Differential");
-
- snd_ctl_add(card->snd_card, snd_ctl_new1(&audio_out_mux, card));
-
- return 0;
-}
-
-/* s6105 digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link s6105_dai = {
- .name = "TLV320AIC31",
- .stream_name = "AIC31",
- .cpu_dai_name = "s6000-i2s",
- .codec_dai_name = "tlv320aic3x-hifi",
- .platform_name = "s6000-pcm-audio",
- .codec_name = "tlv320aic3x-codec.0-001a",
- .init = s6105_aic3x_init,
- .ops = &s6105_ops,
-};
-
-/* s6105 audio machine driver */
-static struct snd_soc_card snd_soc_card_s6105 = {
- .name = "Stretch IP Camera",
- .owner = THIS_MODULE,
- .dai_link = &s6105_dai,
- .num_links = 1,
-
- .dapm_widgets = aic3x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(aic3x_dapm_widgets),
- .dapm_routes = audio_map,
- .num_dapm_routes = ARRAY_SIZE(audio_map),
- .fully_routed = true,
-};
-
-static struct s6000_snd_platform_data s6105_snd_data __initdata = {
- .wide = 0,
- .channel_in = 0,
- .channel_out = 1,
- .lines_in = 1,
- .lines_out = 1,
- .same_rate = 1,
-};
-
-static struct platform_device *s6105_snd_device;
-
-/* temporary i2c device creation until this can be moved into the machine
- * support file.
-*/
-static struct i2c_board_info i2c_device[] = {
- { I2C_BOARD_INFO("tlv320aic33", 0x18), }
-};
-
-static int __init s6105_init(void)
-{
- int ret;
-
- i2c_register_board_info(0, i2c_device, ARRAY_SIZE(i2c_device));
-
- s6105_snd_device = platform_device_alloc("soc-audio", -1);
- if (!s6105_snd_device)
- return -ENOMEM;
-
- platform_set_drvdata(s6105_snd_device, &snd_soc_card_s6105);
- platform_device_add_data(s6105_snd_device, &s6105_snd_data,
- sizeof(s6105_snd_data));
-
- ret = platform_device_add(s6105_snd_device);
- if (ret)
- platform_device_put(s6105_snd_device);
-
- return ret;
-}
-
-static void __exit s6105_exit(void)
-{
- platform_device_unregister(s6105_snd_device);
-}
-
-module_init(s6105_init);
-module_exit(s6105_exit);
-
-MODULE_AUTHOR("Daniel Gloeckner");
-MODULE_DESCRIPTION("Stretch s6105 IP camera ASoC driver");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index e1615113fd84..7952a625669d 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver s3c_ac97_dai[] = {
[S3C_AC97_DAI_PCM] = {
.name = "samsung-ac97",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.stream_name = "AC97 Playback",
.channels_min = 2,
@@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
},
[S3C_AC97_DAI_MIC] = {
.name = "samsung-ac97-mic",
- .ac97_control = 1,
+ .bus_control = true,
.capture = {
.stream_name = "AC97 Mic Capture",
.channels_min = 1,
diff --git a/sound/soc/samsung/snow.c b/sound/soc/samsung/snow.c
index 0acf5d0eed53..72118a77dd5b 100644
--- a/sound/soc/samsung/snow.c
+++ b/sound/soc/samsung/snow.c
@@ -110,6 +110,7 @@ static const struct of_device_id snow_of_match[] = {
{ .compatible = "google,snow-audio-max98095", },
{},
};
+MODULE_DEVICE_TABLE(of, snow_of_match);
static struct platform_driver snow_driver = {
.driver = {
diff --git a/sound/soc/sh/fsi.c b/sound/soc/sh/fsi.c
index 66fddec9543d..8869971d7884 100644
--- a/sound/soc/sh/fsi.c
+++ b/sound/soc/sh/fsi.c
@@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev,
return -EINVAL;
if (1 == clock->count--) {
- if (clock->xck)
- clk_disable(clock->xck);
- if (clock->ick)
- clk_disable(clock->ick);
- if (clock->div)
- clk_disable(clock->div);
+ clk_disable(clock->xck);
+ clk_disable(clock->ick);
+ clk_disable(clock->div);
}
return 0;
@@ -1711,8 +1708,7 @@ static const struct snd_soc_dai_ops fsi_dai_ops = {
static struct snd_pcm_hardware fsi_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/sh/hac.c b/sound/soc/sh/hac.c
index 0af2e4dfd139..d5f567e085ff 100644
--- a/sound/soc/sh/hac.c
+++ b/sound/soc/sh/hac.c
@@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
static struct snd_soc_dai_driver sh4_hac_dai[] = {
{
.name = "hac-dai.0",
- .ac97_control = 1,
+ .bus_control = true,
.playback = {
.rates = AC97_RATES,
.formats = AC97_FMTS,
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 1922ec57d10a..70042197f9e2 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -886,8 +886,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
static struct snd_pcm_hardware rsnd_pcm_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE,
+ SNDRV_PCM_INFO_MMAP_VALID,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 32,
.period_bytes_max = 8192,
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
new file mode 100644
index 000000000000..2e10e9a38376
--- /dev/null
+++ b/sound/soc/soc-ac97.c
@@ -0,0 +1,256 @@
+/*
+ * soc-ac97.c -- ALSA SoC Audio Layer AC97 support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * with code, comments and ideas from :-
+ * Richard Purdie <richard@openedhand.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+struct snd_ac97_reset_cfg {
+ struct pinctrl *pctl;
+ struct pinctrl_state *pstate_reset;
+ struct pinctrl_state *pstate_warm_reset;
+ struct pinctrl_state *pstate_run;
+ int gpio_sdata;
+ int gpio_sync;
+ int gpio_reset;
+};
+
+static struct snd_ac97_bus soc_ac97_bus = {
+ .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
+};
+
+static void soc_ac97_device_release(struct device *dev)
+{
+ kfree(to_ac97_t(dev));
+}
+
+/**
+ * snd_soc_new_ac97_codec - initailise AC97 device
+ * @codec: audio codec
+ *
+ * Initialises AC97 codec resources for use by ad-hoc devices only.
+ */
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
+{
+ struct snd_ac97 *ac97;
+ int ret;
+
+ ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+ if (ac97 == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ac97->bus = &soc_ac97_bus;
+ ac97->num = 0;
+
+ ac97->dev.bus = &ac97_bus_type;
+ ac97->dev.parent = codec->component.card->dev;
+ ac97->dev.release = soc_ac97_device_release;
+
+ dev_set_name(&ac97->dev, "%d-%d:%s",
+ codec->component.card->snd_card->number, 0,
+ codec->component.name);
+
+ ret = device_register(&ac97->dev);
+ if (ret) {
+ put_device(&ac97->dev);
+ return ERR_PTR(ret);
+ }
+
+ return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+
+/**
+ * snd_soc_free_ac97_codec - free AC97 codec device
+ * @codec: audio codec
+ *
+ * Frees AC97 codec device resources.
+ */
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
+{
+ device_del(&ac97->dev);
+ ac97->bus = NULL;
+ put_device(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+ struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+ udelay(10);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+ msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+ struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+ udelay(10);
+
+ gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+ pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+ msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+ struct snd_ac97_reset_cfg *cfg)
+{
+ struct pinctrl *p;
+ struct pinctrl_state *state;
+ int gpio;
+ int ret;
+
+ p = devm_pinctrl_get(dev);
+ if (IS_ERR(p)) {
+ dev_err(dev, "Failed to get pinctrl\n");
+ return PTR_ERR(p);
+ }
+ cfg->pctl = p;
+
+ state = pinctrl_lookup_state(p, "ac97-reset");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+ return PTR_ERR(state);
+ }
+ cfg->pstate_reset = state;
+
+ state = pinctrl_lookup_state(p, "ac97-warm-reset");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+ return PTR_ERR(state);
+ }
+ cfg->pstate_warm_reset = state;
+
+ state = pinctrl_lookup_state(p, "ac97-running");
+ if (IS_ERR(state)) {
+ dev_err(dev, "Can't find pinctrl state ac97-running\n");
+ return PTR_ERR(state);
+ }
+ cfg->pstate_run = state;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-sync gpio\n");
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-sync gpio\n");
+ return ret;
+ }
+ cfg->gpio_sync = gpio;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+ return ret;
+ }
+ cfg->gpio_sdata = gpio;
+
+ gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+ if (gpio < 0) {
+ dev_err(dev, "Can't find ac97-reset gpio\n");
+ return gpio;
+ }
+ ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+ if (ret) {
+ dev_err(dev, "Failed requesting ac97-reset gpio\n");
+ return ret;
+ }
+ cfg->gpio_reset = gpio;
+
+ return 0;
+}
+
+struct snd_ac97_bus_ops *soc_ac97_ops;
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+ if (ops == soc_ac97_ops)
+ return 0;
+
+ if (soc_ac97_ops && ops)
+ return -EBUSY;
+
+ soc_ac97_ops = ops;
+ soc_ac97_bus.ops = ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
+
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_ac97_reset_cfg cfg;
+ int ret;
+
+ ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_set_ac97_ops(ops);
+ if (ret)
+ return ret;
+
+ ops->warm_reset = snd_soc_ac97_warm_reset;
+ ops->reset = snd_soc_ac97_reset;
+
+ snd_ac97_rst_cfg = cfg;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
diff --git a/sound/soc/soc-cache.c b/sound/soc/soc-cache.c
index a9f82b5aba9d..07f43356f963 100644
--- a/sound/soc/soc-cache.c
+++ b/sound/soc/soc-cache.c
@@ -15,56 +15,6 @@
#include <linux/export.h>
#include <linux/slab.h>
-#include <trace/events/asoc.h>
-
-static bool snd_soc_set_cache_val(void *base, unsigned int idx,
- unsigned int val, unsigned int word_size)
-{
- switch (word_size) {
- case 1: {
- u8 *cache = base;
- if (cache[idx] == val)
- return true;
- cache[idx] = val;
- break;
- }
- case 2: {
- u16 *cache = base;
- if (cache[idx] == val)
- return true;
- cache[idx] = val;
- break;
- }
- default:
- WARN(1, "Invalid word_size %d\n", word_size);
- break;
- }
- return false;
-}
-
-static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
- unsigned int word_size)
-{
- if (!base)
- return -1;
-
- switch (word_size) {
- case 1: {
- const u8 *cache = base;
- return cache[idx];
- }
- case 2: {
- const u16 *cache = base;
- return cache[idx];
- }
- default:
- WARN(1, "Invalid word_size %d\n", word_size);
- break;
- }
- /* unreachable */
- return -1;
-}
-
int snd_soc_cache_init(struct snd_soc_codec *codec)
{
const struct snd_soc_codec_driver *codec_drv = codec->driver;
@@ -75,8 +25,6 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
if (!reg_size)
return 0;
- mutex_init(&codec->cache_rw_mutex);
-
dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
codec->component.name);
@@ -103,100 +51,3 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
codec->reg_cache = NULL;
return 0;
}
-
-/**
- * snd_soc_cache_read: Fetch the value of a given register from the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The value to be returned.
- */
-int snd_soc_cache_read(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int *value)
-{
- if (!value)
- return -EINVAL;
-
- mutex_lock(&codec->cache_rw_mutex);
- if (!ZERO_OR_NULL_PTR(codec->reg_cache))
- *value = snd_soc_get_cache_val(codec->reg_cache, reg,
- codec->driver->reg_word_size);
- mutex_unlock(&codec->cache_rw_mutex);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_read);
-
-/**
- * snd_soc_cache_write: Set the value of a given register in the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The new register value.
- */
-int snd_soc_cache_write(struct snd_soc_codec *codec,
- unsigned int reg, unsigned int value)
-{
- mutex_lock(&codec->cache_rw_mutex);
- if (!ZERO_OR_NULL_PTR(codec->reg_cache))
- snd_soc_set_cache_val(codec->reg_cache, reg, value,
- codec->driver->reg_word_size);
- mutex_unlock(&codec->cache_rw_mutex);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_write);
-
-static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
-{
- int i;
- int ret;
- const struct snd_soc_codec_driver *codec_drv;
- unsigned int val;
-
- codec_drv = codec->driver;
- for (i = 0; i < codec_drv->reg_cache_size; ++i) {
- ret = snd_soc_cache_read(codec, i, &val);
- if (ret)
- return ret;
- if (codec_drv->reg_cache_default)
- if (snd_soc_get_cache_val(codec_drv->reg_cache_default,
- i, codec_drv->reg_word_size) == val)
- continue;
-
- ret = snd_soc_write(codec, i, val);
- if (ret)
- return ret;
- dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n",
- i, val);
- }
- return 0;
-}
-
-/**
- * snd_soc_cache_sync: Sync the register cache with the hardware.
- *
- * @codec: CODEC to configure.
- *
- * Any registers that should not be synced should be marked as
- * volatile. In general drivers can choose not to use the provided
- * syncing functionality if they so require.
- */
-int snd_soc_cache_sync(struct snd_soc_codec *codec)
-{
- const char *name = "flat";
- int ret;
-
- if (!codec->cache_sync)
- return 0;
-
- dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
- codec->component.name);
- trace_snd_soc_cache_sync(codec, name, "start");
- ret = snd_soc_flat_cache_sync(codec);
- if (!ret)
- codec->cache_sync = 0;
- trace_snd_soc_cache_sync(codec, name, "end");
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index cecfab3cc948..590a82f01d0b 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -258,10 +258,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
- else
- dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
@@ -456,11 +453,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
if (ret < 0)
goto out;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
- else
- dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-
+ dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 4c8f8a23a0e9..11d01e5a2f70 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -34,9 +34,6 @@
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/ac97_codec.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -69,16 +66,6 @@ static int pmdown_time = 5000;
module_param(pmdown_time, int, 0);
MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
-struct snd_ac97_reset_cfg {
- struct pinctrl *pctl;
- struct pinctrl_state *pstate_reset;
- struct pinctrl_state *pstate_warm_reset;
- struct pinctrl_state *pstate_run;
- int gpio_sdata;
- int gpio_sync;
- int gpio_reset;
-};
-
/* returns the minimum number of bytes needed to represent
* a particular given value */
static int min_bytes_needed(unsigned long val)
@@ -309,9 +296,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component)
{
struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
- debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root,
- &codec->cache_sync);
-
codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
codec->component.debugfs_root,
codec, &codec_reg_fops);
@@ -499,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
-#ifdef CONFIG_SND_SOC_AC97_BUS
-/* unregister ac97 codec */
-static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
-{
- if (codec->ac97->dev.bus)
- device_unregister(&codec->ac97->dev);
- return 0;
-}
-
-/* stop no dev release warning */
-static void soc_ac97_device_release(struct device *dev){}
-
-/* register ac97 codec to bus */
-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->component.card->dev;
- codec->ac97->dev.release = soc_ac97_device_release;
-
- dev_set_name(&codec->ac97->dev, "%d-%d:%s",
- 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");
- codec->ac97->dev.bus = NULL;
- return err;
- }
- return 0;
-}
-#endif
-
static void codec2codec_close_delayed_work(struct work_struct *work)
{
/* Currently nothing to do for c2c links
@@ -592,17 +542,12 @@ int snd_soc_suspend(struct device *dev)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_platform *platform = card->rtd[i].platform;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
cpu_dai->driver->suspend(cpu_dai);
- if (platform->driver->suspend && !platform->suspended) {
- platform->driver->suspend(cpu_dai);
- platform->suspended = 1;
- }
}
/* close any waiting streams and save state */
@@ -629,8 +574,8 @@ int snd_soc_suspend(struct device *dev)
SND_SOC_DAPM_STREAM_SUSPEND);
}
- /* Recheck all analogue paths too */
- dapm_mark_io_dirty(&card->dapm);
+ /* Recheck all endpoints too, their state is affected by suspend */
+ dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* suspend all CODECs */
@@ -656,7 +601,6 @@ int snd_soc_suspend(struct device *dev)
if (codec->driver->suspend)
codec->driver->suspend(codec);
codec->suspended = 1;
- codec->cache_sync = 1;
if (codec->component.regmap)
regcache_mark_dirty(codec->component.regmap);
/* deactivate pins to sleep state */
@@ -676,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
cpu_dai->driver->suspend(cpu_dai);
/* deactivate pins to sleep state */
@@ -712,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
if (card->resume_pre)
card->resume_pre(card);
- /* resume AC97 DAIs */
+ /* resume control bus DAIs */
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
cpu_dai->driver->resume(cpu_dai);
}
@@ -775,17 +719,12 @@ static void soc_resume_deferred(struct work_struct *work)
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- struct snd_soc_platform *platform = card->rtd[i].platform;
if (card->rtd[i].dai_link->ignore_suspend)
continue;
- if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+ if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
cpu_dai->driver->resume(cpu_dai);
- if (platform->driver->resume && platform->suspended) {
- platform->driver->resume(cpu_dai);
- platform->suspended = 0;
- }
}
if (card->resume_post)
@@ -796,8 +735,8 @@ static void soc_resume_deferred(struct work_struct *work)
/* userspace can access us now we are back as we were before */
snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
- /* Recheck all analogue paths too */
- dapm_mark_io_dirty(&card->dapm);
+ /* Recheck all endpoints too, their state is affected by suspend */
+ dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
}
@@ -805,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
int snd_soc_resume(struct device *dev)
{
struct snd_soc_card *card = dev_get_drvdata(dev);
- int i, ac97_control = 0;
+ bool bus_control = false;
+ int i;
/* If the card is not initialized yet there is nothing to do */
if (!card->instantiated)
@@ -828,17 +768,18 @@ int snd_soc_resume(struct device *dev)
}
}
- /* AC97 devices might have other drivers hanging off them so
- * need to resume immediately. Other drivers don't have that
- * problem and may take a substantial amount of time to resume
+ /*
+ * DAIs that also act as the control bus master might have other drivers
+ * hanging off them so need to resume immediately. Other drivers don't
+ * have that problem and may take a substantial amount of time to resume
* due to I/O costs and anti-pop so handle them out of line.
*/
for (i = 0; i < card->num_rtd; i++) {
struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
- ac97_control |= cpu_dai->driver->ac97_control;
+ bus_control |= cpu_dai->driver->bus_control;
}
- if (ac97_control) {
- dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
+ if (bus_control) {
+ dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
soc_resume_deferred(&card->deferred_resume_work);
} else {
dev_dbg(dev, "ASoC: Scheduling resume work\n");
@@ -884,7 +825,7 @@ static struct snd_soc_dai *snd_soc_find_dai(
list_for_each_entry(component, &component_list, list) {
if (dlc->of_node && component->dev->of_node != dlc->of_node)
continue;
- if (dlc->name && strcmp(dev_name(component->dev), dlc->name))
+ if (dlc->name && strcmp(component->name, dlc->name))
continue;
list_for_each_entry(dai, &component->dai_list, list) {
if (dlc->dai_name && strcmp(dai->name, dlc->dai_name))
@@ -1251,25 +1192,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
return 0;
}
-static int soc_probe_codec_dai(struct snd_soc_card *card,
- struct snd_soc_dai *codec_dai,
- int order)
+static int soc_probe_dai(struct snd_soc_dai *dai, int order)
{
int ret;
- if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
- if (codec_dai->driver->probe) {
- ret = codec_dai->driver->probe(codec_dai);
+ if (!dai->probed && dai->driver->probe_order == order) {
+ if (dai->driver->probe) {
+ ret = dai->driver->probe(dai);
if (ret < 0) {
- dev_err(codec_dai->dev,
- "ASoC: failed to probe CODEC DAI %s: %d\n",
- codec_dai->name, ret);
+ dev_err(dai->dev,
+ "ASoC: failed to probe DAI %s: %d\n",
+ dai->name, ret);
return ret;
}
}
- /* mark codec_dai as probed and add to card dai list */
- codec_dai->probed = 1;
+ dai->probed = 1;
}
return 0;
@@ -1319,40 +1257,22 @@ 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_platform *platform = rtd->platform;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
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;
- 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;
- /* probe the cpu_dai */
- if (!cpu_dai->probed &&
- cpu_dai->driver->probe_order == order) {
- if (cpu_dai->driver->probe) {
- ret = cpu_dai->driver->probe(cpu_dai);
- if (ret < 0) {
- dev_err(cpu_dai->dev,
- "ASoC: failed to probe CPU DAI %s: %d\n",
- cpu_dai->name, ret);
- return ret;
- }
- }
- cpu_dai->probed = 1;
- }
+ ret = soc_probe_dai(cpu_dai, order);
+ if (ret)
+ return ret;
/* probe the CODEC DAI */
for (i = 0; i < rtd->num_codecs; i++) {
- ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+ ret = soc_probe_dai(rtd->codec_dais[i], order);
if (ret)
return ret;
}
@@ -1422,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
}
}
- /* add platform data for AC97 devices */
- 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;
}
-#ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_codec(struct snd_soc_codec *codec,
- struct snd_soc_dai *codec_dai)
-{
- int ret;
-
- /* Only instantiate AC97 if not already done by the adaptor
- * for the generic AC97 subsystem.
- */
- if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
- /*
- * It is possible that the AC97 device is already registered to
- * the device subsystem. This happens when the device is created
- * via snd_ac97_mixer(). Currently only SoC codec that does so
- * is the generic AC97 glue but others migh emerge.
- *
- * In those cases we don't try to register the device again.
- */
- if (!codec->ac97_created)
- return 0;
-
- ret = soc_ac97_dev_register(codec);
- if (ret < 0) {
- dev_err(codec->dev,
- "ASoC: AC97 device register failed: %d\n", ret);
- return ret;
- }
-
- codec->ac97_registered = 1;
- }
- return 0;
-}
-
-static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
-{
- if (codec->ac97_registered) {
- soc_ac97_dev_unregister(codec);
- codec->ac97_registered = 0;
- }
-}
-
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
- int i, ret;
-
- for (i = 0; i < rtd->num_codecs; i++) {
- struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
- 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 0;
-}
-
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
- int i;
-
- for (i = 0; i < rtd->num_codecs; i++)
- soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
-}
-#endif
-
static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
{
struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
@@ -1793,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
goto probe_aux_dev_err;
}
-#ifdef CONFIG_SND_SOC_AC97_BUS
- /* register any AC97 codecs */
- for (i = 0; i < card->num_rtd; i++) {
- ret = soc_register_ac97_dai_link(&card->rtd[i]);
- if (ret < 0) {
- dev_err(card->dev,
- "ASoC: failed to register AC97: %d\n", ret);
- while (--i >= 0)
- soc_unregister_ac97_dai_link(&card->rtd[i]);
- goto probe_aux_dev_err;
- }
- }
-#endif
-
card->instantiated = 1;
snd_soc_dapm_sync(&card->dapm);
mutex_unlock(&card->mutex);
@@ -1949,216 +1780,6 @@ static struct platform_driver soc_driver = {
};
/**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
- * @ops: AC97 bus operations
- * @num: AC97 codec number
- *
- * Initialises AC97 codec resources for use by ad-hoc devices only.
- */
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
- struct snd_ac97_bus_ops *ops, int num)
-{
- codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
- if (codec->ac97 == NULL)
- return -ENOMEM;
-
- codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
- if (codec->ac97->bus == NULL) {
- kfree(codec->ac97);
- codec->ac97 = NULL;
- return -ENOMEM;
- }
-
- codec->ac97->bus->ops = ops;
- codec->ac97->num = num;
-
- /*
- * Mark the AC97 device to be created by us. This way we ensure that the
- * device will be registered with the device subsystem later on.
- */
- codec->ac97_created = 1;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
-
-static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
-
-static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
-{
- struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
-
- udelay(10);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
- msleep(2);
-}
-
-static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
-{
- struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
-
- udelay(10);
-
- gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
-
- pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
- msleep(2);
-}
-
-static int snd_soc_ac97_parse_pinctl(struct device *dev,
- struct snd_ac97_reset_cfg *cfg)
-{
- struct pinctrl *p;
- struct pinctrl_state *state;
- int gpio;
- int ret;
-
- p = devm_pinctrl_get(dev);
- if (IS_ERR(p)) {
- dev_err(dev, "Failed to get pinctrl\n");
- return PTR_ERR(p);
- }
- cfg->pctl = p;
-
- state = pinctrl_lookup_state(p, "ac97-reset");
- if (IS_ERR(state)) {
- dev_err(dev, "Can't find pinctrl state ac97-reset\n");
- return PTR_ERR(state);
- }
- cfg->pstate_reset = state;
-
- state = pinctrl_lookup_state(p, "ac97-warm-reset");
- if (IS_ERR(state)) {
- dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
- return PTR_ERR(state);
- }
- cfg->pstate_warm_reset = state;
-
- state = pinctrl_lookup_state(p, "ac97-running");
- if (IS_ERR(state)) {
- dev_err(dev, "Can't find pinctrl state ac97-running\n");
- return PTR_ERR(state);
- }
- cfg->pstate_run = state;
-
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sync gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sync");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sync gpio\n");
- return ret;
- }
- cfg->gpio_sync = gpio;
-
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-sdata gpio\n");
- return ret;
- }
- cfg->gpio_sdata = gpio;
-
- gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
- if (gpio < 0) {
- dev_err(dev, "Can't find ac97-reset gpio\n");
- return gpio;
- }
- ret = devm_gpio_request(dev, gpio, "AC97 link reset");
- if (ret) {
- dev_err(dev, "Failed requesting ac97-reset gpio\n");
- return ret;
- }
- cfg->gpio_reset = gpio;
-
- return 0;
-}
-
-struct snd_ac97_bus_ops *soc_ac97_ops;
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
-{
- if (ops == soc_ac97_ops)
- return 0;
-
- if (soc_ac97_ops && ops)
- return -EBUSY;
-
- soc_ac97_ops = ops;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
-
-/**
- * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
- *
- * This function sets the reset and warm_reset properties of ops and parses
- * the device node of pdev to get pinctrl states and gpio numbers to use.
- */
-int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
- struct platform_device *pdev)
-{
- struct device *dev = &pdev->dev;
- struct snd_ac97_reset_cfg cfg;
- int ret;
-
- ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
- if (ret)
- return ret;
-
- ret = snd_soc_set_ac97_ops(ops);
- if (ret)
- return ret;
-
- ops->warm_reset = snd_soc_ac97_warm_reset;
- ops->reset = snd_soc_ac97_reset;
-
- snd_ac97_rst_cfg = cfg;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
-
-/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
- *
- * Frees AC97 codec device resources.
- */
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
-{
-#ifdef CONFIG_SND_SOC_AC97_BUS
- soc_unregister_ac97_codec(codec);
-#endif
- kfree(codec->ac97->bus);
- kfree(codec->ac97);
- codec->ac97 = NULL;
- codec->ac97_created = 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
-
-/**
* snd_soc_cnew - create new control
* @_template: control template
* @data: control private data
@@ -2326,7 +1947,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
const struct snd_kcontrol_new *controls, int num_controls)
{
- struct snd_card *card = dai->card->snd_card;
+ struct snd_card *card = dai->component->card->snd_card;
return snd_soc_add_controls(card, dai->dev, controls, num_controls,
NULL, dai);
@@ -2334,1020 +1955,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
/**
- * snd_soc_info_enum_double - enumerated double mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a double enumerated
- * mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
- uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
- uinfo->value.enumerated.items = e->items;
-
- if (uinfo->value.enumerated.item >= e->items)
- uinfo->value.enumerated.item = e->items - 1;
- strlcpy(uinfo->value.enumerated.name,
- e->texts[uinfo->value.enumerated.item],
- sizeof(uinfo->value.enumerated.name));
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
-
-/**
- * snd_soc_get_enum_double - enumerated double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double enumerated mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int val, item;
- unsigned int reg_val;
- int ret;
-
- ret = snd_soc_component_read(component, e->reg, &reg_val);
- if (ret)
- return ret;
- val = (reg_val >> e->shift_l) & e->mask;
- item = snd_soc_enum_val_to_item(e, val);
- ucontrol->value.enumerated.item[0] = item;
- if (e->shift_l != e->shift_r) {
- val = (reg_val >> e->shift_l) & e->mask;
- item = snd_soc_enum_val_to_item(e, val);
- ucontrol->value.enumerated.item[1] = item;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
-
-/**
- * snd_soc_put_enum_double - enumerated double mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double enumerated mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int *item = ucontrol->value.enumerated.item;
- unsigned int val;
- unsigned int mask;
-
- if (item[0] >= e->items)
- return -EINVAL;
- val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
- mask = e->mask << e->shift_l;
- if (e->shift_l != e->shift_r) {
- if (item[1] >= e->items)
- return -EINVAL;
- val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
- mask |= e->mask << e->shift_r;
- }
-
- return snd_soc_component_update_bits(component, e->reg, mask, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
-
-/**
- * snd_soc_read_signed - Read a codec register and interprete as signed value
- * @component: component
- * @reg: Register to read
- * @mask: Mask to use after shifting the register value
- * @shift: Right shift of register value
- * @sign_bit: Bit that describes if a number is negative or not.
- * @signed_val: Pointer to where the read value should be stored
- *
- * This functions reads a codec register. The register value is shifted right
- * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
- * the given registervalue into a signed integer if sign_bit is non-zero.
- *
- * Returns 0 on sucess, otherwise an error value
- */
-static int snd_soc_read_signed(struct snd_soc_component *component,
- unsigned int reg, unsigned int mask, unsigned int shift,
- unsigned int sign_bit, int *signed_val)
-{
- int ret;
- unsigned int val;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret < 0)
- return ret;
-
- val = (val >> shift) & mask;
-
- if (!sign_bit) {
- *signed_val = val;
- return 0;
- }
-
- /* non-negative number */
- if (!(val & BIT(sign_bit))) {
- *signed_val = val;
- return 0;
- }
-
- ret = val;
-
- /*
- * The register most probably does not contain a full-sized int.
- * Instead we have an arbitrary number of bits in a signed
- * representation which has to be translated into a full-sized int.
- * This is done by filling up all bits above the sign-bit.
- */
- ret |= ~((int)(BIT(sign_bit) - 1));
-
- *signed_val = ret;
-
- return 0;
-}
-
-/**
- * snd_soc_info_volsw - single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single mixer control, or a double
- * mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int platform_max;
-
- if (!mc->platform_max)
- mc->platform_max = mc->max;
- platform_max = mc->platform_max;
-
- if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
- else
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
- uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = platform_max - mc->min;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
-
-/**
- * snd_soc_get_volsw - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- int sign_bit = mc->sign_bit;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- int val;
- int ret;
-
- if (sign_bit)
- mask = BIT(sign_bit + 1) - 1;
-
- ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] = val - min;
- if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
-
- if (snd_soc_volsw_is_stereo(mc)) {
- if (reg == reg2)
- ret = snd_soc_read_signed(component, reg, mask, rshift,
- sign_bit, &val);
- else
- ret = snd_soc_read_signed(component, reg2, mask, shift,
- sign_bit, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[1] = val - min;
- if (invert)
- ucontrol->value.integer.value[1] =
- max - ucontrol->value.integer.value[1];
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
-
-/**
- * snd_soc_put_volsw - single mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- unsigned int sign_bit = mc->sign_bit;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- int err;
- bool type_2r = false;
- unsigned int val2 = 0;
- unsigned int val, val_mask;
-
- if (sign_bit)
- mask = BIT(sign_bit + 1) - 1;
-
- val = ((ucontrol->value.integer.value[0] + min) & mask);
- if (invert)
- val = max - val;
- val_mask = mask << shift;
- val = val << shift;
- if (snd_soc_volsw_is_stereo(mc)) {
- val2 = ((ucontrol->value.integer.value[1] + min) & mask);
- if (invert)
- val2 = max - val2;
- if (reg == reg2) {
- val_mask |= mask << rshift;
- val |= val2 << rshift;
- } else {
- val2 = val2 << shift;
- type_2r = true;
- }
- }
- err = snd_soc_component_update_bits(component, reg, val_mask, val);
- if (err < 0)
- return err;
-
- if (type_2r)
- err = snd_soc_component_update_bits(component, reg2, val_mask,
- val2);
-
- return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
-
-/**
- * snd_soc_get_volsw_sx - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
- unsigned int val;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret < 0)
- return ret;
-
- ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- ret = snd_soc_component_read(component, reg2, &val);
- if (ret < 0)
- return ret;
-
- val = ((val >> rshift) - min) & mask;
- ucontrol->value.integer.value[1] = val;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
-
-/**
- * snd_soc_put_volsw_sx - double mixer set callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
-
- unsigned int reg = mc->reg;
- unsigned int reg2 = mc->rreg;
- unsigned int shift = mc->shift;
- unsigned int rshift = mc->rshift;
- int max = mc->max;
- int min = mc->min;
- int mask = (1 << (fls(min + max) - 1)) - 1;
- int err = 0;
- unsigned int val, val_mask, val2 = 0;
-
- val_mask = mask << shift;
- val = (ucontrol->value.integer.value[0] + min) & mask;
- val = val << shift;
-
- err = snd_soc_component_update_bits(component, reg, val_mask, val);
- if (err < 0)
- return err;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- val_mask = mask << rshift;
- val2 = (ucontrol->value.integer.value[1] + min) & mask;
- val2 = val2 << rshift;
-
- err = snd_soc_component_update_bits(component, reg2, val_mask,
- val2);
- }
- return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
-
-/**
- * snd_soc_info_volsw_s8 - signed mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int platform_max;
- int min = mc->min;
-
- if (!mc->platform_max)
- mc->platform_max = mc->max;
- platform_max = mc->platform_max;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 2;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = platform_max - min;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
-
-/**
- * snd_soc_get_volsw_s8 - signed mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- unsigned int val;
- int min = mc->min;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] =
- ((signed char)(val & 0xff))-min;
- ucontrol->value.integer.value[1] =
- ((signed char)((val >> 8) & 0xff))-min;
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
-
-/**
- * snd_soc_put_volsw_sgn - signed mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- int min = mc->min;
- unsigned int val;
-
- val = (ucontrol->value.integer.value[0]+min) & 0xff;
- val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
-
- return snd_soc_component_update_bits(component, reg, 0xffff, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
-
-/**
- * snd_soc_info_volsw_range - single mixer info callback with range.
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information, within a range, about a single
- * mixer control.
- *
- * returns 0 for success.
- */
-int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int platform_max;
- int min = mc->min;
-
- if (!mc->platform_max)
- mc->platform_max = mc->max;
- platform_max = mc->platform_max;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = platform_max - min;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
-
-/**
- * snd_soc_put_volsw_range - single mixer put value callback with range.
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value, within a range, for a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- unsigned int reg = mc->reg;
- unsigned int rreg = mc->rreg;
- unsigned int shift = mc->shift;
- int min = mc->min;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- unsigned int val, val_mask;
- int ret;
-
- if (invert)
- val = (max - ucontrol->value.integer.value[0]) & mask;
- else
- val = ((ucontrol->value.integer.value[0] + min) & mask);
- val_mask = mask << shift;
- val = val << shift;
-
- ret = snd_soc_component_update_bits(component, reg, val_mask, val);
- if (ret < 0)
- return ret;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- if (invert)
- val = (max - ucontrol->value.integer.value[1]) & mask;
- else
- val = ((ucontrol->value.integer.value[1] + min) & mask);
- val_mask = mask << shift;
- val = val << shift;
-
- ret = snd_soc_component_update_bits(component, rreg, val_mask,
- val);
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
-
-/**
- * snd_soc_get_volsw_range - single mixer get callback with range
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value, within a range, of a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int rreg = mc->rreg;
- unsigned int shift = mc->shift;
- int min = mc->min;
- int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int invert = mc->invert;
- unsigned int val;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[0] = (val >> shift) & mask;
- if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
- else
- ucontrol->value.integer.value[0] =
- ucontrol->value.integer.value[0] - min;
-
- if (snd_soc_volsw_is_stereo(mc)) {
- ret = snd_soc_component_read(component, rreg, &val);
- if (ret)
- return ret;
-
- ucontrol->value.integer.value[1] = (val >> shift) & mask;
- if (invert)
- ucontrol->value.integer.value[1] =
- max - ucontrol->value.integer.value[1];
- else
- ucontrol->value.integer.value[1] =
- ucontrol->value.integer.value[1] - min;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
-
-/**
- * snd_soc_limit_volume - Set new limit to an existing volume control.
- *
- * @codec: where to look for the control
- * @name: Name of the control
- * @max: new maximum limit
- *
- * Return 0 for success, else error.
- */
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
- const char *name, int max)
-{
- struct snd_card *card = codec->component.card->snd_card;
- struct snd_kcontrol *kctl;
- struct soc_mixer_control *mc;
- int found = 0;
- int ret = -EINVAL;
-
- /* Sanity check for name and max */
- if (unlikely(!name || max <= 0))
- return -EINVAL;
-
- list_for_each_entry(kctl, &card->controls, list) {
- if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
- found = 1;
- break;
- }
- }
- if (found) {
- mc = (struct soc_mixer_control *)kctl->private_value;
- if (max <= mc->max) {
- mc->platform_max = max;
- ret = 0;
- }
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-
-int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_bytes *params = (void *)kcontrol->private_value;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = params->num_regs * component->val_bytes;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
-
-int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_bytes *params = (void *)kcontrol->private_value;
- int ret;
-
- if (component->regmap)
- ret = regmap_raw_read(component->regmap, params->base,
- ucontrol->value.bytes.data,
- params->num_regs * component->val_bytes);
- else
- ret = -EINVAL;
-
- /* Hide any masked bytes to ensure consistent data reporting */
- if (ret == 0 && params->mask) {
- switch (component->val_bytes) {
- case 1:
- ucontrol->value.bytes.data[0] &= ~params->mask;
- break;
- case 2:
- ((u16 *)(&ucontrol->value.bytes.data))[0]
- &= cpu_to_be16(~params->mask);
- break;
- case 4:
- ((u32 *)(&ucontrol->value.bytes.data))[0]
- &= cpu_to_be32(~params->mask);
- break;
- default:
- return -EINVAL;
- }
- }
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
-
-int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_bytes *params = (void *)kcontrol->private_value;
- int ret, len;
- unsigned int val, mask;
- void *data;
-
- if (!component->regmap || !params->num_regs)
- return -EINVAL;
-
- len = params->num_regs * component->val_bytes;
-
- data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
- if (!data)
- return -ENOMEM;
-
- /*
- * If we've got a mask then we need to preserve the register
- * bits. We shouldn't modify the incoming data so take a
- * copy.
- */
- if (params->mask) {
- ret = regmap_read(component->regmap, params->base, &val);
- if (ret != 0)
- goto out;
-
- val &= params->mask;
-
- switch (component->val_bytes) {
- case 1:
- ((u8 *)data)[0] &= ~params->mask;
- ((u8 *)data)[0] |= val;
- break;
- case 2:
- mask = ~params->mask;
- ret = regmap_parse_val(component->regmap,
- &mask, &mask);
- if (ret != 0)
- goto out;
-
- ((u16 *)data)[0] &= mask;
-
- ret = regmap_parse_val(component->regmap,
- &val, &val);
- if (ret != 0)
- goto out;
-
- ((u16 *)data)[0] |= val;
- break;
- case 4:
- mask = ~params->mask;
- ret = regmap_parse_val(component->regmap,
- &mask, &mask);
- if (ret != 0)
- goto out;
-
- ((u32 *)data)[0] &= mask;
-
- ret = regmap_parse_val(component->regmap,
- &val, &val);
- if (ret != 0)
- goto out;
-
- ((u32 *)data)[0] |= val;
- break;
- default:
- ret = -EINVAL;
- goto out;
- }
- }
-
- ret = regmap_raw_write(component->regmap, params->base,
- data, len);
-
-out:
- kfree(data);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
-
-int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *ucontrol)
-{
- struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-
- ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- ucontrol->count = params->max;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
-
-int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
- unsigned int size, unsigned int __user *tlv)
-{
- struct soc_bytes_ext *params = (void *)kcontrol->private_value;
- unsigned int count = size < params->max ? size : params->max;
- int ret = -ENXIO;
-
- switch (op_flag) {
- case SNDRV_CTL_TLV_OP_READ:
- if (params->get)
- ret = params->get(tlv, count);
- break;
- case SNDRV_CTL_TLV_OP_WRITE:
- if (params->put)
- ret = params->put(tlv, count);
- break;
- }
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
-
-/**
- * snd_soc_info_xr_sx - signed multi register info callback
- * @kcontrol: mreg control
- * @uinfo: control element information
- *
- * Callback to provide information of a control that can
- * span multiple codec registers which together
- * forms a single signed value in a MSB/LSB manner.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
- uinfo->value.integer.min = mc->min;
- uinfo->value.integer.max = mc->max;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
-
-/**
- * snd_soc_get_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to get the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- unsigned int regbase = mc->regbase;
- unsigned int regcount = mc->regcount;
- unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
- unsigned int regwmask = (1<<regwshift)-1;
- unsigned int invert = mc->invert;
- unsigned long mask = (1UL<<mc->nbits)-1;
- long min = mc->min;
- long max = mc->max;
- long val = 0;
- unsigned int regval;
- unsigned int i;
- int ret;
-
- for (i = 0; i < regcount; i++) {
- ret = snd_soc_component_read(component, regbase+i, &regval);
- if (ret)
- return ret;
- val |= (regval & regwmask) << (regwshift*(regcount-i-1));
- }
- val &= mask;
- if (min < 0 && val > max)
- val |= ~mask;
- if (invert)
- val = max - val;
- ucontrol->value.integer.value[0] = val;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
-
-/**
- * snd_soc_put_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to set the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mreg_control *mc =
- (struct soc_mreg_control *)kcontrol->private_value;
- unsigned int regbase = mc->regbase;
- unsigned int regcount = mc->regcount;
- unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
- unsigned int regwmask = (1<<regwshift)-1;
- unsigned int invert = mc->invert;
- unsigned long mask = (1UL<<mc->nbits)-1;
- long max = mc->max;
- long val = ucontrol->value.integer.value[0];
- unsigned int i, regval, regmask;
- int err;
-
- if (invert)
- val = max - val;
- val &= mask;
- for (i = 0; i < regcount; i++) {
- regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
- regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
- err = snd_soc_component_update_bits(component, regbase+i,
- regmask, regval);
- if (err < 0)
- return err;
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
-
-/**
- * snd_soc_get_strobe - strobe get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback get the value of a strobe mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = 1 << shift;
- unsigned int invert = mc->invert != 0;
- unsigned int val;
- int ret;
-
- ret = snd_soc_component_read(component, reg, &val);
- if (ret)
- return ret;
-
- val &= mask;
-
- if (shift != 0 && val != 0)
- val = val >> shift;
- ucontrol->value.enumerated.item[0] = val ^ invert;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
-
-/**
- * snd_soc_put_strobe - strobe put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback strobe a register bit to high then low (or the inverse)
- * in one pass of a single mixer enum control.
- *
- * Returns 1 for success.
- */
-int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg = mc->reg;
- unsigned int shift = mc->shift;
- unsigned int mask = 1 << shift;
- unsigned int invert = mc->invert != 0;
- unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
- unsigned int val1 = (strobe ^ invert) ? mask : 0;
- unsigned int val2 = (strobe ^ invert) ? 0 : mask;
- int err;
-
- err = snd_soc_component_update_bits(component, reg, mask, val1);
- if (err < 0)
- return err;
-
- return snd_soc_component_update_bits(component, reg, mask, val2);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
-
-/**
* snd_soc_dai_set_sysclk - configure DAI system or master clock.
* @dai: DAI
* @clk_id: DAI specific clock ID
@@ -3996,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
return 0;
}
-static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
{
- if (!component->regmap)
- component->regmap = dev_get_regmap(component->dev, NULL);
- if (component->regmap) {
- int val_bytes = regmap_get_val_bytes(component->regmap);
- /* Errors are legitimate for non-integer byte multiples */
- if (val_bytes > 0)
- component->val_bytes = val_bytes;
- }
+ int val_bytes = regmap_get_val_bytes(component->regmap);
+
+ /* Errors are legitimate for non-integer byte multiples */
+ if (val_bytes > 0)
+ component->val_bytes = val_bytes;
}
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+ struct regmap *regmap)
+{
+ component->regmap = regmap;
+ snd_soc_component_setup_regmap(component);
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+ regmap_exit(component->regmap);
+ component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
+
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
- if (!component->write && !component->read)
- snd_soc_component_init_regmap(component);
+ if (!component->write && !component->read) {
+ if (!component->regmap)
+ component->regmap = dev_get_regmap(component->dev, NULL);
+ if (component->regmap)
+ snd_soc_component_setup_regmap(component);
+ }
list_add(&component->list, &component_list);
}
@@ -4362,7 +3009,6 @@ int snd_soc_register_codec(struct device *dev,
codec->dev = dev;
codec->driver = codec_drv;
codec->component.val_bytes = codec_drv->reg_word_size;
- mutex_init(&codec->mutex);
#ifdef CONFIG_DEBUG_FS
codec->component.init_debugfs = soc_init_codec_debugfs;
@@ -4585,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
const char *propname)
{
struct device_node *np = card->dev->of_node;
- int num_routes;
+ int num_routes, old_routes;
struct snd_soc_dapm_route *routes;
int i, ret;
@@ -4603,7 +3249,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
return -EINVAL;
}
- routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+ old_routes = card->num_dapm_routes;
+ routes = devm_kzalloc(card->dev,
+ (old_routes + num_routes) * sizeof(*routes),
GFP_KERNEL);
if (!routes) {
dev_err(card->dev,
@@ -4611,9 +3259,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
return -EINVAL;
}
+ memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes));
+
for (i = 0; i < num_routes; i++) {
ret = of_property_read_string_index(np, propname,
- 2 * i, &routes[i].sink);
+ 2 * i, &routes[old_routes + i].sink);
if (ret) {
dev_err(card->dev,
"ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4621,7 +3271,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
return -EINVAL;
}
ret = of_property_read_string_index(np, propname,
- (2 * i) + 1, &routes[i].source);
+ (2 * i) + 1, &routes[old_routes + i].source);
if (ret) {
dev_err(card->dev,
"ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4630,7 +3280,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
}
}
- card->num_dapm_routes = num_routes;
+ card->num_dapm_routes += num_routes;
card->dapm_routes = routes;
return 0;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c61cb9cedbcd..c5136bb1f982 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
}
}
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
+/*
+ * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
+ * paths
+ * @w: The widget for which to invalidate the cached number of input paths
+ *
+ * The function resets the cached number of inputs for the specified widget and
+ * all widgets that can be reached via outgoing paths from the widget.
+ *
+ * This function must be called if the number of input paths for a widget might
+ * have changed. E.g. if the source state of a widget changes or a path is added
+ * or activated with the widget as the sink.
+ */
+static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget *sink;
+ struct snd_soc_dapm_path *p;
+ LIST_HEAD(list);
+
+ dapm_assert_locked(w->dapm);
+
+ if (w->inputs == -1)
+ return;
+
+ w->inputs = -1;
+ list_add_tail(&w->work_list, &list);
+
+ list_for_each_entry(w, &list, work_list) {
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->is_supply || p->weak || !p->connect)
+ continue;
+ sink = p->sink;
+ if (sink->inputs != -1) {
+ sink->inputs = -1;
+ list_add_tail(&sink->work_list, &list);
+ }
+ }
+ }
+}
+
+/*
+ * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
+ * output paths
+ * @w: The widget for which to invalidate the cached number of output paths
+ *
+ * Resets the cached number of outputs for the specified widget and all widgets
+ * that can be reached via incoming paths from the widget.
+ *
+ * This function must be called if the number of output paths for a widget might
+ * have changed. E.g. if the sink state of a widget changes or a path is added
+ * or activated with the widget as the source.
+ */
+static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_widget *source;
+ struct snd_soc_dapm_path *p;
+ LIST_HEAD(list);
+
+ dapm_assert_locked(w->dapm);
+
+ if (w->outputs == -1)
+ return;
+
+ w->outputs = -1;
+ list_add_tail(&w->work_list, &list);
+
+ list_for_each_entry(w, &list, work_list) {
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->is_supply || p->weak || !p->connect)
+ continue;
+ source = p->source;
+ if (source->outputs != -1) {
+ source->outputs = -1;
+ list_add_tail(&source->work_list, &list);
+ }
+ }
+ }
+}
+
+/*
+ * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
+ * for the widgets connected to a path
+ * @p: The path to invalidate
+ *
+ * Resets the cached number of inputs for the sink of the path and the cached
+ * number of outputs for the source of the path.
+ *
+ * This function must be called when a path is added, removed or the connected
+ * state changes.
+ */
+static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
+{
+ /*
+ * Weak paths or supply paths do not influence the number of input or
+ * output paths of their neighbors.
+ */
+ if (p->weak || p->is_supply)
+ return;
+
+ /*
+ * The number of connected endpoints is the sum of the number of
+ * connected endpoints of all neighbors. If a node with 0 connected
+ * endpoints is either connected or disconnected that sum won't change,
+ * so there is no need to re-check the path.
+ */
+ if (p->source->inputs != 0)
+ dapm_widget_invalidate_input_paths(p->sink);
+ if (p->sink->outputs != 0)
+ dapm_widget_invalidate_output_paths(p->source);
+}
+
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
{
- struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w;
mutex_lock(&card->dapm_mutex);
list_for_each_entry(w, &card->widgets, list) {
- switch (w->id) {
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- dapm_mark_dirty(w, "Rechecking inputs and outputs");
- break;
- default:
- break;
+ if (w->is_sink || w->is_source) {
+ dapm_mark_dirty(w, "Rechecking endpoints");
+ if (w->is_sink)
+ dapm_widget_invalidate_output_paths(w);
+ if (w->is_source)
+ dapm_widget_invalidate_input_paths(w);
}
}
mutex_unlock(&card->dapm_mutex);
}
-EXPORT_SYMBOL_GPL(dapm_mark_io_dirty);
+EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
/* create a new dapm widget */
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
list_for_each_entry(w, &card->widgets, list) {
w->new_power = w->power;
w->power_checked = false;
- w->inputs = -1;
- w->outputs = -1;
}
}
@@ -469,10 +575,9 @@ out:
/* connect mux widget to its interconnecting audio paths */
static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
- struct snd_soc_dapm_path *path, const char *control_name,
- const struct snd_kcontrol_new *kcontrol)
+ struct snd_soc_dapm_path *path, const char *control_name)
{
+ const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int val, item;
int i;
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
for (i = 0; i < e->items; i++) {
if (!(strcmp(control_name, e->texts[i]))) {
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &dest->sources);
- list_add(&path->list_source, &src->sinks);
- path->name = (char*)e->texts[i];
+ path->name = e->texts[i];
if (i == item)
path->connect = 1;
else
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
}
/* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
- struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)
- w->kcontrol_news[i].private_value;
+ p->sink->kcontrol_news[i].private_value;
unsigned int reg = mc->reg;
unsigned int shift = mc->shift;
unsigned int max = mc->max;
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
unsigned int val;
if (reg != SND_SOC_NOPM) {
- soc_dapm_read(w->dapm, reg, &val);
+ soc_dapm_read(p->sink->dapm, reg, &val);
val = (val >> shift) & mask;
if (invert)
val = max - val;
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
/* connect mixer widget to its interconnecting audio paths */
static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
struct snd_soc_dapm_path *path, const char *control_name)
{
int i;
/* search for mixer kcontrol */
- for (i = 0; i < dest->num_kcontrols; i++) {
- if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &dest->sources);
- list_add(&path->list_source, &src->sinks);
- path->name = dest->kcontrol_news[i].name;
- dapm_set_mixer_path_status(dest, path, i);
+ for (i = 0; i < path->sink->num_kcontrols; i++) {
+ if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
+ path->name = path->sink->kcontrol_news[i].name;
+ dapm_set_mixer_path_status(path, i);
return 0;
}
}
@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
if (ret < 0)
return ret;
- list_for_each_entry(path, &w->sources, list_sink)
- dapm_kcontrol_add_path(w->kcontrols[0], path);
+ list_for_each_entry(path, &w->sources, list_sink) {
+ if (path->name)
+ dapm_kcontrol_add_path(w->kcontrols[0], path);
+ }
return 0;
}
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
return 0;
}
-/* reset 'walked' bit for each dapm path */
-static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
- struct list_head *sink)
-{
- struct snd_soc_dapm_path *p;
-
- list_for_each_entry(p, sink, list_source) {
- if (p->walked) {
- p->walked = 0;
- dapm_clear_walk_output(dapm, &p->sink->sinks);
- }
- }
-}
-
-static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
- struct list_head *source)
-{
- struct snd_soc_dapm_path *p;
-
- list_for_each_entry(p, source, list_sink) {
- if (p->walked) {
- p->walked = 0;
- dapm_clear_walk_input(dapm, &p->source->sources);
- }
- }
-}
-
-
/* We implement power down on suspend by checking the power state of
* the ALSA card - when we are suspending the ALSA state for the card
* is set to D3.
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT(widget, path_checks);
- switch (widget->id) {
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_kcontrol:
- return 0;
- default:
- break;
- }
-
- switch (widget->id) {
- case snd_soc_dapm_adc:
- case snd_soc_dapm_aif_out:
- case snd_soc_dapm_dai_out:
- if (widget->active) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
- }
- default:
- break;
- }
-
- if (widget->connected) {
- /* connected pin ? */
- if (widget->id == snd_soc_dapm_output && !widget->ext) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
- }
-
- /* connected jack or spk ? */
- if (widget->id == snd_soc_dapm_hp ||
- widget->id == snd_soc_dapm_spk ||
- (widget->id == snd_soc_dapm_line &&
- !list_empty(&widget->sources))) {
- widget->outputs = snd_soc_dapm_suspend_check(widget);
- return widget->outputs;
- }
+ if (widget->is_sink && widget->connected) {
+ widget->outputs = snd_soc_dapm_suspend_check(widget);
+ return widget->outputs;
}
list_for_each_entry(path, &widget->sinks, list_source) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
- if (path->weak)
+ if (path->weak || path->is_supply)
continue;
if (path->walking)
return 1;
- if (path->walked)
- continue;
-
trace_snd_soc_dapm_output_path(widget, path);
- if (path->sink && path->connect) {
- path->walked = 1;
+ if (path->connect) {
path->walking = 1;
/* do we need to add this widget to the list ? */
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
DAPM_UPDATE_STAT(widget, path_checks);
- switch (widget->id) {
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_kcontrol:
- return 0;
- default:
- break;
- }
-
- /* active stream ? */
- switch (widget->id) {
- case snd_soc_dapm_dac:
- case snd_soc_dapm_aif_in:
- case snd_soc_dapm_dai_in:
- if (widget->active) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
- default:
- break;
- }
-
- if (widget->connected) {
- /* connected pin ? */
- if (widget->id == snd_soc_dapm_input && !widget->ext) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- /* connected VMID/Bias for lower pops */
- if (widget->id == snd_soc_dapm_vmid) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- /* connected jack ? */
- if (widget->id == snd_soc_dapm_mic ||
- (widget->id == snd_soc_dapm_line &&
- !list_empty(&widget->sinks))) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
-
- /* signal generator */
- if (widget->id == snd_soc_dapm_siggen) {
- widget->inputs = snd_soc_dapm_suspend_check(widget);
- return widget->inputs;
- }
+ if (widget->is_source && widget->connected) {
+ widget->inputs = snd_soc_dapm_suspend_check(widget);
+ return widget->inputs;
}
list_for_each_entry(path, &widget->sources, list_sink) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
- if (path->weak)
+ if (path->weak || path->is_supply)
continue;
if (path->walking)
return 1;
- if (path->walked)
- continue;
-
trace_snd_soc_dapm_input_path(widget, path);
- if (path->source && path->connect) {
- path->walked = 1;
+ if (path->connect) {
path->walking = 1;
/* do we need to add this widget to the list ? */
@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
struct snd_soc_dapm_widget_list **list)
{
- struct snd_soc_card *card = dai->card;
+ struct snd_soc_card *card = dai->component->card;
+ struct snd_soc_dapm_widget *w;
int paths;
mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
- dapm_reset(card);
- if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * For is_connected_{output,input}_ep fully discover the graph we need
+ * to reset the cached number of inputs and outputs.
+ */
+ list_for_each_entry(w, &card->widgets, list) {
+ w->inputs = -1;
+ w->outputs = -1;
+ }
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
paths = is_connected_output_ep(dai->playback_widget, list);
- dapm_clear_walk_output(&card->dapm,
- &dai->playback_widget->sinks);
- } else {
+ else
paths = is_connected_input_ep(dai->capture_widget, list);
- dapm_clear_walk_input(&card->dapm,
- &dai->capture_widget->sources);
- }
trace_snd_soc_dapm_connected(paths, stream);
mutex_unlock(&card->dapm_mutex);
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
DAPM_UPDATE_STAT(w, power_checks);
in = is_connected_input_ep(w, NULL);
- dapm_clear_walk_input(w->dapm, &w->sources);
out = is_connected_output_ep(w, NULL);
- dapm_clear_walk_output(w->dapm, &w->sinks);
return out != 0 && in != 0;
}
-/* Check to see if an ADC has power */
-static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
-{
- int in;
-
- DAPM_UPDATE_STAT(w, power_checks);
-
- if (w->active) {
- in = is_connected_input_ep(w, NULL);
- dapm_clear_walk_input(w->dapm, &w->sources);
- return in != 0;
- } else {
- return dapm_generic_check_power(w);
- }
-}
-
-/* Check to see if a DAC has power */
-static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
-{
- int out;
-
- DAPM_UPDATE_STAT(w, power_checks);
-
- if (w->active) {
- out = is_connected_output_ep(w, NULL);
- dapm_clear_walk_output(w->dapm, &w->sinks);
- return out != 0;
- } else {
- return dapm_generic_check_power(w);
- }
-}
-
/* Check to see if a power supply is needed */
static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
{
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
!path->connected(path->source, path->sink))
continue;
- if (!path->sink)
- continue;
-
if (dapm_widget_power_check(path->sink))
return 1;
}
@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
/* If we changed our power state perhaps our neigbours changed
* also.
*/
- list_for_each_entry(path, &w->sources, list_sink) {
- if (path->source) {
- dapm_widget_set_peer_power(path->source, power,
+ list_for_each_entry(path, &w->sources, list_sink)
+ dapm_widget_set_peer_power(path->source, power, path->connect);
+
+ /* Supplies can't affect their outputs, only their inputs */
+ if (!w->is_supply) {
+ list_for_each_entry(path, &w->sinks, list_source)
+ dapm_widget_set_peer_power(path->sink, power,
path->connect);
- }
- }
- switch (w->id) {
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_kcontrol:
- /* Supplies can't affect their outputs, only their inputs */
- break;
- default:
- list_for_each_entry(path, &w->sinks, list_source) {
- if (path->sink) {
- dapm_widget_set_peer_power(path->sink, power,
- path->connect);
- }
- }
- break;
}
if (power)
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
if (!buf)
return -ENOMEM;
- in = is_connected_input_ep(w, NULL);
- dapm_clear_walk_input(w->dapm, &w->sources);
- out = is_connected_output_ep(w, NULL);
- dapm_clear_walk_output(w->dapm, &w->sinks);
+ /* Supply widgets are not handled by is_connected_{input,output}_ep() */
+ if (w->is_supply) {
+ in = 0;
+ out = 0;
+ } else {
+ in = is_connected_input_ep(w, NULL);
+ out = is_connected_output_ep(w, NULL);
+ }
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
w->name, w->power ? "On" : "Off",
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
#endif
+/*
+ * soc_dapm_connect_path() - Connects or disconnects a path
+ * @path: The path to update
+ * @connect: The new connect state of the path. True if the path is connected,
+ * false if it is disconneted.
+ * @reason: The reason why the path changed (for debugging only)
+ */
+static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+ bool connect, const char *reason)
+{
+ if (path->connect == connect)
+ return;
+
+ path->connect = connect;
+ dapm_mark_dirty(path->source, reason);
+ dapm_mark_dirty(path->sink, reason);
+ dapm_path_invalidate(path);
+}
+
/* test and update the power status of a mux widget */
static int soc_dapm_mux_update_power(struct snd_soc_card *card,
struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
{
struct snd_soc_dapm_path *path;
int found = 0;
+ bool connect;
lockdep_assert_held(&card->dapm_mutex);
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) {
- if (!path->name || !e->texts[mux])
- continue;
-
found = 1;
/* we now need to match the string in the enum to the path */
- if (!(strcmp(path->name, e->texts[mux]))) {
- path->connect = 1; /* new connection */
- dapm_mark_dirty(path->source, "mux connection");
- } else {
- if (path->connect)
- dapm_mark_dirty(path->source,
- "mux disconnection");
- path->connect = 0; /* old connection must be powered down */
- }
- dapm_mark_dirty(path->sink, "mux change");
+ if (!(strcmp(path->name, e->texts[mux])))
+ connect = true;
+ else
+ connect = false;
+
+ soc_dapm_connect_path(path, connect, "mux update");
}
if (found)
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
/* find dapm widget path assoc with kcontrol */
dapm_kcontrol_for_each_path(path, kcontrol) {
found = 1;
- path->connect = connect;
- dapm_mark_dirty(path->source, "mixer connection");
- dapm_mark_dirty(path->sink, "mixer update");
+ soc_dapm_connect_path(path, connect, "mixer update");
}
if (found)
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
return -EINVAL;
}
- if (w->connected != status)
+ if (w->connected != status) {
dapm_mark_dirty(w, "pin configuration");
+ dapm_widget_invalidate_input_paths(w);
+ dapm_widget_invalidate_output_paths(w);
+ }
w->connected = status;
if (status == 0)
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
+/*
+ * dapm_update_widget_flags() - Re-compute widget sink and source flags
+ * @w: The widget for which to update the flags
+ *
+ * Some widgets have a dynamic category which depends on which neighbors they
+ * are connected to. This function update the category for these widgets.
+ *
+ * This function must be called whenever a path is added or removed to a widget.
+ */
+static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+
+ switch (w->id) {
+ case snd_soc_dapm_input:
+ w->is_source = 1;
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (p->source->id == snd_soc_dapm_micbias ||
+ p->source->id == snd_soc_dapm_mic ||
+ p->source->id == snd_soc_dapm_line ||
+ p->source->id == snd_soc_dapm_output) {
+ w->is_source = 0;
+ break;
+ }
+ }
+ break;
+ case snd_soc_dapm_output:
+ w->is_sink = 1;
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (p->sink->id == snd_soc_dapm_spk ||
+ p->sink->id == snd_soc_dapm_hp ||
+ p->sink->id == snd_soc_dapm_line ||
+ p->sink->id == snd_soc_dapm_input) {
+ w->is_sink = 0;
+ break;
+ }
+ }
+ break;
+ case snd_soc_dapm_line:
+ w->is_sink = !list_empty(&w->sources);
+ w->is_source = !list_empty(&w->sinks);
+ break;
+ default:
+ break;
+ }
+}
+
static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
const char *control,
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_path *path;
int ret;
+ if (wsink->is_supply && !wsource->is_supply) {
+ dev_err(dapm->dev,
+ "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ if (connected && !wsource->is_supply) {
+ dev_err(dapm->dev,
+ "connected() callback only supported for supply widgets (%s -> %s)\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ if (wsource->is_supply && control) {
+ dev_err(dapm->dev,
+ "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
+ wsource->name, control, wsink->name);
+ return -EINVAL;
+ }
+
path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
if (!path)
return -ENOMEM;
@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&path->list_source);
INIT_LIST_HEAD(&path->list_sink);
- /* check for external widgets */
- if (wsink->id == snd_soc_dapm_input) {
- if (wsource->id == snd_soc_dapm_micbias ||
- wsource->id == snd_soc_dapm_mic ||
- wsource->id == snd_soc_dapm_line ||
- wsource->id == snd_soc_dapm_output)
- wsink->ext = 1;
- }
- if (wsource->id == snd_soc_dapm_output) {
- if (wsink->id == snd_soc_dapm_spk ||
- wsink->id == snd_soc_dapm_hp ||
- wsink->id == snd_soc_dapm_line ||
- wsink->id == snd_soc_dapm_input)
- wsource->ext = 1;
- }
-
- dapm_mark_dirty(wsource, "Route added");
- dapm_mark_dirty(wsink, "Route added");
+ if (wsource->is_supply || wsink->is_supply)
+ path->is_supply = 1;
/* connect static paths */
if (control == NULL) {
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
path->connect = 1;
- return 0;
- }
-
- /* connect dynamic paths */
- switch (wsink->id) {
- case snd_soc_dapm_adc:
- case snd_soc_dapm_dac:
- case snd_soc_dapm_pga:
- case snd_soc_dapm_out_drv:
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
- case snd_soc_dapm_siggen:
- case snd_soc_dapm_micbias:
- case snd_soc_dapm_vmid:
- case snd_soc_dapm_pre:
- case snd_soc_dapm_post:
- case snd_soc_dapm_supply:
- case snd_soc_dapm_regulator_supply:
- case snd_soc_dapm_clock_supply:
- case snd_soc_dapm_aif_in:
- case snd_soc_dapm_aif_out:
- case snd_soc_dapm_dai_in:
- case snd_soc_dapm_dai_out:
- case snd_soc_dapm_dai_link:
- case snd_soc_dapm_kcontrol:
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
- path->connect = 1;
- return 0;
- case snd_soc_dapm_mux:
- ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
- &wsink->kcontrol_news[0]);
- if (ret != 0)
- goto err;
- break;
- case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer:
- case snd_soc_dapm_mixer_named_ctl:
- ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
- if (ret != 0)
+ } else {
+ /* connect dynamic paths */
+ switch (wsink->id) {
+ case snd_soc_dapm_mux:
+ ret = dapm_connect_mux(dapm, path, control);
+ if (ret != 0)
+ goto err;
+ break;
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ ret = dapm_connect_mixer(dapm, path, control);
+ if (ret != 0)
+ goto err;
+ break;
+ default:
+ dev_err(dapm->dev,
+ "Control not supported for path %s -> [%s] -> %s\n",
+ wsource->name, control, wsink->name);
+ ret = -EINVAL;
goto err;
- break;
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
- case snd_soc_dapm_line:
- case snd_soc_dapm_spk:
- list_add(&path->list, &dapm->card->paths);
- list_add(&path->list_sink, &wsink->sources);
- list_add(&path->list_source, &wsource->sinks);
- path->connect = 0;
- return 0;
+ }
}
+ list_add(&path->list, &dapm->card->paths);
+ list_add(&path->list_sink, &wsink->sources);
+ list_add(&path->list_source, &wsource->sinks);
+
+ dapm_update_widget_flags(wsource);
+ dapm_update_widget_flags(wsink);
+
+ dapm_mark_dirty(wsource, "Route added");
+ dapm_mark_dirty(wsink, "Route added");
+
+ if (dapm->card->instantiated && path->connect)
+ dapm_path_invalidate(path);
+
return 0;
err:
kfree(path);
@@ -2489,6 +2476,7 @@ err:
static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
+ struct snd_soc_dapm_widget *wsource, *wsink;
struct snd_soc_dapm_path *path, *p;
const char *sink;
const char *source;
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
}
if (path) {
- dapm_mark_dirty(path->source, "Route removed");
- dapm_mark_dirty(path->sink, "Route removed");
+ wsource = path->source;
+ wsink = path->sink;
+
+ dapm_mark_dirty(wsource, "Route removed");
+ dapm_mark_dirty(wsink, "Route removed");
+ if (path->connect)
+ dapm_path_invalidate(path);
dapm_free_path(path);
+
+ /* Update any path related flags */
+ dapm_update_widget_flags(wsource);
+ dapm_update_widget_flags(wsink);
} else {
dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
source, sink);
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
}
switch (w->id) {
- case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer:
- case snd_soc_dapm_mixer_named_ctl:
+ case snd_soc_dapm_mic:
+ case snd_soc_dapm_input:
+ w->is_source = 1;
w->power_check = dapm_generic_check_power;
break;
- case snd_soc_dapm_mux:
+ case snd_soc_dapm_spk:
+ case snd_soc_dapm_hp:
+ case snd_soc_dapm_output:
+ w->is_sink = 1;
w->power_check = dapm_generic_check_power;
break;
- case snd_soc_dapm_dai_out:
- w->power_check = dapm_adc_check_power;
- break;
- case snd_soc_dapm_dai_in:
- w->power_check = dapm_dac_check_power;
+ case snd_soc_dapm_vmid:
+ case snd_soc_dapm_siggen:
+ w->is_source = 1;
+ w->power_check = dapm_always_on_check_power;
break;
+ case snd_soc_dapm_mux:
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
- case snd_soc_dapm_input:
- case snd_soc_dapm_output:
case snd_soc_dapm_micbias:
- case snd_soc_dapm_spk:
- case snd_soc_dapm_hp:
- case snd_soc_dapm_mic:
case snd_soc_dapm_line:
case snd_soc_dapm_dai_link:
+ case snd_soc_dapm_dai_out:
+ case snd_soc_dapm_dai_in:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_supply:
case snd_soc_dapm_regulator_supply:
case snd_soc_dapm_clock_supply:
case snd_soc_dapm_kcontrol:
+ w->is_supply = 1;
w->power_check = dapm_supply_check_power;
break;
default:
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
INIT_LIST_HEAD(&w->dirty);
list_add(&w->list, &dapm->card->widgets);
+ w->inputs = -1;
+ w->outputs = -1;
+
/* machine layer set ups unconnected pins and insertions */
w->connected = 1;
return w;
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
break;
}
+
+ if (w->id == snd_soc_dapm_dai_in) {
+ w->is_source = w->active;
+ dapm_widget_invalidate_input_paths(w);
+ } else {
+ w->is_sink = w->active;
+ dapm_widget_invalidate_output_paths(w);
+ }
}
}
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
}
dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
- w->connected = 1;
+ if (!w->connected) {
+ /*
+ * w->force does not affect the number of input or output paths,
+ * so we only have to recheck if w->connected is changed
+ */
+ dapm_widget_invalidate_input_paths(w);
+ dapm_widget_invalidate_output_paths(w);
+ w->connected = 1;
+ }
w->force = 1;
dapm_mark_dirty(w, "force enable");
@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
+/**
+ * dapm_is_external_path() - Checks if a path is a external path
+ * @card: The card the path belongs to
+ * @path: The path to check
+ *
+ * Returns true if the path is either between two different DAPM contexts or
+ * between two external pins of the same DAPM context. Otherwise returns
+ * false.
+ */
+static bool dapm_is_external_path(struct snd_soc_card *card,
+ struct snd_soc_dapm_path *path)
+{
+ dev_dbg(card->dev,
+ "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
+ path->source->name, path->source->id, path->source->dapm,
+ path->sink->name, path->sink->id, path->sink->dapm);
+
+ /* Connection between two different DAPM contexts */
+ if (path->source->dapm != path->sink->dapm)
+ return true;
+
+ /* Loopback connection from external pin to external pin */
+ if (path->sink->id == snd_soc_dapm_input) {
+ switch (path->source->id) {
+ case snd_soc_dapm_output:
+ case snd_soc_dapm_micbias:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_path *p;
- list_for_each_entry(p, &card->paths, list) {
- if ((p->source == w) || (p->sink == w)) {
- dev_dbg(card->dev,
- "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
- p->source->name, p->source->id, p->source->dapm,
- p->sink->name, p->sink->id, p->sink->dapm);
+ list_for_each_entry(p, &w->sources, list_sink) {
+ if (dapm_is_external_path(card, p))
+ return true;
+ }
- /* Connected to something other than the codec */
- if (p->source->dapm != p->sink->dapm)
- return true;
- /*
- * Loopback connection from codec external pin to
- * codec external pin
- */
- if (p->sink->id == snd_soc_dapm_input) {
- switch (p->source->id) {
- case snd_soc_dapm_output:
- case snd_soc_dapm_micbias:
- return true;
- default:
- break;
- }
- }
- }
+ list_for_each_entry(p, &w->sinks, list_source) {
+ if (dapm_is_external_path(card, p))
+ return true;
}
return false;
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index ab47fea997a3..4380dcc064a5 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_report);
*
* @jack: ASoC jack
* @count: Number of zones
- * @zone: Array of zones
+ * @zones: Array of zones
*
* After this function has been called the zones specified in the
* array will be associated with the jack.
@@ -309,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
/* GPIO descriptor */
gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
gpios[i].name,
- gpios[i].idx);
+ gpios[i].idx, GPIOD_IN);
if (IS_ERR(gpios[i].desc)) {
ret = PTR_ERR(gpios[i].desc);
dev_err(gpios[i].gpiod_dev,
@@ -327,17 +327,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
goto undo;
}
- ret = gpio_request(gpios[i].gpio, gpios[i].name);
+ ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
+ gpios[i].name);
if (ret)
goto undo;
gpios[i].desc = gpio_to_desc(gpios[i].gpio);
}
- ret = gpiod_direction_input(gpios[i].desc);
- if (ret)
- goto err;
-
INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
gpios[i].jack = jack;
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
new file mode 100644
index 000000000000..100d92b5b77e
--- /dev/null
+++ b/sound/soc/soc-ops.c
@@ -0,0 +1,952 @@
+/*
+ * soc-ops.c -- Generic ASoC operations
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ * with code, comments and ideas from :-
+ * Richard Purdie <richard@openedhand.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; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+#include <sound/initval.h>
+
+/**
+ * snd_soc_info_enum_double - enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double enumerated
+ * mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+ e->items, e->texts);
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+
+/**
+ * snd_soc_get_enum_double - enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val, item;
+ unsigned int reg_val;
+ int ret;
+
+ ret = snd_soc_component_read(component, e->reg, &reg_val);
+ if (ret)
+ return ret;
+ val = (reg_val >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[0] = item;
+ if (e->shift_l != e->shift_r) {
+ val = (reg_val >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ ucontrol->value.enumerated.item[1] = item;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+
+/**
+ * snd_soc_put_enum_double - enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val;
+ unsigned int mask;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+ mask = e->mask << e->shift_l;
+ if (e->shift_l != e->shift_r) {
+ if (item[1] >= e->items)
+ return -EINVAL;
+ val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+ mask |= e->mask << e->shift_r;
+ }
+
+ return snd_soc_component_update_bits(component, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+
+/**
+ * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * @component: component
+ * @reg: Register to read
+ * @mask: Mask to use after shifting the register value
+ * @shift: Right shift of register value
+ * @sign_bit: Bit that describes if a number is negative or not.
+ * @signed_val: Pointer to where the read value should be stored
+ *
+ * This functions reads a codec register. The register value is shifted right
+ * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
+ * the given registervalue into a signed integer if sign_bit is non-zero.
+ *
+ * Returns 0 on sucess, otherwise an error value
+ */
+static int snd_soc_read_signed(struct snd_soc_component *component,
+ unsigned int reg, unsigned int mask, unsigned int shift,
+ unsigned int sign_bit, int *signed_val)
+{
+ int ret;
+ unsigned int val;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ val = (val >> shift) & mask;
+
+ if (!sign_bit) {
+ *signed_val = val;
+ return 0;
+ }
+
+ /* non-negative number */
+ if (!(val & BIT(sign_bit))) {
+ *signed_val = val;
+ return 0;
+ }
+
+ ret = val;
+
+ /*
+ * The register most probably does not contain a full-sized int.
+ * Instead we have an arbitrary number of bits in a signed
+ * representation which has to be translated into a full-sized int.
+ * This is done by filling up all bits above the sign-bit.
+ */
+ ret |= ~((int)(BIT(sign_bit) - 1));
+
+ *signed_val = ret;
+
+ return 0;
+}
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int platform_max;
+
+ if (!mc->platform_max)
+ mc->platform_max = mc->max;
+ platform_max = mc->platform_max;
+
+ if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ else
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+ uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = platform_max - mc->min;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+
+/**
+ * snd_soc_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int sign_bit = mc->sign_bit;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int val;
+ int ret;
+
+ if (sign_bit)
+ mask = BIT(sign_bit + 1) - 1;
+
+ ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = val - min;
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ if (reg == reg2)
+ ret = snd_soc_read_signed(component, reg, mask, rshift,
+ sign_bit, &val);
+ else
+ ret = snd_soc_read_signed(component, reg2, mask, shift,
+ sign_bit, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[1] = val - min;
+ if (invert)
+ ucontrol->value.integer.value[1] =
+ max - ucontrol->value.integer.value[1];
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+
+/**
+ * snd_soc_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ unsigned int sign_bit = mc->sign_bit;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ int err;
+ bool type_2r = false;
+ unsigned int val2 = 0;
+ unsigned int val, val_mask;
+
+ if (sign_bit)
+ mask = BIT(sign_bit + 1) - 1;
+
+ val = ((ucontrol->value.integer.value[0] + min) & mask);
+ if (invert)
+ val = max - val;
+ val_mask = mask << shift;
+ val = val << shift;
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val2 = ((ucontrol->value.integer.value[1] + min) & mask);
+ if (invert)
+ val2 = max - val2;
+ if (reg == reg2) {
+ val_mask |= mask << rshift;
+ val |= val2 << rshift;
+ } else {
+ val2 = val2 << shift;
+ type_2r = true;
+ }
+ }
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ if (type_2r)
+ err = snd_soc_component_update_bits(component, reg2, val_mask,
+ val2);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+
+/**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int mask = (1 << (fls(min + max) - 1)) - 1;
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret < 0)
+ return ret;
+
+ ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ ret = snd_soc_component_read(component, reg2, &val);
+ if (ret < 0)
+ return ret;
+
+ val = ((val >> rshift) - min) & mask;
+ ucontrol->value.integer.value[1] = val;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+
+ unsigned int reg = mc->reg;
+ unsigned int reg2 = mc->rreg;
+ unsigned int shift = mc->shift;
+ unsigned int rshift = mc->rshift;
+ int max = mc->max;
+ int min = mc->min;
+ int mask = (1 << (fls(min + max) - 1)) - 1;
+ int err = 0;
+ unsigned int val, val_mask, val2 = 0;
+
+ val_mask = mask << shift;
+ val = (ucontrol->value.integer.value[0] + min) & mask;
+ val = val << shift;
+
+ err = snd_soc_component_update_bits(component, reg, val_mask, val);
+ if (err < 0)
+ return err;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val_mask = mask << rshift;
+ val2 = (ucontrol->value.integer.value[1] + min) & mask;
+ val2 = val2 << rshift;
+
+ err = snd_soc_component_update_bits(component, reg2, val_mask,
+ val2);
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
+ * snd_soc_info_volsw_range - single mixer info callback with range.
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information, within a range, about a single
+ * mixer control.
+ *
+ * returns 0 for success.
+ */
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int platform_max;
+ int min = mc->min;
+
+ if (!mc->platform_max)
+ mc->platform_max = mc->max;
+ platform_max = mc->platform_max;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = platform_max - min;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
+
+/**
+ * snd_soc_put_volsw_range - single mixer put value callback with range.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value, within a range, for a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ unsigned int reg = mc->reg;
+ unsigned int rreg = mc->rreg;
+ unsigned int shift = mc->shift;
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val, val_mask;
+ int ret;
+
+ if (invert)
+ val = (max - ucontrol->value.integer.value[0]) & mask;
+ else
+ val = ((ucontrol->value.integer.value[0] + min) & mask);
+ val_mask = mask << shift;
+ val = val << shift;
+
+ ret = snd_soc_component_update_bits(component, reg, val_mask, val);
+ if (ret < 0)
+ return ret;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ if (invert)
+ val = (max - ucontrol->value.integer.value[1]) & mask;
+ else
+ val = ((ucontrol->value.integer.value[1] + min) & mask);
+ val_mask = mask << shift;
+ val = val << shift;
+
+ ret = snd_soc_component_update_bits(component, rreg, val_mask,
+ val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
+
+/**
+ * snd_soc_get_volsw_range - single mixer get callback with range
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value, within a range, of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int rreg = mc->rreg;
+ unsigned int shift = mc->shift;
+ int min = mc->min;
+ int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int invert = mc->invert;
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = (val >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[0] =
+ max - ucontrol->value.integer.value[0];
+ else
+ ucontrol->value.integer.value[0] =
+ ucontrol->value.integer.value[0] - min;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ ret = snd_soc_component_read(component, rreg, &val);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[1] = (val >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[1] =
+ max - ucontrol->value.integer.value[1];
+ else
+ ucontrol->value.integer.value[1] =
+ ucontrol->value.integer.value[1] - min;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
+
+/**
+ * snd_soc_limit_volume - Set new limit to an existing volume control.
+ *
+ * @codec: where to look for the control
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+ const char *name, int max)
+{
+ struct snd_card *card = codec->component.card->snd_card;
+ struct snd_kcontrol *kctl;
+ struct soc_mixer_control *mc;
+ int found = 0;
+ int ret = -EINVAL;
+
+ /* Sanity check for name and max */
+ if (unlikely(!name || max <= 0))
+ return -EINVAL;
+
+ list_for_each_entry(kctl, &card->controls, list) {
+ if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+ found = 1;
+ break;
+ }
+ }
+ if (found) {
+ mc = (struct soc_mixer_control *)kctl->private_value;
+ if (max <= mc->max) {
+ mc->platform_max = max;
+ ret = 0;
+ }
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = params->num_regs * component->val_bytes;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
+
+int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ int ret;
+
+ if (component->regmap)
+ ret = regmap_raw_read(component->regmap, params->base,
+ ucontrol->value.bytes.data,
+ params->num_regs * component->val_bytes);
+ else
+ ret = -EINVAL;
+
+ /* Hide any masked bytes to ensure consistent data reporting */
+ if (ret == 0 && params->mask) {
+ switch (component->val_bytes) {
+ case 1:
+ ucontrol->value.bytes.data[0] &= ~params->mask;
+ break;
+ case 2:
+ ((u16 *)(&ucontrol->value.bytes.data))[0]
+ &= cpu_to_be16(~params->mask);
+ break;
+ case 4:
+ ((u32 *)(&ucontrol->value.bytes.data))[0]
+ &= cpu_to_be32(~params->mask);
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
+
+int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_bytes *params = (void *)kcontrol->private_value;
+ int ret, len;
+ unsigned int val, mask;
+ void *data;
+
+ if (!component->regmap || !params->num_regs)
+ return -EINVAL;
+
+ len = params->num_regs * component->val_bytes;
+
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * If we've got a mask then we need to preserve the register
+ * bits. We shouldn't modify the incoming data so take a
+ * copy.
+ */
+ if (params->mask) {
+ ret = regmap_read(component->regmap, params->base, &val);
+ if (ret != 0)
+ goto out;
+
+ val &= params->mask;
+
+ switch (component->val_bytes) {
+ case 1:
+ ((u8 *)data)[0] &= ~params->mask;
+ ((u8 *)data)[0] |= val;
+ break;
+ case 2:
+ mask = ~params->mask;
+ ret = regmap_parse_val(component->regmap,
+ &mask, &mask);
+ if (ret != 0)
+ goto out;
+
+ ((u16 *)data)[0] &= mask;
+
+ ret = regmap_parse_val(component->regmap,
+ &val, &val);
+ if (ret != 0)
+ goto out;
+
+ ((u16 *)data)[0] |= val;
+ break;
+ case 4:
+ mask = ~params->mask;
+ ret = regmap_parse_val(component->regmap,
+ &mask, &mask);
+ if (ret != 0)
+ goto out;
+
+ ((u32 *)data)[0] &= mask;
+
+ ret = regmap_parse_val(component->regmap,
+ &val, &val);
+ if (ret != 0)
+ goto out;
+
+ ((u32 *)data)[0] |= val;
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ ret = regmap_raw_write(component->regmap, params->base,
+ data, len);
+
+out:
+ kfree(data);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *ucontrol)
+{
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+ ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ ucontrol->count = params->max;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
+
+int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
+ unsigned int size, unsigned int __user *tlv)
+{
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ unsigned int count = size < params->max ? size : params->max;
+ int ret = -ENXIO;
+
+ switch (op_flag) {
+ case SNDRV_CTL_TLV_OP_READ:
+ if (params->get)
+ ret = params->get(tlv, count);
+ break;
+ case SNDRV_CTL_TLV_OP_WRITE:
+ if (params->put)
+ ret = params->put(tlv, count);
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
+
+/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = mc->min;
+ uinfo->value.integer.max = mc->max;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+ unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int invert = mc->invert;
+ unsigned long mask = (1UL<<mc->nbits)-1;
+ long min = mc->min;
+ long max = mc->max;
+ long val = 0;
+ unsigned int regval;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < regcount; i++) {
+ ret = snd_soc_component_read(component, regbase+i, &regval);
+ if (ret)
+ return ret;
+ val |= (regval & regwmask) << (regwshift*(regcount-i-1));
+ }
+ val &= mask;
+ if (min < 0 && val > max)
+ val |= ~mask;
+ if (invert)
+ val = max - val;
+ ucontrol->value.integer.value[0] = val;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mreg_control *mc =
+ (struct soc_mreg_control *)kcontrol->private_value;
+ unsigned int regbase = mc->regbase;
+ unsigned int regcount = mc->regcount;
+ unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+ unsigned int regwmask = (1<<regwshift)-1;
+ unsigned int invert = mc->invert;
+ unsigned long mask = (1UL<<mc->nbits)-1;
+ long max = mc->max;
+ long val = ucontrol->value.integer.value[0];
+ unsigned int i, regval, regmask;
+ int err;
+
+ if (invert)
+ val = max - val;
+ val &= mask;
+ for (i = 0; i < regcount; i++) {
+ regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+ regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+ err = snd_soc_component_update_bits(component, regbase+i,
+ regmask, regval);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int val;
+ int ret;
+
+ ret = snd_soc_component_read(component, reg, &val);
+ if (ret)
+ return ret;
+
+ val &= mask;
+
+ if (shift != 0 && val != 0)
+ val = val >> shift;
+ ucontrol->value.enumerated.item[0] = val ^ invert;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ unsigned int reg = mc->reg;
+ unsigned int shift = mc->shift;
+ unsigned int mask = 1 << shift;
+ unsigned int invert = mc->invert != 0;
+ unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+ unsigned int val1 = (strobe ^ invert) ? mask : 0;
+ unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+ int err;
+
+ err = snd_soc_component_update_bits(component, reg, mask, val1);
+ if (err < 0)
+ return err;
+
+ return snd_soc_component_update_bits(component, reg, mask, val2);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 002311afdeaa..eb87d96e2cf0 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
codec_dai->rate = 0;
}
+ snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
for (i = 0; i < rtd->num_codecs; i++)
snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
substream->stream);
+ snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
out:
mutex_unlock(&rtd->pcm_mutex);
@@ -1522,13 +1525,36 @@ static void dpcm_set_fe_runtime(struct snd_pcm_substream *substream)
dpcm_init_runtime_hw(runtime, &cpu_dai_drv->capture);
}
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd);
+
+/* Set FE's runtime_update state; the state is protected via PCM stream lock
+ * for avoiding the race with trigger callback.
+ * If the state is unset and a trigger is pending while the previous operation,
+ * process the pending trigger action here.
+ */
+static void dpcm_set_fe_update_state(struct snd_soc_pcm_runtime *fe,
+ int stream, enum snd_soc_dpcm_update state)
+{
+ struct snd_pcm_substream *substream =
+ snd_soc_dpcm_get_substream(fe, stream);
+
+ snd_pcm_stream_lock_irq(substream);
+ if (state == SND_SOC_DPCM_UPDATE_NO && fe->dpcm[stream].trigger_pending) {
+ dpcm_fe_dai_do_trigger(substream,
+ fe->dpcm[stream].trigger_pending - 1);
+ fe->dpcm[stream].trigger_pending = 0;
+ }
+ fe->dpcm[stream].runtime_update = state;
+ snd_pcm_stream_unlock_irq(substream);
+}
+
static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
{
struct snd_soc_pcm_runtime *fe = fe_substream->private_data;
struct snd_pcm_runtime *runtime = fe_substream->runtime;
int stream = fe_substream->stream, ret = 0;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
ret = dpcm_be_dai_startup(fe, fe_substream->stream);
if (ret < 0) {
@@ -1550,13 +1576,13 @@ static int dpcm_fe_dai_startup(struct snd_pcm_substream *fe_substream)
dpcm_set_fe_runtime(fe_substream);
snd_pcm_limit_hw_rates(runtime);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
unwind:
dpcm_be_dai_startup_unwind(fe, fe_substream->stream);
be_err:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -1603,7 +1629,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* shutdown the BEs */
dpcm_be_dai_shutdown(fe, substream->stream);
@@ -1617,7 +1643,7 @@ static int dpcm_fe_dai_shutdown(struct snd_pcm_substream *substream)
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return 0;
}
@@ -1641,6 +1667,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
continue;
+ /* do not free hw if this BE is used by other FE */
+ if (be->dpcm[stream].users > 1)
+ continue;
+
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
@@ -1665,7 +1695,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
int err, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
dev_dbg(fe->dev, "ASoC: hw_free FE %s\n", fe->dai_link->name);
@@ -1680,7 +1710,7 @@ static int dpcm_fe_dai_hw_free(struct snd_pcm_substream *substream)
err = dpcm_be_dai_hw_free(fe, stream);
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_FREE;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return 0;
@@ -1773,7 +1803,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
int ret, stream = substream->stream;
mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
memcpy(&fe->dpcm[substream->stream].hw_params, params,
sizeof(struct snd_pcm_hw_params));
@@ -1796,7 +1826,7 @@ static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
}
@@ -1910,7 +1940,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream,
}
EXPORT_SYMBOL_GPL(dpcm_be_dai_trigger);
-static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+static int dpcm_fe_dai_do_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *fe = substream->private_data;
int stream = substream->stream, ret;
@@ -1984,6 +2014,23 @@ out:
return ret;
}
+static int dpcm_fe_dai_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *fe = substream->private_data;
+ int stream = substream->stream;
+
+ /* if FE's runtime_update is already set, we're in race;
+ * process this trigger later at exit
+ */
+ if (fe->dpcm[stream].runtime_update != SND_SOC_DPCM_UPDATE_NO) {
+ fe->dpcm[stream].trigger_pending = cmd + 1;
+ return 0; /* delayed, assuming it's successful */
+ }
+
+ /* we're alone, let's trigger */
+ return dpcm_fe_dai_do_trigger(substream, cmd);
+}
+
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
{
struct snd_soc_dpcm *dpcm;
@@ -2027,7 +2074,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
dev_dbg(fe->dev, "ASoC: prepare FE %s\n", fe->dai_link->name);
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_FE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
/* there is no point preparing this FE if there are no BEs */
if (list_empty(&fe->dpcm[stream].be_clients)) {
@@ -2054,7 +2101,7 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
out:
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
mutex_unlock(&fe->card->mutex);
return ret;
@@ -2201,11 +2248,11 @@ static int dpcm_run_new_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_startup(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to startup some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -2214,11 +2261,11 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
{
int ret;
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_BE;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_BE);
ret = dpcm_run_update_shutdown(fe, stream);
if (ret < 0)
dev_err(fe->dev, "ASoC: failed to shutdown some BEs\n");
- fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
+ dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_NO);
return ret;
}
@@ -2248,7 +2295,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
fe->dai_link->name);
/* skip if FE doesn't have playback capability */
- if (!fe->cpu_dai->driver->playback.channels_min)
+ if (!fe->cpu_dai->driver->playback.channels_min
+ || !fe->codec_dai->driver->playback.channels_min)
+ goto capture;
+
+ /* skip if FE isn't currently playing */
+ if (!fe->cpu_dai->playback_active
+ || !fe->codec_dai->playback_active)
goto capture;
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
@@ -2278,7 +2331,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
dpcm_path_put(&list);
capture:
/* skip if FE doesn't have capture capability */
- if (!fe->cpu_dai->driver->capture.channels_min)
+ if (!fe->cpu_dai->driver->capture.channels_min
+ || !fe->codec_dai->driver->capture.channels_min)
+ continue;
+
+ /* skip if FE isn't currently capturing */
+ if (!fe->cpu_dai->capture_active
+ || !fe->codec_dai->capture_active)
continue;
paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index 3b0fa12dbff7..29a9957d335a 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver tegra20_ac97_dai = {
.name = "tegra-ac97-pcm",
- .ac97_control = 1,
+ .bus_control = true,
.probe = tegra20_ac97_probe,
.playback = {
.stream_name = "PCM Playback",
diff --git a/sound/soc/txx9/txx9aclc-ac97.c b/sound/soc/txx9/txx9aclc-ac97.c
index 9edd68db9f48..f7135cdaa2ca 100644
--- a/sound/soc/txx9/txx9aclc-ac97.c
+++ b/sound/soc/txx9/txx9aclc-ac97.c
@@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
}
static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
- .ac97_control = 1,
+ .bus_control = true,
.probe = txx9aclc_ac97_probe,
.remove = txx9aclc_ac97_remove,
.playback = {
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index cd71fd889d8b..00b7e2d02690 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
struct snd_card *card = rtd->card->snd_card;
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
- struct platform_device *pdev = to_platform_device(dai->platform->dev);
+ struct platform_device *pdev = to_platform_device(rtd->platform->dev);
struct txx9aclc_soc_device *dev;
struct resource *r;
int i;
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 7ecd0e8a5c51..f61ebb17cc64 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -591,18 +591,19 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
{
struct snd_card *card;
struct list_head *p;
+ bool was_shutdown;
if (chip == (void *)-1L)
return;
card = chip->card;
down_write(&chip->shutdown_rwsem);
+ was_shutdown = chip->shutdown;
chip->shutdown = 1;
up_write(&chip->shutdown_rwsem);
mutex_lock(&register_mutex);
- chip->num_interfaces--;
- if (chip->num_interfaces <= 0) {
+ if (!was_shutdown) {
struct snd_usb_endpoint *ep;
snd_card_disconnect(card);
@@ -622,6 +623,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
list_for_each(p, &chip->mixer_list) {
snd_usb_mixer_disconnect(p);
}
+ }
+
+ chip->num_interfaces--;
+ if (chip->num_interfaces <= 0) {
usb_chip[chip->index] = NULL;
mutex_unlock(&register_mutex);
snd_card_free_when_closed(card);
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 2e4a9dbc51fa..6e354d326858 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -2033,10 +2033,11 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid,
cval->res = 1;
cval->initialized = 1;
- if (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR)
- cval->control = UAC2_CX_CLOCK_SELECTOR;
- else
+ if (state->mixer->protocol == UAC_VERSION_1)
cval->control = 0;
+ else /* UAC_VERSION_2 */
+ cval->control = (desc->bDescriptorSubtype == UAC2_CLOCK_SELECTOR) ?
+ UAC2_CX_CLOCK_SELECTOR : UAC2_SU_SELECTOR;
namelist = kmalloc(sizeof(char *) * desc->bNrInPins, GFP_KERNEL);
if (!namelist) {
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index f119a41ed9a9..8c9bf4b7aaf0 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -593,10 +593,10 @@ static int snd_nativeinstruments_control_get(struct snd_kcontrol *kcontrol,
if (mixer->chip->shutdown)
ret = -ENODEV;
else
- ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
+ ret = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), bRequest,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0, wIndex,
- &tmp, sizeof(tmp), 1000);
+ &tmp, sizeof(tmp));
up_read(&mixer->chip->shutdown_rwsem);
if (ret < 0) {
@@ -885,6 +885,11 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
return changed;
}
+static void kctl_private_value_free(struct snd_kcontrol *kctl)
+{
+ kfree((void *)kctl->private_value);
+}
+
static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
int validx, int bUnitID)
{
@@ -919,6 +924,7 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer,
return -ENOMEM;
}
+ kctl->private_free = kctl_private_value_free;
err = snd_ctl_add(mixer->chip->card, kctl);
if (err < 0)
return err;
diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h
index 223c47b33ba3..c657752a420c 100644
--- a/sound/usb/quirks-table.h
+++ b/sound/usb/quirks-table.h
@@ -385,6 +385,36 @@ YAMAHA_DEVICE(0x105d, NULL),
}
},
{
+ USB_DEVICE(0x0499, 0x1509),
+ .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+ /* .vendor_name = "Yamaha", */
+ /* .product_name = "Steinberg UR22", */
+ .ifnum = QUIRK_ANY_INTERFACE,
+ .type = QUIRK_COMPOSITE,
+ .data = (const struct snd_usb_audio_quirk[]) {
+ {
+ .ifnum = 1,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 2,
+ .type = QUIRK_AUDIO_STANDARD_INTERFACE
+ },
+ {
+ .ifnum = 3,
+ .type = QUIRK_MIDI_YAMAHA
+ },
+ {
+ .ifnum = 4,
+ .type = QUIRK_IGNORE_INTERFACE
+ },
+ {
+ .ifnum = -1
+ }
+ }
+ }
+},
+{
USB_DEVICE(0x0499, 0x150a),
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
/* .vendor_name = "Yamaha", */
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index d2aa45a8d895..60dfe0d28771 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1146,6 +1146,20 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
+
+ /* Marantz/Denon devices with USB DAC functionality need a delay
+ * after each class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 0x154e) &&
+ (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS) {
+
+ switch (le16_to_cpu(dev->descriptor.idProduct)) {
+ case 0x3005: /* Marantz HD-DAC1 */
+ case 0x3006: /* Marantz SA-14S1 */
+ mdelay(20);
+ break;
+ }
+ }
}
/*
@@ -1179,12 +1193,12 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
/* iFi Audio micro/nano iDSD */
case USB_ID(0x20b1, 0x3008):
if (fp->altsetting == 2)
- return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
/* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
case USB_ID(0x20b1, 0x2009):
if (fp->altsetting == 3)
- return SNDRV_PCM_FMTBIT_DSD_U32_LE;
+ return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;
default:
break;